// Toolbar item
function SpawTbItem(module, name, id)
{
  this.module = module;
  this.name = name;
  this.id = id;
  this.is_enabled = true;
}
// module (plugin)
SpawTbItem.prototype.module;
// command name
SpawTbItem.prototype.name;
// control id
SpawTbItem.prototype.id;
// belongs to editor
SpawTbItem.prototype.editor;
// belongs to toolbar
SpawTbItem.prototype.toolbar_name;
// is toolbar item enabled?
SpawTbItem.prototype.is_enabled;

// Toolbar image
function SpawTbImage(module, name, id)
{
  this.constructor(module, name, id);
}
SpawTbImage.prototype = new SpawTbItem;

// Toolbar button
function SpawTbButton(module, name, id, on_enabled_check, on_pushed_check, on_click, base_image_url, show_in_context_menu)
{
  this.constructor(module, name, id);
  this.on_enabled_check = on_enabled_check;
  this.on_pushed_check = on_pushed_check;
  this.on_click = on_click;  
  this.base_image_url = base_image_url;
  if (show_in_context_menu)
    this.show_in_context_menu = show_in_context_menu;
  else
    this.show_in_context_menu = false; 
}
SpawTbButton.prototype = new SpawTbItem;
SpawTbButton.prototype.on_enabled_check;
SpawTbButton.prototype.on_pushed_check;
SpawTbButton.prototype.on_click;
SpawTbButton.prototype.is_pushed = false;
SpawTbButton.prototype.show_in_context_menu = false;

// images
SpawTbButton.prototype.base_image_url;
SpawTbButton.prototype.image;
SpawTbButton.prototype.image_over;
SpawTbButton.prototype.image_down;
SpawTbButton.prototype.image_off;

// Toolbar dropdown
function SpawTbDropdown(module, name, id, on_enabled_check, on_status_check, on_change)
{
  this.constructor(module, name, id);
  this.on_enabled_check = on_enabled_check;
  this.on_status_check = on_status_check;
  this.on_change = on_change;  
}
SpawTbDropdown.prototype = new SpawTbItem;
SpawTbDropdown.prototype.on_enabled_check;
SpawTbDropdown.prototype.on_status_check;
SpawTbDropdown.prototype.on_change;
// Editor class
function SpawEditor(name)
{
  this.name = name;
  this.toolbar_items = new Array();
  this.pages = new Array();
  this.tabs = new Array();
  this.controlled_editors = new Array();
  //this.event_handlers = new Array();
  this.config = new Array();
}

SpawEditor.prototype.name;

// secure config id
SpawEditor.prototype.scid;

// stylesheet
SpawEditor.prototype.stylesheet;

// config
SpawEditor.prototype.config;
SpawEditor.prototype.getConfigValue = function(name)
{
  return this.config[name];
}
SpawEditor.prototype.setConfigValue = function(name, value)
{
  this.config[name] = value;
}
SpawEditor.prototype.getRequestUriConfigValue = function()
{
  return this.getConfigValue("__request_uri");
}
// returns true if all pages are initialized
SpawEditor.prototype.isInitialized = function()
{
  var result = true;
  for (var i=0; i<this.pages.length; i++)
  {
    if (!this.pages[i].initialized)
    {
      result = false;
      break;
    }
  }
  return result;
}

// toolbars
SpawEditor.prototype.toolbar_items;
SpawEditor.prototype.addToolbarItem = function(tbi, toolbar_name)
{
  if (tbi.base_image_url)
    this.theme.preloadImages(tbi);
  tbi.editor = this;
  tbi.toolbar_name = toolbar_name;
  this.toolbar_items.push(tbi);
}
SpawEditor.prototype.getToolbarItem = function(id)
{
  for (var i=0; i<this.toolbar_items.length; i++)
  {
    if (this.toolbar_items[i].id == id)
    {
      return this.toolbar_items[i];
    }
  }
  return null;
}

// enable editing mode button
SpawEditor.prototype.enableEditingMode = function(tbi)
{
  if (tbi && tbi.editor.getActivePage().editing_mode_tbi && tbi.editor.getActivePage().editing_mode_tbi != null)
  {
    var mbt = this.document.getElementById(tbi.editor.getActivePage().editing_mode_tbi.id);
    mbt.disabled = false;
    tbi.editor.theme.buttonOut(tbi.editor.getActivePage().editing_mode_tbi, mbt);
  }
}
// disable editing mode button
SpawEditor.prototype.disableEditingMode = function(tbi)
{
  if (tbi && tbi.editor.getActivePage().editing_mode_tbi && tbi.editor.getActivePage().editing_mode_tbi != null)
  {
    var mbt = this.document.getElementById(tbi.editor.getActivePage().editing_mode_tbi.id);
    mbt.disabled = true;
    tbi.editor.theme.buttonOff(tbi.editor.getActivePage().editing_mode_tbi, mbt);
  }
}


// pages
SpawEditor.prototype.pages;
SpawEditor.prototype.addPage = function(page)
{
  this.pages.push(page);
  this.addTab(page);
}
SpawEditor.prototype.getPage = function(id)
{
  for(var i=0; i<this.pages.length; i++)
  {
    if (this.pages[i].name == id)
      return this.pages[i];
  }
  return null;
}
SpawEditor.prototype.active_page;
SpawEditor.prototype.setActivePage = function(id)
{
  if (this.active_page && (this.active_page.name != id || SpawEngine.active_editor != this))
  {
    // raise before page switch event
    SpawEngine.handleEvent("spawbeforepageswitch", null, null, this.name);
  
    this.getTab(this.active_page.name).setInactive(); 
    this.hidePage(this.active_page);
    this.enableEditingMode(this.active_page.editing_mode_tbi);
    this.active_page = this.getPage(id);
    this.getTab(this.active_page.name).setActive();
    this.showPage(this.active_page);
    this.disableEditingMode(this.active_page.editing_mode_tbi);
    SpawEngine.setActiveEditor(this);

    // raise page switch event
    SpawEngine.handleEvent("spawpageswitch", null, null, this.name);

    setTimeout(this.name + '_obj.updateToolbar();', 10); // firefox crashes if called imediatly
  }
}
SpawEditor.prototype.getActivePage = function()
{
  return this.active_page;
}

// parent object of the editing area
SpawEditor.prototype.pageOffsetParent;
SpawEditor.prototype.hidePage = function(page)
{
  var pta = this.getPageInput(page.name);
  var pif = this.getPageIframeObject(page.name);
  pta.style.display = 'none';
  pif.style.display = 'none';
}
SpawEditor.prototype.showPage = function(page)
{
  var pta = this.getPageInput(page.name);
  var pif = this.getPageIframeObject(page.name);
  var pdoc = this.getPageDoc(page.name);
  if (page.editing_mode == 'design')
  {
    pta.style.display = 'none';
    pif.style.display = 'inline';
    pdoc.designMode = 'on'; // mozilla (and probably early firefox versions) looses this when the page is hidden
  }
  else
  {
    pta.style.width = pif.offsetWidth + 'px'; 
    pta.style.display = 'inline';
    pif.style.display = 'none';
  }
  this.focus();
}


// tabs
SpawEditor.prototype.tabs;
SpawEditor.prototype.addTab = function(page)
{
  this.tabs.push(new SpawTab(page));
}
SpawEditor.prototype.getTab = function(page_name)
{
  for (var i=0; i<this.tabs.length; i++)
  {
    if (this.tabs[i].page.name == page_name)
      return this.tabs[i];
  }
  return null;
}

SpawEditor.prototype.floating_mode = false;
// controlled editors (for floating mode master)
SpawEditor.prototype.controlled_editors;
SpawEditor.prototype.addControlledEditor = function(editor)
{
  this.controlled_editors.push(editor);
}
SpawEditor.prototype.isControlledEditor = function(name)
{
  for (var i=0; i<this.controlled_editors.length; i++)
  {
    if (this.controlled_editors[i].name == name)
      return true;
  }
  return false;
}

// controlled by (for floating mode slave)
SpawEditor.prototype.controlled_by;

// returns currently active editor (might need more complex logic later)
SpawEditor.prototype.getCurrentEditor = function()
{
  return SpawEngine.getActiveEditor();
}

// returns which editor is currently controlled by this editors toolbar
SpawEditor.prototype.getTargetEditor = function()
{
  if (this.controlled_by == this && this.controlled_editors.length <= 1)
    return this;
  else
    return SpawEngine.getActiveEditor();
}

// theme
SpawEditor.prototype.theme;
SpawEditor.prototype.setTheme = function(theme)
{
  this.theme = theme;
}
SpawEditor.prototype.getTheme = function()
{
  return this.theme;
}

// language
SpawEditor.prototype.lang;
SpawEditor.prototype.setLang = function(lang)
{
  this.lang = lang;
}
SpawEditor.prototype.getLang = function()
{
  return this.lang;
}
SpawEditor.prototype.output_charset;
SpawEditor.prototype.setOutputCharset = function(output_charset)
{
  this.output_charset = output_charset;
}
SpawEditor.prototype.getOutputCharset = function()
{
  return this.output_charset;
}

// hooks up to window onload event and calls initialization
SpawEditor.prototype.onLoadHookup = function()
{
  var spaw_tmpstr="";
  if (window.onload != null) 
  {
    spaw_tmpstr = window.onload.toString();
    var spaw_i = spaw_tmpstr.indexOf("{") + 2;
    spaw_tmpstr = spaw_tmpstr.substr(spaw_i,spaw_tmpstr.length-spaw_i-2);
  }
  window.onload = new Function(this.name+'_obj.initialize();'+spaw_tmpstr);   
}

// returns reference to page textarea
SpawEditor.prototype.getPageInput = function(page_name)
{
  return this.document.getElementById(page_name);
}

// returns reference to outer iframe object (differs from getPageIfram in IE only)
SpawEditor.prototype.getPageIframeObject = function(page_name)
{
  return this.document.getElementById(page_name+'_rEdit');
}

// returns reference to active page's doc
SpawEditor.prototype.getActivePageDoc = function()
{
  return this.getPageDoc(this.active_page.name);
}

// floating toolbar control
SpawEditor.prototype.currentToolbarX;
SpawEditor.prototype.currentToolbarY;
SpawEditor.prototype.lastMousePosX;
SpawEditor.prototype.lastMousePosY;
SpawEditor.prototype.isToolbarMoving = false;
SpawEditor.prototype.floatingMouseDown = function(event)
{
  this.currentToolbarX = this.document.getElementById(this.name + '_toolbox').offsetLeft;
  this.currentToolbarY = this.document.getElementById(this.name + '_toolbox').offsetTop;
  this.lastMousePosX = event.clientX;
  this.lastMousePosY = event.clientY;
  this.isToolbarMoving = true;
  SpawEngine.movingToolbar = this;
}


// floating toolbar position relative to this instance
SpawEditor.prototype.floatingToolbarX = 100;
SpawEditor.prototype.floatingToolbarY = 10;
SpawEditor.prototype.positionFloatingToolbar = function()
{
  this.document.getElementById(this.controlled_by.name + '_toolbox').style.left = this.getPageOffsetLeft() + this.floatingToolbarX + "px";   
  this.document.getElementById(this.controlled_by.name + '_toolbox').style.top = this.getPageOffsetTop() + this.floatingToolbarY + "px";   
}
SpawEditor.prototype.saveFloatingToolbarPosition = function(x, y)
{
  this.floatingToolbarX = x - this.getPageOffsetLeft();
  this.floatingToolbarY = y - this.getPageOffsetTop();
}


// page offsets
SpawEditor.prototype.getPageOffsetLeft = function()
{
  var elm = this.getPageIframeObject(this.active_page?this.active_page.name:this.name);
  var res = elm.offsetLeft;
  
  while ((elm = elm.offsetParent) != null)
  {
    res += elm.offsetLeft;
  }
  return res;
}

SpawEditor.prototype.getPageOffsetTop = function()
{
  var elm = this.getPageIframeObject(this.active_page?this.active_page.name:this.name);
  var res = elm.offsetTop;
  
  while ((elm = elm.offsetParent) != null)
  {
    res += elm.offsetTop;
  }
  return res;
}

// resizing control
SpawEditor.prototype.isResizing = false;
SpawEditor.prototype.isVerticalResizingAllowed = function()
{
  var res = this.getConfigValue("resizing_directions");
  res = res?res.toLowerCase():res;
  if (res == 'vertical' || res == 'both')
    return true;
  else
    return false;
}
SpawEditor.prototype.isHorizontalResizingAllowed = function()
{
  var res = this.getConfigValue("resizing_directions");
  res = res?res.toLowerCase():res;
  if (res == 'horizontal' || res == 'both')
    return true;
  else
    return false;
}
SpawEditor.prototype.resizingGripMouseDown = function(event)
{
  this.lastMousePosX = event.clientX;
  this.lastMousePosY = event.clientY;
  this.isResizing = true;
  SpawEngine.resizingEditor = this;

  //SpawEngine.resizingEditor.hidePage(SpawEngine.resizingEditor.getActivePage());
  // prevent gecko from dragging image
  if (event.preventDefault)
    event.preventDefault();
}
SpawEditor.prototype.finalizeResizing = function()
{
  var resobj = this.isInDesignMode()?this.getPageIframeObject(this.getActivePage().name):this.getPageInput(this.getActivePage().name);

  for (var i=0; i<this.pages.length; i++)
  {
    var pif = this.getPageIframeObject(this.pages[i].name);
    var pta = this.getPageInput(this.pages[i].name);
    pif.style.height = resobj.offsetHeight + 'px';    
    pta.style.height = resobj.offsetHeight + 'px';    
    pta.style.width = resobj.offsetWidth + 'px';
  }
} 

// active toolbar control
SpawEditor.prototype.updateToolbar = function()
{
  if (this.controlled_by != this)
    this.updateEditorToolbar(this.controlled_by);
  this.updateEditorToolbar(this);
}
SpawEditor.prototype.updateEditorToolbar = function(ed)
{
  for(var i=0; i<ed.toolbar_items.length; i++)
  {
    // check if item is enabled
    if (ed.toolbar_items[i].on_enabled_check && ed.toolbar_items[i].on_enabled_check != '')
    {
      if(eval("SpawPG"+ed.toolbar_items[i].module 
              + '.' + ed.toolbar_items[i].on_enabled_check + '(this, ed.toolbar_items[i])'))
      {
        this.document.getElementById(ed.toolbar_items[i].id).disabled = false;
        ed.toolbar_items[i].is_enabled = true;
        if (ed.toolbar_items[i].on_click)
        { 
          // button     
          ed.theme.buttonOut(ed.toolbar_items[i], this.document.getElementById(ed.toolbar_items[i].id));
        }
        else
        {
          // dropdown
          ed.theme.dropdownOut(ed.toolbar_items[i], this.document.getElementById(ed.toolbar_items[i].id));
        }
        
        // check if button is pushed
        if (ed.toolbar_items[i].on_pushed_check && ed.toolbar_items[i].on_pushed_check != '')
        {
          if (eval("SpawPG"+ed.toolbar_items[i].module 
                  + '.' + ed.toolbar_items[i].on_pushed_check + '(this, ed.toolbar_items[i])'))
          {
            ed.toolbar_items[i].is_pushed = true;
            ed.theme.buttonDown(ed.toolbar_items[i], this.document.getElementById(ed.toolbar_items[i].id));
          }
          else
          {
            ed.toolbar_items[i].is_pushed = false;
            ed.theme.buttonOut(ed.toolbar_items[i], this.document.getElementById(ed.toolbar_items[i].id));
          }
        } 
        // update drowpdown value
        if (ed.toolbar_items[i].on_status_check && ed.toolbar_items[i].on_status_check != '')
        {
          var val = eval("SpawPG"+ed.toolbar_items[i].module 
                        + '.' + ed.toolbar_items[i].on_status_check + '(this, ed.toolbar_items[i])');
          var dd = this.document.getElementById(ed.toolbar_items[i].id);
          if (val)
          {
            for(var oi=0; oi<dd.options.length; oi++)
            {
              if (dd.options[oi].value == val)
                dd.options[oi].selected = true;
            }
          }
          else
          {
            dd.selectedIndex = 0;
          }
        }
        
      }
      else
      {
        this.document.getElementById(ed.toolbar_items[i].id).disabled = true;
        ed.toolbar_items[i].is_enabled = false;
        if (ed.toolbar_items[i].on_click)
        { 
          // button     
          ed.theme.buttonOff(ed.toolbar_items[i], this.document.getElementById(ed.toolbar_items[i].id));
        }
        else
        {
          // dropdown
          ed.theme.dropdownOff(ed.toolbar_items[i], this.document.getElementById(ed.toolbar_items[i].id));
        }
      }
    }
  }
}

// editor content
// updates code editor content from wysiwyg editor
SpawEditor.prototype.updatePageInput = function(page)
{
  var pdoc = this.getPageDoc(page.name);
  var pta = this.getPageInput(page.name);
  pta.value = this.getPageHtml(page); 
}
// updates wysiwyg editor content from code editor
SpawEditor.prototype.updatePageDoc = function(page)
{
  var pdoc = this.getPageDoc(page.name);
  var pta = this.getPageInput(page.name);
  pdoc.body.innerHTML = pta.value;
  this.flash2img();
}
// returns html of the current page
SpawEditor.prototype.getPageHtml = function(page)
{
  // raise get html event
  SpawEngine.handleEvent("spawgethtml", null, "page_doc", this.name);

  var pdoc = this.getPageDoc(page.name);
  var pta = this.getPageInput(page.name);
  var result;
  if (page.editing_mode == "html")
  {
    // html mode
    result = pta.value;
  }
  else if(page.editing_mode == "design")
  {
    // remove glyphs
    this.removeGlyphs(pdoc.body);
    // replace flash placeholders
    this.img2flash();
    // strip absolute urls
    this.stripAbsoluteUrls();
    // wysiwyg mode
    if (this.getConfigValue("rendering_mode") == "builtin")
    {
      // let browser handle html rendering
      result = pdoc.body.innerHTML;
    }
    else
    {
      // call custom html renderer
      result = this.dom2xml(pdoc.body, '');
    }
  }
  if (this.getConfigValue('convert_html_entities'))
    result = this.convertToEntities(result);

  return result;
} 
SpawEditor.updateFields = function(editor, event)
{
  editor.updateFields();
}
SpawEditor.prototype.updateFields = function()
{
  for(var i=0; i<this.pages.length; i++)
  {
    this.updatePageInput(this.pages[i]);
  }
}
SpawEditor.prototype.spawSubmit = function()
{
  SpawEngine.updateFields();
  var frm = this.getPageInput(this.pages[0].name).form;
  document.forms[0].setAttribute("__spawsubmiting","true");
  frm.formSubmit();
}

// renders xhtml
SpawEditor.prototype.dom2xml = function(node, indent)
{
    var xbuf = '';
    var f_indent = '';
    var e_indent = '';
    var f_crlf = '';
    var e_crlf = '';
    for (var i=0; i<node.childNodes.length; i++)
    {
      var chnode = node.childNodes[i];
      if (chnode.nodeType == 3) // text node
      {
        if (SpawUtils.trim(chnode.nodeValue).length > 0)
          xbuf += SpawUtils.trimLineBreaks(SpawUtils.htmlEncode(chnode.nodeValue));
      }
      else if (chnode.nodeType == 8) // comment node
      {
        xbuf += "<!--" + chnode.nodeValue + "-->";
      }
      else if (chnode.nodeType == 1) // html element
      {
        if (chnode.getAttribute("__spawprocessed") == null) // workaround to prevent elements from doubling
        {
          chnode.setAttribute("__spawprocessed",true);
          // form attributes string
          var attr_str = '';
          for(var j=0; j<chnode.attributes.length; j++)
          {
            var attr = chnode.attributes[j];
            if (attr.nodeValue != null 
                && (chnode.getAttribute(attr.nodeName, 2)!=null || chnode.getAttribute(attr.nodeName, 0) != null)
                && attr.specified && attr.nodeName.toLowerCase()!="__spawprocessed" 
                && attr.nodeName.toLowerCase().indexOf("_moz") != 0)
            {
              var attrval = chnode.getAttribute(attr.nodeName, 2);
              if (attrval == null)
                attrval = chnode.getAttribute(attr.nodeName, 0);
              
              if (chnode.tagName.toLowerCase() != 'font')
              {
                if (attr.nodeName.toLowerCase() != 'class' && attr.nodeName.toLowerCase() != 'style'
                   && attr.nodeName.toLowerCase() != 'href' && attr.nodeName.toLowerCase() != 'src' 
                   && attr.nodeName.toLowerCase() != 'shape' && attr.nodeName.toLowerCase() != 'coords' // coords and shape attributes are lost in area tag in IE 
                   && attr.nodeName.toLowerCase() != 'type' && attr.nodeName.toLowerCase() != 'value' && attr.nodeName.toLowerCase() != 'enctype') // type and value get lost on IE in input tags
                  attr_str += ' ' + attr.nodeName.toLowerCase() + '="' + attrval + '"';
              }
              else
              {
                // replace font tag with span
                if (attr.nodeName.toLowerCase() == 'face')
                  chnode.style.fontFamily = attrval;
                else if (attr.nodeName.toLowerCase() == 'size')
                {
                  switch(attrval)
                  {
                    case '1':
                      attrval = 'xx-small';
                      break;
                    case '2':
                      attrval = 'x-small';
                      break;
                    case '3':
                      attrval = 'small';
                      break;
                    case '4':
                      attrval = 'medium';
                      break;
                    case '5':
                      attrval = 'large';
                      break;
                    case '6':
                      attrval = 'x-large';
                      break;
                    case '7':
                      attrval = 'xx-large';
                      break;
                    default:
                      attrval = 'medium';
                      break;
                  }
                  chnode.style.fontSize = attrval;
                } 
                else if (attr.nodeName.toLowerCase() == 'color')
                  chnode.style.color = attrval; 
                  
                // crop attribute string
                attr_str = '';
              }
            }
            
          }
          // style attribute
          if (chnode.style.cssText && chnode.style.cssText != '')
            attr_str += ' style="' + chnode.style.cssText + '"';
        
          // class attribute
          if (chnode.className && chnode.className != '')
            attr_str += ' class="' + chnode.className + '"';

          if (chnode.type && chnode.type != '')
            attr_str += ' type="' + chnode.type + '"';
          if (chnode.value && chnode.value != '' 
            && !(chnode.tagName.toLowerCase() == 'li' && chnode.value == '-1')) // workaround for firefox 
            attr_str += ' value="' + chnode.value + '"';
          if (chnode.enctype && chnode.enctype != '' && chnode.enctype != 'application/x-www-form-urlencoded')
            attr_str += ' enctype="' + chnode.enctype + '"';

          // replace & to &amp; in href and src attributes
          if (chnode.href && chnode.href != '' && chnode.tagName.toLowerCase() != 'img')
            attr_str += ' href="' +  this.getStrippedAbsoluteUrl(chnode.href, false).replace(/&amp;/g,"&").replace(/&/g,"&amp;") + '"';
          if (chnode.src && chnode.src != '')
            attr_str += ' src="' +  this.getStrippedAbsoluteUrl(chnode.src, true).replace(/&amp;/g,"&").replace(/&/g,"&amp;") + '"';
        
          // shape and coords attributes
          if (chnode.coords && chnode.coords != '') 
            attr_str += ' coords="' + chnode.coords + '"'; 
          if (chnode.shape && chnode.shape != '') 
            attr_str += ' shape="' + chnode.shape + '"'; 

          if (this.getConfigValue("beautify_xhtml_output"))
          {
            switch(chnode.tagName.toLowerCase())
            {
              case "p":
              case "td":
              case "th":
              case "label":
              case "li":
              case "br":
                f_indent = indent;
                f_crlf = '\n';
                e_indent = '';
                e_crlf = '';
                break;
              case "table":
              case "thead":
              case "tfoot":
              case "tbody":
              case "tr":
              case "div":
              case "ul":
              case "ol":
                f_indent = indent;
                f_crlf = '\n';
                e_indent = indent;
                e_crlf = '\n';
                break;
              default:
                f_indent = '';
                f_crlf = '';
                e_indent = '';
                e_crlf = '';
            }
          }  
          if (chnode.tagName.toLowerCase() != "script")
          {
            // replace font with span
            var tag_name = (chnode.tagName.toLowerCase() != 'font')?chnode.tagName.toLowerCase():'span';
            if (chnode.childNodes.length>0)
            {
              var innercode = this.dom2xml(chnode, indent + ((f_indent!="tmp")?"  ":""));
              if (SpawUtils.trim(innercode) == '')
                innercode = '&nbsp;';
              xbuf += f_crlf + f_indent + "<" + SpawUtils.trim(tag_name + attr_str) + ">" + innercode + e_crlf + e_indent + "</" + tag_name + ">";
            }
            else if (chnode.tagName.indexOf("/") == -1)// empty tag (sometimes ending tag is passed as a separate node)
            {
              if (tag_name == "img"
                || tag_name == "br"
                || tag_name == "wbr"
                || tag_name == "hr"
                || tag_name == "input")
              {
                xbuf += f_crlf + f_indent + "<" + SpawUtils.trim(tag_name + attr_str) + " />" + e_crlf + e_indent;
              }
              else
              {
                // don't generate empty useless tags
                if (tag_name != "b"
                  && tag_name != "i"
                  && tag_name != "u"
                  && tag_name != "strike"
                  && tag_name != "strong"
                  && tag_name != "em"
                  && tag_name != "i"
                  && tag_name != "span"
                  )
                {
                  var innercode = '';
                  if (tag_name == 'p')
                    innercode = '&nbsp;';
                  xbuf += f_crlf + f_indent + "<" + SpawUtils.trim(tag_name + attr_str) + ">" + innercode + "</" + tag_name + ">";
                }
              }
            }
          }
          else // script
          {
            xbuf += f_crlf + f_indent + "<" + SpawUtils.trim(chnode.tagName.toLowerCase() + attr_str) + ">" + chnode.innerHTML + "</" + chnode.tagName.toLowerCase() + ">";                    
          }
        }
      }
    }
    return xbuf;
}

// cleans up code
SpawEditor.prototype.getCleanCode = function(node, clean_type) // clean_type reserved for future use
{
  var xbuf = '';
  for (var i=0; i<node.childNodes.length; i++)
  {
    var chnode = node.childNodes[i];
    if (chnode.nodeType == 3) // text node
    {
      if (SpawUtils.trim(chnode.nodeValue).length > 0)
        xbuf += chnode.nodeValue.replace(/\u00A0/g, "&nbsp;");
    }
    else if (chnode.nodeType == 8) // comment node
    {
      xbuf += "<!--" + chnode.nodeValue + "-->";
    }
    else if (chnode.nodeType == 1) // html element
    {
      if (chnode.getAttribute("__spawprocessed") == null) // workaround to prevent elements from doubling
      {
        chnode.setAttribute("__spawprocessed",true);
        // form attributes string
        var attr_str = '';
        for(var j=0; j<chnode.attributes.length; j++)
        {
          var attr = chnode.attributes[j];
          if (attr.nodeValue != null && chnode.getAttribute(attr.nodeName, 2)!=null && attr.specified && attr.nodeName.toLowerCase()!="__spawprocessed" && attr.nodeName.toLowerCase().indexOf("_moz") != 0)
          {
            var attrval = chnode.getAttribute(attr.nodeName, 2);
            
            if (attr.nodeName.toLowerCase() != 'class' && attr.nodeName.toLowerCase() != 'style'
              && attr.nodeName.toLowerCase() != 'type' && attr.nodeName.toLowerCase() != 'value' && attr.nodeName.toLowerCase() != 'enctype') // type and value get lost on IE in input tags
              attr_str += ' ' + attr.nodeName.toLowerCase() + '="' + attrval + '"';
          }
          
        }

        if (chnode.type && chnode.type != '')
          attr_str += ' type="' + chnode.type + '"';
        if (chnode.value && chnode.value != '')
          attr_str += ' value="' + chnode.value + '"';
        if (chnode.enctype && chnode.enctype != '' && chnode.enctype != 'application/x-www-form-urlencoded')
          attr_str += ' enctype="' + chnode.enctype + '"';
      
        if (chnode.tagName.toLowerCase() != "script")
        {
          if (chnode.childNodes.length>0)
          {
            if (chnode.tagName.indexOf(":") == -1 && chnode.tagName.toLowerCase() != 'font' && chnode.tagName.toLowerCase() != 'div' && chnode.tagName.toLowerCase() != 'span')
              xbuf += "<" + SpawUtils.trim(chnode.tagName.toLowerCase() + attr_str) + ">" + this.getCleanCode(chnode, clean_type) + "</" + chnode.tagName.toLowerCase() + ">";
            else
              xbuf += this.getCleanCode(chnode, clean_type);
          }
          else if (chnode.tagName.indexOf("/") == -1)// empty tag (sometimes ending tag is passed as a separate node)
          {
            if (chnode.tagName.indexOf(":") == -1 && chnode.tagName.toLowerCase() != 'font' && chnode.tagName.toLowerCase() != 'div' && chnode.tagName.toLowerCase() != 'span')
            {
              if (chnode.tagName.toLowerCase() == "img"
                || chnode.tagName.toLowerCase() == "br"
                || chnode.tagName.toLowerCase() == "wbr"
                || chnode.tagName.toLowerCase() == "hr"
                || chnode.tagName.toLowerCase() == "input")
              {
                xbuf += "<" + SpawUtils.trim(chnode.tagName.toLowerCase() + attr_str) + " />";
              }
              else
              {
                xbuf += "<" + SpawUtils.trim(chnode.tagName.toLowerCase() + attr_str) + "></" + chnode.tagName.toLowerCase() + ">";
              }
            }
          }
        }
        else // script
        {
          xbuf += "<" + SpawUtils.trim(chnode.tagName.toLowerCase() + attr_str) + ">" + chnode.innerHTML + "</" + chnode.tagName.toLowerCase() + ">";                    
        }
      }
    }
  }
  return xbuf;
}
// cleans current page code
SpawEditor.prototype.cleanPageCode = function(clean_type) // clean_type reserved for future use
{
  var pname = this.getActivePage().name;
  var pdoc = this.getActivePageDoc();
  var pta = this.getPageInput(pname);

  pta.value = this.getCleanCode(pdoc.body, clean_type);
  this.updatePageDoc(this.getActivePage());     
}


// status bar
SpawEditor.prototype.showStatus = function(message)
{
  var sb = this.document.getElementById(this.name + '_status');
  if (sb && !document.attachEvent) // disable status bar in IE cause it breaks undo/redo
  {
    sb.innerHTML = message;
  }
}

// right click
SpawEditor.rightClick = function(editor, event)
{
  editor.rightClick(editor, event);
}
SpawEditor.prototype.rightClick = function(editor, event)
{
  if (SpawEngine.active_context_menu != null)
    SpawEngine.active_context_menu.hide();
  var cm = new SpawContextMenu(this);
  if (cm.show(event))
  {
    SpawEngine.active_context_menu = cm;
    if (event.preventDefault)
      event.preventDefault();
    else
      event.returnValue = false;
  }
}
// hide context menu
SpawEditor.hideContextMenu = function(editor, event)
{
  editor.hideContextMenu(editor, event);
}
SpawEditor.prototype.hideContextMenu = function(editor, event)
{
  if (SpawEngine.active_context_menu)
  {
    SpawEngine.active_context_menu.hide();
    SpawEngine.active_context_menu = null;
  }
}

// returns specified element surrounding current selection, returns null if selection is not inside such element 
SpawEditor.prototype.getSelectedElementByTagName = function(tagName)
{
  var result = null;
  var elm = this.getSelectionParent();
  
  while (elm && elm.tagName && elm.tagName.toLowerCase() != tagName.toLowerCase() && elm.tagName.toLowerCase() != 'body')
    elm = elm.parentNode;
    
  if (elm && elm.tagName && elm.tagName.toLowerCase() != 'body')
    result = elm;
    
  return result;
}

// returns all anchors on the active page
SpawEditor.prototype.getAnchors = function()
{
  var anchors = new Array();
  var pdoc = this.getActivePageDoc();
  var links = pdoc.getElementsByTagName("a");
  for (var i=0; i<links.length; i++)
  {
    if (links[i].name && links[i].name != '')
      anchors.push(links[i]); 
  }
  return anchors;
}

// show borders on borderless objects
SpawEditor.prototype.show_glyphs = true;

// returns true if active page is in design mode
SpawEditor.prototype.isInDesignMode = function()
{
  return (this.getActivePage().editing_mode == "design");
}

// removes absolute path portion from url
SpawEditor.prototype.getStrippedAbsoluteUrl = function(url, host_only)
{
  if (this.getConfigValue('strip_absolute_urls'))
  {
    var pdoc = this.getActivePageDoc();

  	var curl = pdoc.location.href;
  	var di = curl.lastIndexOf('/', curl.lastIndexOf('?')!=-1?curl.lastIndexOf('?'):curl.length);
  	var cdir = curl;
  	if (di != -1)
  		cdir = curl.substr(0,di+1);
  	var chost = curl;
  	var hi = curl.indexOf('/',curl.indexOf('://')!=-1?(curl.indexOf('://')+3):curl.length);
  	if (hi != -1)
  		chost = curl.substr(0,hi);
   	if (url.toLowerCase().indexOf(curl.toLowerCase())==0 && !host_only)
  	{
  		url = url.substr(curl.length);
  	}
  	else if (url.toLowerCase().indexOf(cdir.toLowerCase())==0 && !host_only)
  	{
  		url = url.substr(cdir.length);
  	}
  	else if (url.toLowerCase().indexOf(chost.toLowerCase())==0)
  	{
  		url = url.substr(chost.length);
  	}
	}
	return url;
}
SpawEditor.prototype.stripAbsoluteUrls = function()
{
  if (this.getConfigValue('strip_absolute_urls'))
  {
    var pdoc = this.getActivePageDoc();
   
    var links = pdoc.getElementsByTagName("a");
    for(var i=0; i<links.length; i++)
    {
      if (links[i].href && links[i].href != '')
        links[i].href = this.getStrippedAbsoluteUrl(links[i].href, false); 
    } 
    var imgs = pdoc.getElementsByTagName("img");
    for(var i=0; i<imgs.length; i++)
    {
      if (imgs[i].src && imgs[i].src != '')
        imgs[i].src = this.getStrippedAbsoluteUrl(imgs[i].src, true); 
    } 
  }
}
// strips absolute url from single object
SpawEditor.prototype.stripAbsoluteUrl = function(elm)
{
  if (this.getConfigValue('strip_absolute_urls') && elm && elm.nodeType == 1)
  {
    if (elm.tagName.toLowerCase() == 'a' && elm.href && elm.href != "")
    {
      elm.href = this.getStrippedAbsoluteUrl(elm.href, false); 
    }
    else if (elm.tagName.toLowerCase() == 'img' && elm.src && elm.src != "")
    {
      elm.src = this.getStrippedAbsoluteUrl(elm.src, true); 
    }
  }
}

// workaround functions to deal with flash objects
// replaces flash movies with image placeholders
SpawEditor.prototype.flash2img = function()
{
  var pdoc = this.getActivePageDoc();
  var flashs_elm = pdoc.getElementsByTagName("EMBED");

  // create a copy that wont be affected by changes to document
  var flashs = new Array();
  for (var i=0; i<flashs_elm.length; i++)
  {
    flashs[i] = flashs_elm[i];
  }

  for (var i=0; i<flashs.length; i++)
  {
    if (flashs[i].attributes.getNamedItem('src') != null /*&& flashs[i].attributes.getNamedItem('src').nodeValue.indexOf(".swf") != -1*/)
    {
  	  var flash = pdoc.createElement("IMG");
  	  flash.setAttribute('src', SpawEngine.spaw_dir + 'img/spacer100.gif?imgtype=flash&src='+flashs[i].getAttribute("src"));
    	if (flashs[i].style.cssText != '') // save original style
    	{
        flash.setAttribute("__spaw_style", flashs[i].style.cssText);
        if (flashs[i].style.width != '')
          flash.setAttribute('width',flashs[i].style.width);
        if (flashs[i].style.height != '')
          flash.setAttribute('height',flashs[i].style.height);
      }  
      // set attributes
      for(var j=0; j<flashs[i].attributes.length; j++)
      {
        var attr = flashs[i].attributes[j];
        if (attr.nodeValue != null 
            && (flashs[i].getAttribute(attr.nodeName, 2)!=null || flashs[i].getAttribute(attr.nodeName, 0) != null)
            && attr.specified 
            && attr.nodeName.toLowerCase().indexOf("_moz") != 0
            && attr.nodeName.toLowerCase() != "src")
        {
          var attrval = flashs[i].getAttribute(attr.nodeName, 2);
          if (attrval == null)
            attrval = flashs[i].getAttribute(attr.nodeName, 0);
          flash.setAttribute(attr.nodeName.toLowerCase(), attrval);
        }
      }
  	  flash.style.cssText = "border: 1px solid #000000; background: url(" + SpawEngine.spaw_dir + "img/flash.gif);";

      flashs[i].parentNode.replaceChild(flash, flashs[i]);
    }
  }
}
// replaces image placeholders for flash movies into embed tags
SpawEditor.prototype.img2flash = function()
{
  var pdoc = this.getActivePageDoc();
  var imgs_elm = pdoc.getElementsByTagName("IMG");

  // create a copy that wont be affected by changes to document
  var imgs = new Array();
  for (var i=0; i<imgs_elm.length; i++)
  {
    imgs[i] = imgs_elm[i];
  }
  
  
  for (var i=0; i<imgs.length; i++)
  {
    if (imgs[i].src.indexOf("spacer100.gif?imgtype=flash") != -1)
    {
      var flash = pdoc.createElement('EMBED');
  	  flash.setAttribute('type','application/x-shockwave-flash');
  	  flash.setAttribute('src',imgs[i].src.substring(imgs[i].src.indexOf("src=")+4));

      // set attributes
      for(var j=0; j<imgs[i].attributes.length; j++)
      {
        var attr = imgs[i].attributes[j];
        if (attr.nodeValue != null 
            && (imgs[i].getAttribute(attr.nodeName, 2)!=null || imgs[i].getAttribute(attr.nodeName, 0) != null)
            && attr.specified 
            && attr.nodeName.toLowerCase().indexOf("_moz") != 0
            && attr.nodeName.toLowerCase() != "src"
            && attr.nodeName.toLowerCase() != "type"
            && attr.nodeName.toLowerCase() != "style")
        {
          var attrval = imgs[i].getAttribute(attr.nodeName, 2);
          if (attrval == null)
            attrval = imgs[i].getAttribute(attr.nodeName, 0);
          flash.setAttribute(attr.nodeName.toLowerCase(), attrval);
        }
      }
    	if (imgs[i].getAttribute("__spaw_style", 2) != null) // restore original style
    	{
        flash.style.cssText = imgs[i].getAttribute("__spaw_style", 2);
        flash.removeAttribute("__spaw_style");
      }  
        	   
  	  imgs[i].parentNode.replaceChild(flash, imgs[i]);
    }
  }
}

// context
SpawEditor.prototype.current_context;
SpawEditor.checkContext = function(editor, event)
{
  editor.checkContext(editor, event);
}
SpawEditor.prototype.checkContext = function(editor, event)
{
  if (SpawEngine.getActiveEditor() != this)
    SpawEngine.setActiveEditor(this);

  var sp = this.getSelectionParent();
  if (this.current_context != sp)
  {
    this.updateToolbar();
    this.current_context = sp;
  }  
}

// focus
SpawEditor.prototype.focus = function()
{
  var obj = (this.isInDesignMode())?this.getPageIframe(this.getActivePage().name):this.getPageInput(this.getActivePage().name);
  if (obj.contentWindow) // gecko
    obj.contentWindow.focus();
  else
    obj.focus();
}
// event handling
// array storing event handlers
SpawEngine.event_handlers = new Array();
SpawEngine.addEventHandler = function(evt_type, handler_fn, evt_target)
{
  var trg = (evt_target == null)?"page_doc":evt_target.toLowerCase();
  if (!SpawEngine.event_handlers[trg])
    SpawEngine.event_handlers[trg] = new Array();
  if (SpawEngine.event_handlers[trg][evt_type])
  {
    // this is not the first handler for this event
    SpawEngine.event_handlers[trg][evt_type].push(handler_fn);
  }
  else
  {
    // there are no handlers for this event
    if (evt_type.toLowerCase().substring(0,4) != "spaw")
    {
      // non-spaw event, register handlers
      var ev_obj;
      if (trg.substring(0,4) != "page" && trg != "form")
      {
        // not page or editor level events
        ev_obj = SpawEngine.getEventTargetObject(trg, null);
        if (ev_obj.attachEvent)
        {
          // ie
          ev_obj.attachEvent("on"+evt_type, new Function("event", 'SpawEngine.handleEvent("'+evt_type+'", event, "'+trg+'", null);'));
        }
        else
        {
          ev_obj.addEventListener(evt_type, new Function("event", 'SpawEngine.handleEvent("'+evt_type+'", event, "'+trg+'", null);'), false);
        }
      }
      else
      {
        var old_ev_obj;
        for (var si=0; si<SpawEngine.editors.length; si++)
        {
          if (trg == "form")
          {
            // editor level event
            ev_obj = SpawEngine.getEventTargetObject(trg, null, SpawEngine.editors[si]);
            if (ev_obj != old_ev_obj)
            {
              if (ev_obj.attachEvent)
              {
                // ie
                ev_obj.attachEvent("on"+evt_type, new Function("event", 'SpawEngine.handleEvent("'+evt_type+'", event, "'+trg+'","'+SpawEngine.editors[si].name+'");'));
              }
              else
              {
                ev_obj.addEventListener(evt_type, new Function("event", 'SpawEngine.handleEvent("'+evt_type+'", event, "'+trg+'","'+SpawEngine.editors[si].name+'");'), false);
              }
              old_ev_obj = ev_obj;
            }
          }
          else
          {
            // page level event
            for(var i=0; i<SpawEngine.editors[si].pages.length; i++)
            {
              ev_obj = SpawEngine.getEventTargetObject(trg, SpawEngine.editors[si].pages[i], SpawEngine.editors[si]);
              if (ev_obj.attachEvent)
              {
                // ie
                ev_obj.attachEvent("on"+evt_type, new Function("event", 'SpawEngine.handleEvent("'+evt_type+'", event, "'+trg+'","'+SpawEngine.editors[si].name+'");'));
              }
              else
              {
                ev_obj.addEventListener(evt_type, new Function("event", 'SpawEngine.handleEvent("'+evt_type+'", event, "'+trg+'","'+SpawEngine.editors[si].name+'");'), false);
              }
            }
          }
        }
      }
    }
    SpawEngine.event_handlers[trg][evt_type] = new Array();
    SpawEngine.event_handlers[trg][evt_type].push(handler_fn);
  }
}
SpawEngine.handleEvent = function(evt_type, evt, evt_target, editor_name)
{
  var trg = (evt_target == null)?"page_doc":evt_target.toLowerCase();
  var ed = editor_name?SpawEngine.getEditor(editor_name):SpawEngine.getActiveEditor();
  if (SpawEngine.event_handlers[trg] && SpawEngine.event_handlers[trg][evt_type])
  {
    for(var i=0; i<SpawEngine.event_handlers[trg][evt_type].length; i++)
    {
      eval(SpawEngine.event_handlers[trg][evt_type][i] + '(ed, evt)');
    }
  }
}
SpawEngine.getEventTargetObject = function(evt_target, page, editor)
{
  var trg = (evt_target == null)?"page_doc":evt_target.toLowerCase();
  var ev_obj;
  switch(trg)
  {
    case "page_iframe":
      ev_obj = editor.getPageIframeObject(page.name);
      break;
    case "page_doc":
      ev_obj = editor.getPageDoc(page.name);
      break;
    case "page_body":
      ev_obj = editor.getPageDoc(page.name).body;
      break;
    case "form":
      ev_obj = editor.getPageInput(editor.getActivePage().name).form;
      break;
    case "window":
      ev_obj = window;
      break;
    case "document":
      ev_obj = document;
      break;
    default:
      ev_obj = editor.getActivePageDoc();
      break;
  }
  return ev_obj;
}
// color class
function SpawColor()
{
}
SpawColor.prototype.hue = 359;
SpawColor.prototype.saturation = 100;
SpawColor.prototype.brightness = 100;
SpawColor.prototype.red = 255;
SpawColor.prototype.green = 255;
SpawColor.prototype.blue = 255;

SpawColor.prototype.setHSB = function(h, s, b)
{
  if (h != null)
    this.hue = h;
  if (s != null)
    this.saturation = s;
  if (b != null)
    this.brightness = b;
  this.updateRGBFromHSB();
}
SpawColor.prototype.setRGB = function(r, g, b)
{
  if (r != null)
    this.red = r;
  if (g != null)
    this.green = g;
  if (b != null)
    this.blue = b;
  this.updateHSBFromRGB();
}
SpawColor.prototype.setRGBFromHTML = function(val)
{
  var sval = val;
  if ((sval.length != 4 && sval.length != 7) || sval.charAt(0) != '#') 
  {
    // try named colors
    sval = this.getHTMLColorFromKeyword(sval);
  }

  if (sval != null && (sval.length == 4 || sval.length == 7) && sval.charAt(0) == '#')
  { 
    if (sval.length == 4)
    {
      this.red = this.hex2dec(sval.charAt(1)+sval.charAt(1));
      this.green = this.hex2dec(sval.charAt(2)+sval.charAt(2));
      this.blue = this.hex2dec(sval.charAt(3)+sval.charAt(3));
    }
    else
    {
      this.red = this.hex2dec(sval.substring(1,3));
      this.green = this.hex2dec(sval.substring(3,5));
      this.blue = this.hex2dec(sval.substring(5,7));
    }
    this.updateHSBFromRGB();
  }
}
SpawColor.prototype.getHTMLColorFromKeyword = function(kwd)
{
  var named_colors = new Array();
  named_colors['aliceblue']='#f0f8ff';
  named_colors['antiquewhite']='#faebd7';
  named_colors['aqua']='#00ffff';
  named_colors['aquamarine']='#7fffd4';
  named_colors['azure']='#f0ffff';
  named_colors['beige']='#f5f5dc';
  named_colors['bisque']='#ffe4c4';
  named_colors['black']='#000000';
  named_colors['blanchedalmond']='#ffebcd';
  named_colors['blue']='#0000ff';
  named_colors['blueviolet']='#8a2be2';
  named_colors['brown']='#a52a2a';
  named_colors['burlywood']='#deb887';
  named_colors['cadetblue']='#5f9ea0';
  named_colors['chartreuse']='#7fff00';
  named_colors['chocolate']='#d2691e';
  named_colors['coral']='#ff7f50';
  named_colors['cornflowerblue']='#6495ed';
  named_colors['cornsilk']='#fff8dc';
  named_colors['crimson']='#dc143c';
  named_colors['cyan']='#00ffff';
  named_colors['darkblue']='#00008b';
  named_colors['darkcyan']='#008b8b';
  named_colors['darkgoldenrod']='#b8860b';
  named_colors['darkgray']='#a9a9a9';
  named_colors['darkgreen']='#006400';
  named_colors['darkkhaki']='#bdb76b';
  named_colors['darkmagenta']='#8b008b';
  named_colors['darkolivegreen']='#556b2f';
  named_colors['darkorange']='#ff8c00';
  named_colors['darkorchid']='#9932cc';
  named_colors['darkred']='#8b0000';
  named_colors['darksalmon']='#e9967a';
  named_colors['darkseagreen']='#8fbc8f';
  named_colors['darkslateblue']='#483d8b';
  named_colors['darkslategray']='#2f4f4f';
  named_colors['darkturquoise']='#00ced1';
  named_colors['darkviolet']='#9400d3';
  named_colors['deeppink']='#ff1493';
  named_colors['deepskyblue']='#00bfff';
  named_colors['dimgray']='#696969';
  named_colors['dodgerblue']='#1e90ff';
  named_colors['firebrick']='#b22222';
  named_colors['floralwhite']='#fffaf0';
  named_colors['forestgreen']='#228b22';
  named_colors['fuchsia']='#ff00ff';
  named_colors['gainsboro']='#dcdcdc';
  named_colors['ghostwhite']='#f8f8ff';
  named_colors['gold']='#ffd700';
  named_colors['goldenrod']='#daa520';
  named_colors['gray']='#808080';
  named_colors['green']='#008000';
  named_colors['greenyellow']='#adff2f';
  named_colors['honeydew']='#f0fff0';
  named_colors['hotpink']='#ff69b4';
  named_colors['indianred']='#cd5c5c';
  named_colors['indigo']='#4b0082';
  named_colors['ivory']='#fffff0';
  named_colors['khaki']='#f0e68c';
  named_colors['lavender']='#e6e6fa';
  named_colors['lavenderblush']='#fff0f5';
  named_colors['lawngreen']='#7cfc00';
  named_colors['lemonchiffon']='#fffacd';
  named_colors['lightblue']='#add8e6';
  named_colors['lightcoral']='#f08080';
  named_colors['lightcyan']='#e0ffff';
  named_colors['lightgoldenrodyellow']='#fafad2';
  named_colors['lightgreen']='#90ee90';
  named_colors['lightgrey']='#d3d3d3';
  named_colors['lightpink']='#ffb6c1';
  named_colors['lightsalmon']='#ffa07a';
  named_colors['lightseagreen']='#20b2aa';
  named_colors['lightskyblue']='#87cefa';
  named_colors['lightslategray']='#778899';
  named_colors['lightsteelblue']='#b0c4de';
  named_colors['lightyellow']='#ffffe0';
  named_colors['lime']='#00ff00';
  named_colors['limegreen']='#32cd32';
  named_colors['linen']='#faf0e6';
  named_colors['magenta']='#ff00ff';
  named_colors['maroon']='#800000';
  named_colors['mediumaquamarine']='#66cdaa';
  named_colors['mediumblue']='#0000cd';
  named_colors['mediumorchid']='#ba55d3';
  named_colors['mediumpurple']='#9370db';
  named_colors['mediumseagreen']='#3cb371';
  named_colors['mediumslateblue']='#7b68ee';
  named_colors['mediumspringgreen']='#00fa9a';
  named_colors['mediumturquoise']='#48d1cc';
  named_colors['mediumvioletred']='#c71585';
  named_colors['midnightblue']='#191970';
  named_colors['mintcream']='#f5fffa';
  named_colors['mistyrose']='#ffe4e1';
  named_colors['moccasin']='#ffe4b5';
  named_colors['navajowhite']='#ffdead';
  named_colors['navy']='#000080';
  named_colors['oldlace']='#fdf5e6';
  named_colors['olive']='#808000';
  named_colors['olivedrab']='#6b8e23';
  named_colors['orange']='#ffa500';
  named_colors['orangered']='#ff4500';
  named_colors['orchid']='#da70d6';
  named_colors['palegoldenrod']='#eee8aa';
  named_colors['palegreen']='#98fb98';
  named_colors['paleturquoise']='#afeeee';
  named_colors['palevioletred']='#db7093';
  named_colors['papayawhip']='#ffefd5';
  named_colors['peachpuff']='#ffdab9';
  named_colors['peru']='#cd853f';
  named_colors['pink']='#ffc0cb';
  named_colors['plum']='#dda0dd';
  named_colors['powderblue']='#b0e0e6';
  named_colors['purple']='#800080';
  named_colors['red']='#ff0000';
  named_colors['rosybrown']='#bc8f8f';
  named_colors['royalblue']='#4169e1';
  named_colors['saddlebrown']='#8b4513';
  named_colors['salmon']='#fa8072';
  named_colors['sandybrown']='#f4a460';
  named_colors['seagreen']='#2e8b57';
  named_colors['seashell']='#fff5ee';
  named_colors['sienna']='#a0522d';
  named_colors['silver']='#c0c0c0';
  named_colors['skyblue']='#87ceeb';
  named_colors['slateblue']='#6a5acd';
  named_colors['slategray']='#708090';
  named_colors['snow']='#fffafa';
  named_colors['springgreen']='#00ff7f';
  named_colors['steelblue']='#4682b4';
  named_colors['tan']='#d2b48c';
  named_colors['teal']='#008080';
  named_colors['thistle']='#d8bfd8';
  named_colors['tomato']='#ff6347';
  named_colors['turquoise']='#40e0d0';
  named_colors['violet']='#ee82ee';
  named_colors['wheat']='#f5deb3';
  named_colors['white']='#ffffff';
  named_colors['whitesmoke']='#f5f5f5';
  named_colors['yellow']='#ffff00';
  named_colors['yellowgreen']=['#9acd32'];
  if (named_colors[kwd.toLowerCase()] != null)
    return named_colors[kwd.toLowerCase()];
  else
    return null;
}
SpawColor.parseRGB = function(rgbstr)
{
  var nc = new SpawColor();
  var r, g, b;
  if (!isNaN(parseInt(rgbstr)))
  {
    // number representation
    var n = parseInt(rgbstr);
    r = n%256;
    n = Math.floor(n/256);
    g = n%256;
    n = Math.floor(n/256);
    b = n%256;
    nc.setRGB(r, g, b);
  }
  else if (rgbstr.toLowerCase().indexOf('rgb(') == 0)
  {
    r = parseInt(rgbstr.substring(4, rgbstr.indexOf(',')));
    g = parseInt(rgbstr.substring(rgbstr.indexOf(',')+1, rgbstr.lastIndexOf(',')));
    b = parseInt(rgbstr.substring(rgbstr.lastIndexOf(',')+1, rgbstr.indexOf(')')));
    nc.setRGB(r, g, b);
  }
  else
  {
    nc.setRGBFromHTML(rgbstr);
  }
  return nc;
}

SpawColor.prototype.updateRGBFromHSB = function()
{
  var r,g,b;
  var fS = this.saturation/100;
  var fB = this.brightness/100;
  var hi = Math.floor(this.hue / 60) % 6;
  var f = this.hue / 60 - hi;
  var p = fB * (1 - fS);
  var q = fB * (1 - f * fS);
  var t = fB * (1 - (1 - f) * fS);
  switch (hi)
  {
  case 0:
    r = Math.round(fB * 255);
    g = Math.round(t * 255);
    b = Math.round(p * 255);
    break;
  case 1:
    r = Math.round(q * 255);
    g = Math.round(fB * 255);
    b = Math.round(p * 255);
    break;
  case 2:
    r = Math.round(p * 255);
    g = Math.round(fB * 255);
    b = Math.round(t * 255);
    break;
  case 3:
    r = Math.round(p * 255);
    g = Math.round(q * 255);
    b = Math.round(fB * 255);
    break;
  case 4:
    r = Math.round(t * 255);
    g = Math.round(p * 255);
    b = Math.round(fB * 255);
    break;
  case 5:
    r = Math.round(fB * 255);
    g = Math.round(p * 255);
    b = Math.round(q * 255);
    break;
  }
  this.red = r;
  this.green = g;
  this.blue = b;
}
SpawColor.prototype.updateHSBFromRGB = function()
{
  var h,s;
  var r = this.red/255;
  var g = this.green/255;
  var b = this.blue/255;
  var mx = Math.max(this.red, this.green, this.blue)/255;
  var mn = Math.min(this.red, this.green, this.blue)/255;
  if (mx == mn)
  {
    h = 0;
  }
  else if (mx == r && g>=b)
  {
    h = 60*(g - b)/(mx - mn);
  }
  else if (mx == r && g<b)
  {
    h = 60*(g - b)/(mx - mn) + 360;
  }
  else if (mx == g)
  {
    h = 60*(b - r)/(mx - mn) + 120;
  }
  else if (mx == b)
  {
    h = 60*(r - g)/(mx - mn) + 240;
  }
  
  if (mx == 0)
    s = 0;
  else
    s = Math.round((1 - mn/mx));
    
  this.hue = Math.round(h);
  this.saturation = Math.round(s*100);
  this.brightness = Math.round(mx*100);
}
SpawColor.prototype.dec2hex = function(dec)
{
  var result = '';
  var num = dec;
  while (num/16 >= 1 || num%16 > 0)
  {
    var ch = num%16;
    if (ch>=10)
    {
      switch(ch)
      {
        case 10:
          ch = 'a';
          break;
        case 11:
          ch = 'b';
          break;
        case 12:
          ch = 'c';
          break;
        case 13:
          ch = 'd';
          break;
        case 14:
          ch = 'e';
          break;
        case 15:
          ch = 'f';
          break;
      }
    }
    result = '' + ch + result;
    num = Math.floor(num/16);
  }
  return result;
}
SpawColor.prototype.hex2dec = function(hex)
{
  var l = hex.length;
  var p = 0;
  var result = 0;
  for(var i = l-1; i>=0; i--)
  {
    p = l-i-1;
    var c = hex.charAt(i);
    if (!isNaN(parseInt(c)))
    {
      result += parseInt(c)*Math.pow(16, p); 
    }
    else
    {
      switch(c.toLowerCase())
      {
        case 'a':
          result += 10*Math.pow(16, p);
          break; 
        case 'b':
          result += 11*Math.pow(16, p);
          break; 
        case 'c':
          result += 12*Math.pow(16, p);
          break; 
        case 'd':
          result += 13*Math.pow(16, p);
          break; 
        case 'e':
          result += 14*Math.pow(16, p);
          break; 
        case 'f':
          result += 15*Math.pow(16, p);
          break; 
      }
    }
  }
  return result; 
}
SpawColor.prototype.addZeroes = function(source, num)
{
  var result = source;
  while(result.length<num)
  {
    result = '0' + result;
  }
  return result;
}
SpawColor.prototype.getHtmlColor = function()
{
  return result = '#' + this.addZeroes(this.dec2hex(this.red),2) + this.addZeroes(this.dec2hex(this.green),2) + this.addZeroes(this.dec2hex(this.blue),2);  
}
// Tab class
function SpawTab(page)
{
  this.page = page;
}
SpawTab.prototype.page;
SpawTab.prototype.template;
SpawTab.prototype.active_template;
SpawTab.prototype.setInactive = function()
{
  var tab = document.getElementById(this.page.name + '_tab');
  if (tab)
  {
    tab.innerHTML = this.template;
  }
}
SpawTab.prototype.setActive = function()
{
  var tab = document.getElementById(this.page.name + '_tab');
  if (tab)
  {
    tab.innerHTML = this.active_template;
  }
}
// Page class
function SpawEditorPage(name, caption, direction)
{
  this.name = name;
  this.caption = caption;
  if (direction != null)
    this.direction = direction;
  else
    this.direction = "ltr";
}
SpawEditorPage.prototype.name;
SpawEditorPage.prototype.caption;
SpawEditorPage.prototype.direction;
SpawEditorPage.prototype.value;
SpawEditorPage.prototype.is_initialized = false;
SpawEditorPage.prototype.document;
// editing mode (design or html)
SpawEditorPage.prototype.editing_mode = "design";
// current editing mode toolbar item
SpawEditorPage.prototype.editing_mode_tbi;
// Context menu
function SpawContextMenu(editor)
{
  this.editor = editor;
}
SpawContextMenu.prototype.editor;
SpawContextMenu.prototype.enclosure;
SpawContextMenu.prototype.show = function(event)
{
  var last_tbn = '';
  this.enclosure = document.createElement("div");
  this.enclosure.className = this.editor.theme.prefix+'contextmenu';
  this.enclosure.style.position = "absolute";  
  this.enclosure.style.left = this.editor.getPageOffsetLeft() + event.clientX + "px";  
  this.enclosure.style.top = this.editor.getPageOffsetTop() + event.clientY + "px"; 
  this.enclosure.style.zIndex = 15000; 
  
  // add items
  var ed = this.editor.controlled_by;
  for(var i=0; i<ed.toolbar_items.length; i++)
  {
    if (ed.toolbar_items[i].on_enabled_check && ed.toolbar_items[i].on_enabled_check != '')
    {
      // check if item is button and if it wants to be included in context menu
      if (ed.toolbar_items[i].on_click && ed.toolbar_items[i].show_in_context_menu)
      {
        // check if item is enabled
        if(eval("SpawPG"+ed.toolbar_items[i].module 
                + '.' + ed.toolbar_items[i].on_enabled_check + '(this.editor, ed.toolbar_items[i])'))
        {
          if (last_tbn != '' && ed.toolbar_items[i].toolbar_name != last_tbn)
          {
            // insert separator
            var sep = document.createElement("div");
            sep.className = this.editor.theme.prefix + 'contextmenuseparator';
            this.enclosure.appendChild(sep);             
          } 
          last_tbn = ed.toolbar_items[i].toolbar_name;
          var mitem = document.createElement("div");
          var checkmark = document.createElement("img");
          checkmark.src = SpawEngine.getSpawDir() + 'plugins/core/lib/theme/'+this.editor.theme.prefix+'/img/checkmark.gif';
          checkmark.style.visibility = 'hidden';
          checkmark.className = this.editor.theme.prefix + 'checkmark';
          if (ed.toolbar_items[i].on_pushed_check && ed.toolbar_items[i].on_pushed_check != '')
          {
            if(eval("SpawPG"+ed.toolbar_items[i].module 
                    + '.' + ed.toolbar_items[i].on_pushed_check + '(this.editor, ed.toolbar_items[i])'))
            {
              checkmark.style.visibility = 'visible';
            }
          }          
          mitem.appendChild(checkmark);
          
          mitem.appendChild(document.createTextNode(document.getElementById(ed.toolbar_items[i].id).title));
          mitem.style.cursor = "default";
          mitem.style.whiteSpace = "nowrap";
          mitem.setAttribute("unselectable","on");
          mitem.className = this.editor.theme.prefix + "contextmenuitem";
          mitem.onmouseover = new Function("this.className = '" + this.editor.theme.prefix + "contextmenuitemover'");
          mitem.onmouseout = new Function("this.className = '" + this.editor.theme.prefix + "contextmenuitem'");
          if (mitem.attachEvent)
          {
            // IE
            mitem.attachEvent("onclick", 
              new Function("SpawEngine.active_context_menu.hide(); SpawEngine.active_context_menu = null;"
                + "SpawPG"+ed.toolbar_items[i].module + '.' + ed.toolbar_items[i].on_click + '(' + this.editor.name + '_obj, ' + ed.name + '_obj.toolbar_items[' + i + '], null)'));
            //alert(mitem.outerHTML);
          }
          else if (mitem.addEventListener)
          {
            // Gecko
            mitem.addEventListener("click", 
              new Function("SpawEngine.active_context_menu.hide(); SpawEngine.active_context_menu = null;"
                + "SpawPG"+ed.toolbar_items[i].module + '.' + ed.toolbar_items[i].on_click + '(' + this.editor.name + '_obj, ' + ed.name + '_obj.toolbar_items[' + i + '], null)'), false);
          }
          //alert(mitem.onclick);
          this.enclosure.appendChild(mitem);             
        }
      }
    }
  }  
  
  if (this.enclosure.innerHTML != '')
  {
    document.body.appendChild(this.enclosure);
    return true;
  }
  else
  {
    return false;
  }
}
SpawContextMenu.prototype.hide = function()
{
  if (this.enclosure != null)
  {
    document.body.removeChild(this.enclosure);
  }
}
// Spaw Engine related data and operations
function SpawEngine()
{
}

// spaw directory
SpawEngine.spaw_dir;
SpawEngine.setSpawDir = function(spaw_dir)
{
  SpawEngine.spaw_dir = spaw_dir;
}
SpawEngine.getSpawDir = function()
{
  return (SpawEngine.spaw_dir);
}

SpawEngine.addBrowserEventHandler = function(obj, evt, func)
{
  if (document.attachEvent)
  {
    // ie
    obj.attachEvent("on"+evt, func);
  }
  else
  {
    obj.addEventListener(evt, func, false);
  }
}

// plugin registry
SpawEngine.plugins = new Array();
// registers plugin object or class
SpawEngine.registerPlugin = function(plugin_object)
{
  SpawEngine.plugins.push(plugin_object);
}
// returns plugin class or object
SpawEngine.getPlugin = function(name)
{
  for (var i=0; i<SpawEngine.plugins.length; i++)
  {
    if (SpawEngine.plugins[i].name == name)
      return SpawEngine.plugins[i];
  }
  return null;
}

// editor registry
SpawEngine.editors = new Array();
SpawEngine.registerEditor = function(editor)
{
  SpawEngine.editors.push(editor);
}
SpawEngine.isEditorRegistered = function(name)
{
  for (var i=0; i<SpawEngine.editors.length; i++)
  {
    if (SpawEngine.editors[i].name == name)
      return true;
  }
  return false;
}
SpawEngine.getEditor = function(name)
{
  for (var i=0; i<SpawEngine.editors.length; i++)
  {
    if (SpawEngine.editors[i].name == name)
      return SpawEngine.editors[i];
  }
  return null;
}
// returns true if all editors are initialized
SpawEngine.isInitialized = function()
{
  var result = true;
  for (var i=0; i<SpawEngine.editors.length; i++)
  {
    if (!SpawEngine.editors[i].isInitialized())
    {
      result = false;
      break;
    }
  }
  return result;
}
SpawEngine.updateFields = function()
{
  if (!document.forms[0].getAttribute("__spawsubmiting"))
  {
    for(var i=0; i<SpawEngine.editors.length; i++)
    {
      SpawEngine.editors[i].updateFields();
    }
  }
}
SpawEngine.onSubmit = function()
{
  // raise before submit event
  SpawEngine.handleEvent("spawbeforesubmit", null, null, null);
  SpawEngine.updateFields();
}

SpawEngine.active_editor;
SpawEngine.setActiveEditor = function(editor)
{
  if (SpawEngine.active_editor != editor)
  {
    SpawEngine.active_editor = editor;
    if (editor.floating_mode)
      editor.positionFloatingToolbar();
  }
}
SpawEngine.getActiveEditor = function()
{
  return SpawEngine.active_editor;
}

// global event listeners
SpawEngine.mouseMove = function(event)
{
  if (event == null && window.event != null)
  {
    // workaround for IE
    event = window.event;
    if (event.button != undefined && event.button != 1)
    {
      // if button was released outside window edges (works in IE only)
      if (SpawEngine.resizingEditor != null)
      {
        SpawEngine.resizingEditor.isResizing = false;
        //SpawEngine.resizingEditor.showPage(SpawEngine.resizingEditor.getActivePage());
        SpawEngine.resizingEditor.finalizeResizing();
        SpawEngine.resizingEditor = null;
      }
      if (SpawEngine.movingToolbar != null)
      {
        SpawEngine.movingToolbar.isMouseMoving = false;
        SpawEngine.movingToolbar = null;
      }
    }
  }
  if (SpawEngine.movingToolbar != null && SpawEngine.movingToolbar.isToolbarMoving)
  {
    document.getElementById(SpawEngine.movingToolbar.name + '_toolbox').style.left = SpawEngine.movingToolbar.currentToolbarX + event.clientX - SpawEngine.movingToolbar.lastMousePosX + "px";   
    document.getElementById(SpawEngine.movingToolbar.name + '_toolbox').style.top = SpawEngine.movingToolbar.currentToolbarY + event.clientY - SpawEngine.movingToolbar.lastMousePosY + "px";   
    SpawEngine.movingToolbar.currentToolbarX = document.getElementById(SpawEngine.movingToolbar.name + '_toolbox').offsetLeft;
    SpawEngine.movingToolbar.currentToolbarY = document.getElementById(SpawEngine.movingToolbar.name + '_toolbox').offsetTop;
    SpawEngine.movingToolbar.lastMousePosX = event.clientX;
    SpawEngine.movingToolbar.lastMousePosY = event.clientY;
    SpawEngine.getActiveEditor().saveFloatingToolbarPosition(SpawEngine.movingToolbar.currentToolbarX, SpawEngine.movingToolbar.currentToolbarY); 
  }
  if (SpawEngine.resizingEditor != null && SpawEngine.resizingEditor.isResizing)
  {
    if (SpawEngine.resizingEditor.isHorizontalResizingAllowed() && !event.ctrlKey) // vertical resizing only with ctrl pressed
    {
      var encwidth = document.getElementById(SpawEngine.resizingEditor.name + "_enclosure").offsetWidth;
      var w_delta = event.clientX - SpawEngine.resizingEditor.lastMousePosX;
      var resobj = SpawEngine.resizingEditor.getPageInput(SpawEngine.resizingEditor.getActivePage().name);
      if (!SpawEngine.resizingEditor.isInDesignMode()) // resize textarea
      {
        resobj.style.width = resobj.offsetWidth + w_delta + "px";
      }
      document.getElementById(SpawEngine.resizingEditor.name + "_enclosure").style.width = encwidth + w_delta + "px";
      if (!SpawEngine.resizingEditor.isInDesignMode() 
        && (document.getElementById(SpawEngine.resizingEditor.name + "_enclosure").offsetWidth - w_delta) > (encwidth)) // resize textarea
      {
        // fix resizing
        //resobj.style.width = resobj.offsetWidth - w_delta + (document.getElementById(SpawEngine.resizingEditor.name + "_enclosure").offsetWidth - encwidth) + "px";
      }
      SpawEngine.resizingEditor.lastMousePosX = event.clientX;
    }
    if (SpawEngine.resizingEditor.isVerticalResizingAllowed() && !event.shiftKey) // horizontal resizing only with shift pressed
    {
      //document.getElementById(SpawEngine.resizingEditor.name + "_enclosure").style.height = document.getElementById(SpawEngine.resizingEditor.name + "_enclosure").offsetHeight + event.clientY - SpawEngine.resizingEditor.lastMousePosY + "px";
      var resobj;
      if (SpawEngine.resizingEditor.isInDesignMode())
        resobj = SpawEngine.resizingEditor.getPageIframeObject(SpawEngine.resizingEditor.getActivePage().name);
      else
        resobj = SpawEngine.resizingEditor.getPageInput(SpawEngine.resizingEditor.getActivePage().name);
      resobj.style.height = resobj.offsetHeight + event.clientY - SpawEngine.resizingEditor.lastMousePosY + "px";
      document.getElementById(SpawEngine.resizingEditor.name+'_enclosure').style.height = resobj.style.height; 
      SpawEngine.resizingEditor.lastMousePosY = event.clientY;
    }
  }
}
SpawEngine.addBrowserEventHandler(document, "mousemove", SpawEngine.mouseMove);
SpawEngine.mouseUp = function(event)
{
  if (SpawEngine.resizingEditor != null)
  {
    SpawEngine.resizingEditor.isResizing = false;
    //SpawEngine.resizingEditor.showPage(SpawEngine.resizingEditor.getActivePage());
    SpawEngine.resizingEditor.finalizeResizing();
    SpawEngine.resizingEditor = null;
  }
  if (SpawEngine.movingToolbar != null)
  {
    SpawEngine.movingToolbar.isMouseMoving = false;
    SpawEngine.movingToolbar = null;
  }
}
SpawEngine.addBrowserEventHandler(document, "mouseup", SpawEngine.mouseUp);
SpawEngine.resizingEditor;
SpawEngine.movingToolbar;

// context menu
SpawEngine.active_context_menu;

// opens standard dialog
SpawEngine.openDialog = function(module, dialog, editor, arguments, querystring, callback, tbi, sender)
{
  var posX = screen.availWidth/2 - 275;
  var posY = screen.availHeight/2 - 250;
  var durl = SpawEngine.spaw_dir + 'dialogs/dialog.php?module=' + module + '&dialog=' + dialog 
    + '&theme=' + editor.theme.prefix + '&lang=' + editor.getLang() 
    + '&charset=' + editor.getOutputCharset() 
    + '&scid=' + editor.scid + "&" + querystring + editor.getRequestUriConfigValue();
  
  var args = new Object();
  args.editor = editor;
  args.arguments = arguments;
  args.callback = callback;
  args.tbi = tbi;
  args.sender = sender;
  var wnd = window.open(durl, module + '_' + dialog, 'status=no,resizable=yes,width=350,height=250,left='+posX+',top='+posY);
  window.dialogArguments = args;
  wnd.focus();   
  return wnd;
}

// replaces special characters with HTML entities
SpawEditor.prototype.convertToEntities = function(src_string)
{
  var result = src_string;
  
  var entities = {
    // Latin-1
    "¡":"&iexcl;",
    "¢":"&cent;",
    "£":"&pound;",
    "¤":"&curren;",
    "¥":"&yen;",
    "¦":"&brvbar;",
    "§":"&sect;",
    "¨":"&uml;",
    "©":"&copy;",
    "ª":"&ordf;",
    "«":"&laquo;",
    "¬":"&not;",
    "­":"&shy;",
    "®":"&reg;",
    "¯":"&macr;",
    "°":"&deg;",
    "±":"&plusmn;",
    "²":"&sup2;",
    "³":"&sup3;",
    "´":"&acute;",
    "µ":"&micro;",
    "¶":"&para;",
    "·":"&middot;",
    "¸":"&cedil;",
    "¹":"&sup1;",
    "º":"&ordm;",
    "»":"&raquo;",
    "¼":"&frac14;",
    "½":"&frac12;",
    "¾":"&frac34;",
    "¿":"&iquest;",
    "À":"&Agrave;",
    "Á":"&Aacute;",
    "Â":"&Acirc;",
    "Ã":"&Atilde;",
    "Ä":"&Auml;",
    "Å":"&Aring;",
    "Æ":"&AElig;",
    "Ç":"&Ccedil;",
    "È":"&Egrave;",
    "É":"&Eacute;",
    "Ê":"&Ecirc;",
    "Ë":"&Euml;",
    "Ì":"&Igrave;",
    "Í":"&Iacute;",
    "Î":"&Icirc;",
    "Ï":"&Iuml;",
    "Ð":"&ETH;",
    "Ñ":"&Ntilde;",
    "Ò":"&Ograve;",
    "Ó":"&Oacute;",
    "Ô":"&Ocirc;",
    "Õ":"&Otilde;",
    "Ö":"&Ouml;",
    "×":"&times;",
    "Ø":"&Oslash;",
    "Ù":"&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;",
    // symbols and greek
    "ƒ":"&fnof;",
    "Α":"&Alpha;",
    "Β":"&Beta;",
    "γ":"&Gamma;",
    "Δ":"&Delta;",
    "Ε":"&Epsilon;",
    "Ζ":"&Zeta;",
    "Η":"&Eta;",
    "Θ":"&Theta;",
    "Ι":"&Iota;",
    "Κ":"&Kappa;",
    "Λ":"&Lambda;",
    "Μ":"&Mu;",
    "Ν":"&Nu;",
    "Ξ":"&Xi;",
    "Ο":"&Omicron;",
    "Π":"&Pi;",
    "Ρ":"&Rho;",
    "Σ":"&Sigma;",
    "Τ":"&Tau;",
    "Υ":"&Upsilon;",
    "Φ":"&Phi;",
    "Χ":"&Chi;",
    "Ψ":"&Psi;",
    "Ω":"&Omega;",
    "α":"&alpha;",
    "β":"&beta;",
    "γ":"&gamma;",
    "δ":"&delta;",
    "ε":"&epsilon;",
    "ζ":"&zeta;",
    "η":"&eta;",
    "θ":"&theta;",
    "ι":"&iota;",
    "κ":"&kappa;",
    "λ":"&lambda;",
    "μ":"&mu;",
    "ν":"&nu;",
    "ξ":"&xi;",
    "ο":"&omicron;",
    "π":"&pi;",
    "ρ":"&rho;",
    "ς":"&sigmaf;",
    "σ":"&sigma;",
    "τ":"&tau;",
    "υ":"&upsilon;",
    "φ":"&phi;",
    "χ":"&chi;",
    "ψ":"&psi;",
    "ω":"&omega;",
    "•":"&bull;",
    "…":"&hellip;",
    "′":"&prime;",
    "″":"&Prime;",
    "‾":"&oline;",
    "⁄":"&frasl;",
    "℘":"&weierp;",
    "ℑ":"&image;",
    "ℜ":"&real;",
    "™":"&trade;",
    "ℵ":"&alefsym;",
    "←":"&larr;",
    "↑":"&uarr;",
    "→":"&rarr;",
    "↓":"&darr;",
    "↔":"&harr;",
    "↵":"&crarr;",
    "⇐":"&lArr;",
    "⇑":"&uArr;",
    "⇒":"&rArr;",
    "⇔":"&hArr;",
    "∀":"&forall;",
    "∂":"&part;",
    "∃":"&exist;",
    "∅":"&empty;",
    "∇":"&nabla;",
    "∈":"&isin;",
    "∉":"&notin;",
    "∋":"&ni;",
    "∏":"&prod;",
    "∑":"&sum;",
    "−":"&minus;",
    "∗":"&lowast;",
    "√":"&radic;",
    "∝":"&prop;",
    "∞":"&infin;",
    "∧":"&and;",
    "∨":"&or;",
    "∩":"&cap;",
    "∪":"&cup;",
    "∫":"&int;",
    "≅":"&cong;",
    "≈":"&asymp;",
    "≠":"&ne;",
    "≡":"&equiv;",
    "≤":"&le;",
    "≥":"&ge;",
    "⊂":"&sub;",
    "⊃":"&sup;",
    "⊄":"&nsub;",
    "⊆":"&sube;",
    "⊇":"&supe;",
    "⊕":"&oplus;",
    "⊗":"&otimes;",
    "⊥":"&perp;",
    "⋅":"&sdot;",
    "⌈":"&lceil;",
    "⌉":"&rceil;",
    "⌊":"&lfloor;",
    "⌋":"&rfloor;",
    "〈":"&lang;",
    "〉":"&rang;",
    "◊":"&loz;",
    "♠":"&spades;",
    "♣":"&clubs;",
    "♥":"&hearts;",
    "♦":"&diams;",
    // special chars
    "Œ":"&OElig;",
    "œ":"&oelig;",
    "Š":"&Scaron;",
    "š":"&scaron;",
    "Ÿ":"&Yuml;",
    "ˆ":"&circ;",
    "˜":"&tilde;",
    " ":"&ensp;",
    " ":"&emsp;",
    " ":"&thinsp;",
    "‌":"&zwnj;",
    "‍":"&zwj;",
    "‎":"&lrm;",
    "‏":"&rlm;",
    "–":"&ndash;",
    "—":"&mdash;",
    "‘":"&lsquo;",
    "’":"&rsquo;",
    "‚":"&sbquo;",
    "„":"&bdquo;",
    "†":"&dagger;",
    "‡":"&Dagger;",
    "‰":"&permil;",
    "‹":"&lsaquo;",
    "›":"&rsaquo;",
    "€":"&euro;",
    "“":"&ldquo;",
    "”":"&rdquo;"
  }
  var entities_str = "¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿƒΑΒγΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρςστυφχψω•…′″‾⁄℘ℑℜ™ℵ←↑→↓↔↵⇐⇑⇒⇔∀∂∃∅∇∈∉∋∏∑−∗√∝∞∧∨∩∪∫≅≈≠≡≤≥⊂⊃⊄⊆⊇⊕⊗⊥⋅⌈⌉⌊⌋〈〉◊♠♣♥♦ŒœŠšŸˆ˜   ‌‍‎‏–—‘’‚“”„†‡‰‹›€";

  var rgx = new RegExp("[" + entities_str + "]", "gm");
  
  var matches = result.match(rgx);
  if (matches != null)
  {
    var processed = new Array();
    
    for (var i=0; i<matches.length; i++)
    {
      if (processed[matches[i]] == null && entities[matches[i]] != null && entities[matches[i]] != undefined)
      {
        // register that the symbol was processed
        processed[matches[i]] = entities[matches[i]];
        var replace_rgx = new RegExp(matches[i],"gm");
        result = result.replace(replace_rgx, entities[matches[i]]);
      }
    }    
  }
  
  return result;   
}// utility functions
function SpawUtils()
{
}
SpawUtils.ltrim = function(txt)
{
  var spacers = " \t\r\n";
  while (txt.length>0 && spacers.indexOf(txt.charAt(0)) != -1)
  {
    txt = txt.substr(1);
  }
  return(txt);
}
SpawUtils.rtrim = function(txt)
{
  var spacers = " \t\r\n";
  while (txt.length>0 && spacers.indexOf(txt.charAt(txt.length-1)) != -1)
  {
    txt = txt.substr(0,txt.length-1);
  }
  return(txt);
}
SpawUtils.trim = function(txt)
{
  return(SpawUtils.ltrim(SpawUtils.rtrim(txt)));
}
SpawUtils.trimLineBreaks = function(txt)
{
  var spacers = "\r\n";
  while (txt.length>0 && spacers.indexOf(txt.charAt(txt.length-1)) != -1)
  {
    txt = txt.substr(0,txt.length-1);
  }
  while (txt.length>0 && spacers.indexOf(txt.charAt(0)) != -1)
  {
    txt = txt.substr(1);
  }
  return(txt);  
}
SpawUtils.htmlEncode = function(txt)
{
  return txt.replace(/&/gm,'&amp;').replace(/</gm,'&lt;').replace(/>/gm, '&gt;').replace(/\u00A0/g, "&nbsp;");
}
SpawUtils.getPageOffsetLeft = function(obj)
{
    var elm = obj;
    x = obj.offsetLeft;
    while ((elm = elm.offsetParent) != null)
    {
      x += elm.offsetLeft;
    }
    return x;
}
SpawUtils.getPageOffsetTop = function(obj)
{
    var elm = obj;
    y = obj.offsetTop;
    while ((elm = elm.offsetParent) != null)
    {
      y += elm.offsetTop;
    }
    return y;
}
<br />
<b>Warning</b>:  Cannot modify header information - headers already sent by (output started at /usr/local/home/httpd/vhtdocs/applebybowers.com/cms/spaw2/js/common/toolbar.js:19) in <b>/usr/local/home/httpd/vhtdocs/applebybowers.com/cms/spaw2/js/spaw.js.php</b> on line <b>11</b><br />
// Toolbar item
function SpawTbItem(module, name, id)
{
  this.module = module;
  this.name = name;
  this.id = id;
  this.is_enabled = true;
}
// module (plugin)
SpawTbItem.prototype.module;
// command name
SpawTbItem.prototype.name;
// control id
SpawTbItem.prototype.id;
// belongs to editor
SpawTbItem.prototype.editor;
// belongs to toolbar
SpawTbItem.prototype.toolbar_name;
// is toolbar item enabled?
SpawTbItem.prototype.is_enabled;

// Toolbar image
function SpawTbImage(module, name, id)
{
  this.constructor(module, name, id);
}
SpawTbImage.prototype = new SpawTbItem;

// Toolbar button
function SpawTbButton(module, name, id, on_enabled_check, on_pushed_check, on_click, base_image_url, show_in_context_menu)
{
  this.constructor(module, name, id);
  this.on_enabled_check = on_enabled_check;
  this.on_pushed_check = on_pushed_check;
  this.on_click = on_click;  
  this.base_image_url = base_image_url;
  if (show_in_context_menu)
    this.show_in_context_menu = show_in_context_menu;
  else
    this.show_in_context_menu = false; 
}
SpawTbButton.prototype = new SpawTbItem;
SpawTbButton.prototype.on_enabled_check;
SpawTbButton.prototype.on_pushed_check;
SpawTbButton.prototype.on_click;
SpawTbButton.prototype.is_pushed = false;
SpawTbButton.prototype.show_in_context_menu = false;

// images
SpawTbButton.prototype.base_image_url;
SpawTbButton.prototype.image;
SpawTbButton.prototype.image_over;
SpawTbButton.prototype.image_down;
SpawTbButton.prototype.image_off;

// Toolbar dropdown
function SpawTbDropdown(module, name, id, on_enabled_check, on_status_check, on_change)
{
  this.constructor(module, name, id);
  this.on_enabled_check = on_enabled_check;
  this.on_status_check = on_status_check;
  this.on_change = on_change;  
}
SpawTbDropdown.prototype = new SpawTbItem;
SpawTbDropdown.prototype.on_enabled_check;
SpawTbDropdown.prototype.on_status_check;
SpawTbDropdown.prototype.on_change;
// Editor class
function SpawEditor(name)
{
  this.name = name;
  this.toolbar_items = new Array();
  this.pages = new Array();
  this.tabs = new Array();
  this.controlled_editors = new Array();
  //this.event_handlers = new Array();
  this.config = new Array();
}

SpawEditor.prototype.name;

// secure config id
SpawEditor.prototype.scid;

// stylesheet
SpawEditor.prototype.stylesheet;

// config
SpawEditor.prototype.config;
SpawEditor.prototype.getConfigValue = function(name)
{
  return this.config[name];
}
SpawEditor.prototype.setConfigValue = function(name, value)
{
  this.config[name] = value;
}
SpawEditor.prototype.getRequestUriConfigValue = function()
{
  return this.getConfigValue("__request_uri");
}
// returns true if all pages are initialized
SpawEditor.prototype.isInitialized = function()
{
  var result = true;
  for (var i=0; i<this.pages.length; i++)
  {
    if (!this.pages[i].initialized)
    {
      result = false;
      break;
    }
  }
  return result;
}

// toolbars
SpawEditor.prototype.toolbar_items;
SpawEditor.prototype.addToolbarItem = function(tbi, toolbar_name)
{
  if (tbi.base_image_url)
    this.theme.preloadImages(tbi);
  tbi.editor = this;
  tbi.toolbar_name = toolbar_name;
  this.toolbar_items.push(tbi);
}
SpawEditor.prototype.getToolbarItem = function(id)
{
  for (var i=0; i<this.toolbar_items.length; i++)
  {
    if (this.toolbar_items[i].id == id)
    {
      return this.toolbar_items[i];
    }
  }
  return null;
}

// enable editing mode button
SpawEditor.prototype.enableEditingMode = function(tbi)
{
  if (tbi && tbi.editor.getActivePage().editing_mode_tbi && tbi.editor.getActivePage().editing_mode_tbi != null)
  {
    var mbt = this.document.getElementById(tbi.editor.getActivePage().editing_mode_tbi.id);
    mbt.disabled = false;
    tbi.editor.theme.buttonOut(tbi.editor.getActivePage().editing_mode_tbi, mbt);
  }
}
// disable editing mode button
SpawEditor.prototype.disableEditingMode = function(tbi)
{
  if (tbi && tbi.editor.getActivePage().editing_mode_tbi && tbi.editor.getActivePage().editing_mode_tbi != null)
  {
    var mbt = this.document.getElementById(tbi.editor.getActivePage().editing_mode_tbi.id);
    mbt.disabled = true;
    tbi.editor.theme.buttonOff(tbi.editor.getActivePage().editing_mode_tbi, mbt);
  }
}


// pages
SpawEditor.prototype.pages;
SpawEditor.prototype.addPage = function(page)
{
  this.pages.push(page);
  this.addTab(page);
}
SpawEditor.prototype.getPage = function(id)
{
  for(var i=0; i<this.pages.length; i++)
  {
    if (this.pages[i].name == id)
      return this.pages[i];
  }
  return null;
}
SpawEditor.prototype.active_page;
SpawEditor.prototype.setActivePage = function(id)
{
  if (this.active_page && (this.active_page.name != id || SpawEngine.active_editor != this))
  {
    // raise before page switch event
    SpawEngine.handleEvent("spawbeforepageswitch", null, null, this.name);
  
    this.getTab(this.active_page.name).setInactive(); 
    this.hidePage(this.active_page);
    this.enableEditingMode(this.active_page.editing_mode_tbi);
    this.active_page = this.getPage(id);
    this.getTab(this.active_page.name).setActive();
    this.showPage(this.active_page);
    this.disableEditingMode(this.active_page.editing_mode_tbi);
    SpawEngine.setActiveEditor(this);

    // raise page switch event
    SpawEngine.handleEvent("spawpageswitch", null, null, this.name);

    setTimeout(this.name + '_obj.updateToolbar();', 10); // firefox crashes if called imediatly
  }
}
SpawEditor.prototype.getActivePage = function()
{
  return this.active_page;
}

// parent object of the editing area
SpawEditor.prototype.pageOffsetParent;
SpawEditor.prototype.hidePage = function(page)
{
  var pta = this.getPageInput(page.name);
  var pif = this.getPageIframeObject(page.name);
  pta.style.display = 'none';
  pif.style.display = 'none';
}
SpawEditor.prototype.showPage = function(page)
{
  var pta = this.getPageInput(page.name);
  var pif = this.getPageIframeObject(page.name);
  var pdoc = this.getPageDoc(page.name);
  if (page.editing_mode == 'design')
  {
    pta.style.display = 'none';
    pif.style.display = 'inline';
    pdoc.designMode = 'on'; // mozilla (and probably early firefox versions) looses this when the page is hidden
  }
  else
  {
    pta.style.width = pif.offsetWidth + 'px'; 
    pta.style.display = 'inline';
    pif.style.display = 'none';
  }
  this.focus();
}


// tabs
SpawEditor.prototype.tabs;
SpawEditor.prototype.addTab = function(page)
{
  this.tabs.push(new SpawTab(page));
}
SpawEditor.prototype.getTab = function(page_name)
{
  for (var i=0; i<this.tabs.length; i++)
  {
    if (this.tabs[i].page.name == page_name)
      return this.tabs[i];
  }
  return null;
}

SpawEditor.prototype.floating_mode = false;
// controlled editors (for floating mode master)
SpawEditor.prototype.controlled_editors;
SpawEditor.prototype.addControlledEditor = function(editor)
{
  this.controlled_editors.push(editor);
}
SpawEditor.prototype.isControlledEditor = function(name)
{
  for (var i=0; i<this.controlled_editors.length; i++)
  {
    if (this.controlled_editors[i].name == name)
      return true;
  }
  return false;
}

// controlled by (for floating mode slave)
SpawEditor.prototype.controlled_by;

// returns currently active editor (might need more complex logic later)
SpawEditor.prototype.getCurrentEditor = function()
{
  return SpawEngine.getActiveEditor();
}

// returns which editor is currently controlled by this editors toolbar
SpawEditor.prototype.getTargetEditor = function()
{
  if (this.controlled_by == this && this.controlled_editors.length <= 1)
    return this;
  else
    return SpawEngine.getActiveEditor();
}

// theme
SpawEditor.prototype.theme;
SpawEditor.prototype.setTheme = function(theme)
{
  this.theme = theme;
}
SpawEditor.prototype.getTheme = function()
{
  return this.theme;
}

// language
SpawEditor.prototype.lang;
SpawEditor.prototype.setLang = function(lang)
{
  this.lang = lang;
}
SpawEditor.prototype.getLang = function()
{
  return this.lang;
}
SpawEditor.prototype.output_charset;
SpawEditor.prototype.setOutputCharset = function(output_charset)
{
  this.output_charset = output_charset;
}
SpawEditor.prototype.getOutputCharset = function()
{
  return this.output_charset;
}

// hooks up to window onload event and calls initialization
SpawEditor.prototype.onLoadHookup = function()
{
  var spaw_tmpstr="";
  if (window.onload != null) 
  {
    spaw_tmpstr = window.onload.toString();
    var spaw_i = spaw_tmpstr.indexOf("{") + 2;
    spaw_tmpstr = spaw_tmpstr.substr(spaw_i,spaw_tmpstr.length-spaw_i-2);
  }
  window.onload = new Function(this.name+'_obj.initialize();'+spaw_tmpstr);   
}

// returns reference to page textarea
SpawEditor.prototype.getPageInput = function(page_name)
{
  return this.document.getElementById(page_name);
}

// returns reference to outer iframe object (differs from getPageIfram in IE only)
SpawEditor.prototype.getPageIframeObject = function(page_name)
{
  return this.document.getElementById(page_name+'_rEdit');
}

// returns reference to active page's doc
SpawEditor.prototype.getActivePageDoc = function()
{
  return this.getPageDoc(this.active_page.name);
}

// floating toolbar control
SpawEditor.prototype.currentToolbarX;
SpawEditor.prototype.currentToolbarY;
SpawEditor.prototype.lastMousePosX;
SpawEditor.prototype.lastMousePosY;
SpawEditor.prototype.isToolbarMoving = false;
SpawEditor.prototype.floatingMouseDown = function(event)
{
  this.currentToolbarX = this.document.getElementById(this.name + '_toolbox').offsetLeft;
  this.currentToolbarY = this.document.getElementById(this.name + '_toolbox').offsetTop;
  this.lastMousePosX = event.clientX;
  this.lastMousePosY = event.clientY;
  this.isToolbarMoving = true;
  SpawEngine.movingToolbar = this;
}


// floating toolbar position relative to this instance
SpawEditor.prototype.floatingToolbarX = 100;
SpawEditor.prototype.floatingToolbarY = 10;
SpawEditor.prototype.positionFloatingToolbar = function()
{
  this.document.getElementById(this.controlled_by.name + '_toolbox').style.left = this.getPageOffsetLeft() + this.floatingToolbarX + "px";   
  this.document.getElementById(this.controlled_by.name + '_toolbox').style.top = this.getPageOffsetTop() + this.floatingToolbarY + "px";   
}
SpawEditor.prototype.saveFloatingToolbarPosition = function(x, y)
{
  this.floatingToolbarX = x - this.getPageOffsetLeft();
  this.floatingToolbarY = y - this.getPageOffsetTop();
}


// page offsets
SpawEditor.prototype.getPageOffsetLeft = function()
{
  var elm = this.getPageIframeObject(this.active_page?this.active_page.name:this.name);
  var res = elm.offsetLeft;
  
  while ((elm = elm.offsetParent) != null)
  {
    res += elm.offsetLeft;
  }
  return res;
}

SpawEditor.prototype.getPageOffsetTop = function()
{
  var elm = this.getPageIframeObject(this.active_page?this.active_page.name:this.name);
  var res = elm.offsetTop;
  
  while ((elm = elm.offsetParent) != null)
  {
    res += elm.offsetTop;
  }
  return res;
}

// resizing control
SpawEditor.prototype.isResizing = false;
SpawEditor.prototype.isVerticalResizingAllowed = function()
{
  var res = this.getConfigValue("resizing_directions");
  res = res?res.toLowerCase():res;
  if (res == 'vertical' || res == 'both')
    return true;
  else
    return false;
}
SpawEditor.prototype.isHorizontalResizingAllowed = function()
{
  var res = this.getConfigValue("resizing_directions");
  res = res?res.toLowerCase():res;
  if (res == 'horizontal' || res == 'both')
    return true;
  else
    return false;
}
SpawEditor.prototype.resizingGripMouseDown = function(event)
{
  this.lastMousePosX = event.clientX;
  this.lastMousePosY = event.clientY;
  this.isResizing = true;
  SpawEngine.resizingEditor = this;

  //SpawEngine.resizingEditor.hidePage(SpawEngine.resizingEditor.getActivePage());
  // prevent gecko from dragging image
  if (event.preventDefault)
    event.preventDefault();
}
SpawEditor.prototype.finalizeResizing = function()
{
  var resobj = this.isInDesignMode()?this.getPageIframeObject(this.getActivePage().name):this.getPageInput(this.getActivePage().name);

  for (var i=0; i<this.pages.length; i++)
  {
    var pif = this.getPageIframeObject(this.pages[i].name);
    var pta = this.getPageInput(this.pages[i].name);
    pif.style.height = resobj.offsetHeight + 'px';    
    pta.style.height = resobj.offsetHeight + 'px';    
    pta.style.width = resobj.offsetWidth + 'px';
  }
} 

// active toolbar control
SpawEditor.prototype.updateToolbar = function()
{
  if (this.controlled_by != this)
    this.updateEditorToolbar(this.controlled_by);
  this.updateEditorToolbar(this);
}
SpawEditor.prototype.updateEditorToolbar = function(ed)
{
  for(var i=0; i<ed.toolbar_items.length; i++)
  {
    // check if item is enabled
    if (ed.toolbar_items[i].on_enabled_check && ed.toolbar_items[i].on_enabled_check != '')
    {
      if(eval("SpawPG"+ed.toolbar_items[i].module 
              + '.' + ed.toolbar_items[i].on_enabled_check + '(this, ed.toolbar_items[i])'))
      {
        this.document.getElementById(ed.toolbar_items[i].id).disabled = false;
        ed.toolbar_items[i].is_enabled = true;
        if (ed.toolbar_items[i].on_click)
        { 
          // button     
          ed.theme.buttonOut(ed.toolbar_items[i], this.document.getElementById(ed.toolbar_items[i].id));
        }
        else
        {
          // dropdown
          ed.theme.dropdownOut(ed.toolbar_items[i], this.document.getElementById(ed.toolbar_items[i].id));
        }
        
        // check if button is pushed
        if (ed.toolbar_items[i].on_pushed_check && ed.toolbar_items[i].on_pushed_check != '')
        {
          if (eval("SpawPG"+ed.toolbar_items[i].module 
                  + '.' + ed.toolbar_items[i].on_pushed_check + '(this, ed.toolbar_items[i])'))
          {
            ed.toolbar_items[i].is_pushed = true;
            ed.theme.buttonDown(ed.toolbar_items[i], this.document.getElementById(ed.toolbar_items[i].id));
          }
          else
          {
            ed.toolbar_items[i].is_pushed = false;
            ed.theme.buttonOut(ed.toolbar_items[i], this.document.getElementById(ed.toolbar_items[i].id));
          }
        } 
        // update drowpdown value
        if (ed.toolbar_items[i].on_status_check && ed.toolbar_items[i].on_status_check != '')
        {
          var val = eval("SpawPG"+ed.toolbar_items[i].module 
                        + '.' + ed.toolbar_items[i].on_status_check + '(this, ed.toolbar_items[i])');
          var dd = this.document.getElementById(ed.toolbar_items[i].id);
          if (val)
          {
            for(var oi=0; oi<dd.options.length; oi++)
            {
              if (dd.options[oi].value == val)
                dd.options[oi].selected = true;
            }
          }
          else
          {
            dd.selectedIndex = 0;
          }
        }
        
      }
      else
      {
        this.document.getElementById(ed.toolbar_items[i].id).disabled = true;
        ed.toolbar_items[i].is_enabled = false;
        if (ed.toolbar_items[i].on_click)
        { 
          // button     
          ed.theme.buttonOff(ed.toolbar_items[i], this.document.getElementById(ed.toolbar_items[i].id));
        }
        else
        {
          // dropdown
          ed.theme.dropdownOff(ed.toolbar_items[i], this.document.getElementById(ed.toolbar_items[i].id));
        }
      }
    }
  }
}

// editor content
// updates code editor content from wysiwyg editor
SpawEditor.prototype.updatePageInput = function(page)
{
  var pdoc = this.getPageDoc(page.name);
  var pta = this.getPageInput(page.name);
  pta.value = this.getPageHtml(page); 
}
// updates wysiwyg editor content from code editor
SpawEditor.prototype.updatePageDoc = function(page)
{
  var pdoc = this.getPageDoc(page.name);
  var pta = this.getPageInput(page.name);
  pdoc.body.innerHTML = pta.value;
  this.flash2img();
}
// returns html of the current page
SpawEditor.prototype.getPageHtml = function(page)
{
  // raise get html event
  SpawEngine.handleEvent("spawgethtml", null, "page_doc", this.name);

  var pdoc = this.getPageDoc(page.name);
  var pta = this.getPageInput(page.name);
  var result;
  if (page.editing_mode == "html")
  {
    // html mode
    result = pta.value;
  }
  else if(page.editing_mode == "design")
  {
    // remove glyphs
    this.removeGlyphs(pdoc.body);
    // replace flash placeholders
    this.img2flash();
    // strip absolute urls
    this.stripAbsoluteUrls();
    // wysiwyg mode
    if (this.getConfigValue("rendering_mode") == "builtin")
    {
      // let browser handle html rendering
      result = pdoc.body.innerHTML;
    }
    else
    {
      // call custom html renderer
      result = this.dom2xml(pdoc.body, '');
    }
  }
  if (this.getConfigValue('convert_html_entities'))
    result = this.convertToEntities(result);

  return result;
} 
SpawEditor.updateFields = function(editor, event)
{
  editor.updateFields();
}
SpawEditor.prototype.updateFields = function()
{
  for(var i=0; i<this.pages.length; i++)
  {
    this.updatePageInput(this.pages[i]);
  }
}
SpawEditor.prototype.spawSubmit = function()
{
  SpawEngine.updateFields();
  var frm = this.getPageInput(this.pages[0].name).form;
  document.forms[0].setAttribute("__spawsubmiting","true");
  frm.formSubmit();
}

// renders xhtml
SpawEditor.prototype.dom2xml = function(node, indent)
{
    var xbuf = '';
    var f_indent = '';
    var e_indent = '';
    var f_crlf = '';
    var e_crlf = '';
    for (var i=0; i<node.childNodes.length; i++)
    {
      var chnode = node.childNodes[i];
      if (chnode.nodeType == 3) // text node
      {
        if (SpawUtils.trim(chnode.nodeValue).length > 0)
          xbuf += SpawUtils.trimLineBreaks(SpawUtils.htmlEncode(chnode.nodeValue));
      }
      else if (chnode.nodeType == 8) // comment node
      {
        xbuf += "<!--" + chnode.nodeValue + "-->";
      }
      else if (chnode.nodeType == 1) // html element
      {
        if (chnode.getAttribute("__spawprocessed") == null) // workaround to prevent elements from doubling
        {
          chnode.setAttribute("__spawprocessed",true);
          // form attributes string
          var attr_str = '';
          for(var j=0; j<chnode.attributes.length; j++)
          {
            var attr = chnode.attributes[j];
            if (attr.nodeValue != null 
                && (chnode.getAttribute(attr.nodeName, 2)!=null || chnode.getAttribute(attr.nodeName, 0) != null)
                && attr.specified && attr.nodeName.toLowerCase()!="__spawprocessed" 
                && attr.nodeName.toLowerCase().indexOf("_moz") != 0)
            {
              var attrval = chnode.getAttribute(attr.nodeName, 2);
              if (attrval == null)
                attrval = chnode.getAttribute(attr.nodeName, 0);
              
              if (chnode.tagName.toLowerCase() != 'font')
              {
                if (attr.nodeName.toLowerCase() != 'class' && attr.nodeName.toLowerCase() != 'style'
                   && attr.nodeName.toLowerCase() != 'href' && attr.nodeName.toLowerCase() != 'src' 
                   && attr.nodeName.toLowerCase() != 'shape' && attr.nodeName.toLowerCase() != 'coords' // coords and shape attributes are lost in area tag in IE 
                   && attr.nodeName.toLowerCase() != 'type' && attr.nodeName.toLowerCase() != 'value' && attr.nodeName.toLowerCase() != 'enctype') // type and value get lost on IE in input tags
                  attr_str += ' ' + attr.nodeName.toLowerCase() + '="' + attrval + '"';
              }
              else
              {
                // replace font tag with span
                if (attr.nodeName.toLowerCase() == 'face')
                  chnode.style.fontFamily = attrval;
                else if (attr.nodeName.toLowerCase() == 'size')
                {
                  switch(attrval)
                  {
                    case '1':
                      attrval = 'xx-small';
                      break;
                    case '2':
                      attrval = 'x-small';
                      break;
                    case '3':
                      attrval = 'small';
                      break;
                    case '4':
                      attrval = 'medium';
                      break;
                    case '5':
                      attrval = 'large';
                      break;
                    case '6':
                      attrval = 'x-large';
                      break;
                    case '7':
                      attrval = 'xx-large';
                      break;
                    default:
                      attrval = 'medium';
                      break;
                  }
                  chnode.style.fontSize = attrval;
                } 
                else if (attr.nodeName.toLowerCase() == 'color')
                  chnode.style.color = attrval; 
                  
                // crop attribute string
                attr_str = '';
              }
            }
            
          }
          // style attribute
          if (chnode.style.cssText && chnode.style.cssText != '')
            attr_str += ' style="' + chnode.style.cssText + '"';
        
          // class attribute
          if (chnode.className && chnode.className != '')
            attr_str += ' class="' + chnode.className + '"';

          if (chnode.type && chnode.type != '')
            attr_str += ' type="' + chnode.type + '"';
          if (chnode.value && chnode.value != '' 
            && !(chnode.tagName.toLowerCase() == 'li' && chnode.value == '-1')) // workaround for firefox 
            attr_str += ' value="' + chnode.value + '"';
          if (chnode.enctype && chnode.enctype != '' && chnode.enctype != 'application/x-www-form-urlencoded')
            attr_str += ' enctype="' + chnode.enctype + '"';

          // replace & to &amp; in href and src attributes
          if (chnode.href && chnode.href != '' && chnode.tagName.toLowerCase() != 'img')
            attr_str += ' href="' +  this.getStrippedAbsoluteUrl(chnode.href, false).replace(/&amp;/g,"&").replace(/&/g,"&amp;") + '"';
          if (chnode.src && chnode.src != '')
            attr_str += ' src="' +  this.getStrippedAbsoluteUrl(chnode.src, true).replace(/&amp;/g,"&").replace(/&/g,"&amp;") + '"';
        
          // shape and coords attributes
          if (chnode.coords && chnode.coords != '') 
            attr_str += ' coords="' + chnode.coords + '"'; 
          if (chnode.shape && chnode.shape != '') 
            attr_str += ' shape="' + chnode.shape + '"'; 

          if (this.getConfigValue("beautify_xhtml_output"))
          {
            switch(chnode.tagName.toLowerCase())
            {
              case "p":
              case "td":
              case "th":
              case "label":
              case "li":
              case "br":
                f_indent = indent;
                f_crlf = '\n';
                e_indent = '';
                e_crlf = '';
                break;
              case "table":
              case "thead":
              case "tfoot":
              case "tbody":
              case "tr":
              case "div":
              case "ul":
              case "ol":
                f_indent = indent;
                f_crlf = '\n';
                e_indent = indent;
                e_crlf = '\n';
                break;
              default:
                f_indent = '';
                f_crlf = '';
                e_indent = '';
                e_crlf = '';
            }
          }  
          if (chnode.tagName.toLowerCase() != "script")
          {
            // replace font with span
            var tag_name = (chnode.tagName.toLowerCase() != 'font')?chnode.tagName.toLowerCase():'span';
            if (chnode.childNodes.length>0)
            {
              var innercode = this.dom2xml(chnode, indent + ((f_indent!="tmp")?"  ":""));
              if (SpawUtils.trim(innercode) == '')
                innercode = '&nbsp;';
              xbuf += f_crlf + f_indent + "<" + SpawUtils.trim(tag_name + attr_str) + ">" + innercode + e_crlf + e_indent + "</" + tag_name + ">";
            }
            else if (chnode.tagName.indexOf("/") == -1)// empty tag (sometimes ending tag is passed as a separate node)
            {
              if (tag_name == "img"
                || tag_name == "br"
                || tag_name == "wbr"
                || tag_name == "hr"
                || tag_name == "input")
              {
                xbuf += f_crlf + f_indent + "<" + SpawUtils.trim(tag_name + attr_str) + " />" + e_crlf + e_indent;
              }
              else
              {
                // don't generate empty useless tags
                if (tag_name != "b"
                  && tag_name != "i"
                  && tag_name != "u"
                  && tag_name != "strike"
                  && tag_name != "strong"
                  && tag_name != "em"
                  && tag_name != "i"
                  && tag_name != "span"
                  )
                {
                  var innercode = '';
                  if (tag_name == 'p')
                    innercode = '&nbsp;';
                  xbuf += f_crlf + f_indent + "<" + SpawUtils.trim(tag_name + attr_str) + ">" + innercode + "</" + tag_name + ">";
                }
              }
            }
          }
          else // script
          {
            xbuf += f_crlf + f_indent + "<" + SpawUtils.trim(chnode.tagName.toLowerCase() + attr_str) + ">" + chnode.innerHTML + "</" + chnode.tagName.toLowerCase() + ">";                    
          }
        }
      }
    }
    return xbuf;
}

// cleans up code
SpawEditor.prototype.getCleanCode = function(node, clean_type) // clean_type reserved for future use
{
  var xbuf = '';
  for (var i=0; i<node.childNodes.length; i++)
  {
    var chnode = node.childNodes[i];
    if (chnode.nodeType == 3) // text node
    {
      if (SpawUtils.trim(chnode.nodeValue).length > 0)
        xbuf += chnode.nodeValue.replace(/\u00A0/g, "&nbsp;");
    }
    else if (chnode.nodeType == 8) // comment node
    {
      xbuf += "<!--" + chnode.nodeValue + "-->";
    }
    else if (chnode.nodeType == 1) // html element
    {
      if (chnode.getAttribute("__spawprocessed") == null) // workaround to prevent elements from doubling
      {
        chnode.setAttribute("__spawprocessed",true);
        // form attributes string
        var attr_str = '';
        for(var j=0; j<chnode.attributes.length; j++)
        {
          var attr = chnode.attributes[j];
          if (attr.nodeValue != null && chnode.getAttribute(attr.nodeName, 2)!=null && attr.specified && attr.nodeName.toLowerCase()!="__spawprocessed" && attr.nodeName.toLowerCase().indexOf("_moz") != 0)
          {
            var attrval = chnode.getAttribute(attr.nodeName, 2);
            
            if (attr.nodeName.toLowerCase() != 'class' && attr.nodeName.toLowerCase() != 'style'
              && attr.nodeName.toLowerCase() != 'type' && attr.nodeName.toLowerCase() != 'value' && attr.nodeName.toLowerCase() != 'enctype') // type and value get lost on IE in input tags
              attr_str += ' ' + attr.nodeName.toLowerCase() + '="' + attrval + '"';
          }
          
        }

        if (chnode.type && chnode.type != '')
          attr_str += ' type="' + chnode.type + '"';
        if (chnode.value && chnode.value != '')
          attr_str += ' value="' + chnode.value + '"';
        if (chnode.enctype && chnode.enctype != '' && chnode.enctype != 'application/x-www-form-urlencoded')
          attr_str += ' enctype="' + chnode.enctype + '"';
      
        if (chnode.tagName.toLowerCase() != "script")
        {
          if (chnode.childNodes.length>0)
          {
            if (chnode.tagName.indexOf(":") == -1 && chnode.tagName.toLowerCase() != 'font' && chnode.tagName.toLowerCase() != 'div' && chnode.tagName.toLowerCase() != 'span')
              xbuf += "<" + SpawUtils.trim(chnode.tagName.toLowerCase() + attr_str) + ">" + this.getCleanCode(chnode, clean_type) + "</" + chnode.tagName.toLowerCase() + ">";
            else
              xbuf += this.getCleanCode(chnode, clean_type);
          }
          else if (chnode.tagName.indexOf("/") == -1)// empty tag (sometimes ending tag is passed as a separate node)
          {
            if (chnode.tagName.indexOf(":") == -1 && chnode.tagName.toLowerCase() != 'font' && chnode.tagName.toLowerCase() != 'div' && chnode.tagName.toLowerCase() != 'span')
            {
              if (chnode.tagName.toLowerCase() == "img"
                || chnode.tagName.toLowerCase() == "br"
                || chnode.tagName.toLowerCase() == "wbr"
                || chnode.tagName.toLowerCase() == "hr"
                || chnode.tagName.toLowerCase() == "input")
              {
                xbuf += "<" + SpawUtils.trim(chnode.tagName.toLowerCase() + attr_str) + " />";
              }
              else
              {
                xbuf += "<" + SpawUtils.trim(chnode.tagName.toLowerCase() + attr_str) + "></" + chnode.tagName.toLowerCase() + ">";
              }
            }
          }
        }
        else // script
        {
          xbuf += "<" + SpawUtils.trim(chnode.tagName.toLowerCase() + attr_str) + ">" + chnode.innerHTML + "</" + chnode.tagName.toLowerCase() + ">";                    
        }
      }
    }
  }
  return xbuf;
}
// cleans current page code
SpawEditor.prototype.cleanPageCode = function(clean_type) // clean_type reserved for future use
{
  var pname = this.getActivePage().name;
  var pdoc = this.getActivePageDoc();
  var pta = this.getPageInput(pname);

  pta.value = this.getCleanCode(pdoc.body, clean_type);
  this.updatePageDoc(this.getActivePage());     
}


// status bar
SpawEditor.prototype.showStatus = function(message)
{
  var sb = this.document.getElementById(this.name + '_status');
  if (sb && !document.attachEvent) // disable status bar in IE cause it breaks undo/redo
  {
    sb.innerHTML = message;
  }
}

// right click
SpawEditor.rightClick = function(editor, event)
{
  editor.rightClick(editor, event);
}
SpawEditor.prototype.rightClick = function(editor, event)
{
  if (SpawEngine.active_context_menu != null)
    SpawEngine.active_context_menu.hide();
  var cm = new SpawContextMenu(this);
  if (cm.show(event))
  {
    SpawEngine.active_context_menu = cm;
    if (event.preventDefault)
      event.preventDefault();
    else
      event.returnValue = false;
  }
}
// hide context menu
SpawEditor.hideContextMenu = function(editor, event)
{
  editor.hideContextMenu(editor, event);
}
SpawEditor.prototype.hideContextMenu = function(editor, event)
{
  if (SpawEngine.active_context_menu)
  {
    SpawEngine.active_context_menu.hide();
    SpawEngine.active_context_menu = null;
  }
}

// returns specified element surrounding current selection, returns null if selection is not inside such element 
SpawEditor.prototype.getSelectedElementByTagName = function(tagName)
{
  var result = null;
  var elm = this.getSelectionParent();
  
  while (elm && elm.tagName && elm.tagName.toLowerCase() != tagName.toLowerCase() && elm.tagName.toLowerCase() != 'body')
    elm = elm.parentNode;
    
  if (elm && elm.tagName && elm.tagName.toLowerCase() != 'body')
    result = elm;
    
  return result;
}

// returns all anchors on the active page
SpawEditor.prototype.getAnchors = function()
{
  var anchors = new Array();
  var pdoc = this.getActivePageDoc();
  var links = pdoc.getElementsByTagName("a");
  for (var i=0; i<links.length; i++)
  {
    if (links[i].name && links[i].name != '')
      anchors.push(links[i]); 
  }
  return anchors;
}

// show borders on borderless objects
SpawEditor.prototype.show_glyphs = true;

// returns true if active page is in design mode
SpawEditor.prototype.isInDesignMode = function()
{
  return (this.getActivePage().editing_mode == "design");
}

// removes absolute path portion from url
SpawEditor.prototype.getStrippedAbsoluteUrl = function(url, host_only)
{
  if (this.getConfigValue('strip_absolute_urls'))
  {
    var pdoc = this.getActivePageDoc();

  	var curl = pdoc.location.href;
  	var di = curl.lastIndexOf('/', curl.lastIndexOf('?')!=-1?curl.lastIndexOf('?'):curl.length);
  	var cdir = curl;
  	if (di != -1)
  		cdir = curl.substr(0,di+1);
  	var chost = curl;
  	var hi = curl.indexOf('/',curl.indexOf('://')!=-1?(curl.indexOf('://')+3):curl.length);
  	if (hi != -1)
  		chost = curl.substr(0,hi);
   	if (url.toLowerCase().indexOf(curl.toLowerCase())==0 && !host_only)
  	{
  		url = url.substr(curl.length);
  	}
  	else if (url.toLowerCase().indexOf(cdir.toLowerCase())==0 && !host_only)
  	{
  		url = url.substr(cdir.length);
  	}
  	else if (url.toLowerCase().indexOf(chost.toLowerCase())==0)
  	{
  		url = url.substr(chost.length);
  	}
	}
	return url;
}
SpawEditor.prototype.stripAbsoluteUrls = function()
{
  if (this.getConfigValue('strip_absolute_urls'))
  {
    var pdoc = this.getActivePageDoc();
   
    var links = pdoc.getElementsByTagName("a");
    for(var i=0; i<links.length; i++)
    {
      if (links[i].href && links[i].href != '')
        links[i].href = this.getStrippedAbsoluteUrl(links[i].href, false); 
    } 
    var imgs = pdoc.getElementsByTagName("img");
    for(var i=0; i<imgs.length; i++)
    {
      if (imgs[i].src && imgs[i].src != '')
        imgs[i].src = this.getStrippedAbsoluteUrl(imgs[i].src, true); 
    } 
  }
}
// strips absolute url from single object
SpawEditor.prototype.stripAbsoluteUrl = function(elm)
{
  if (this.getConfigValue('strip_absolute_urls') && elm && elm.nodeType == 1)
  {
    if (elm.tagName.toLowerCase() == 'a' && elm.href && elm.href != "")
    {
      elm.href = this.getStrippedAbsoluteUrl(elm.href, false); 
    }
    else if (elm.tagName.toLowerCase() == 'img' && elm.src && elm.src != "")
    {
      elm.src = this.getStrippedAbsoluteUrl(elm.src, true); 
    }
  }
}

// workaround functions to deal with flash objects
// replaces flash movies with image placeholders
SpawEditor.prototype.flash2img = function()
{
  var pdoc = this.getActivePageDoc();
  var flashs_elm = pdoc.getElementsByTagName("EMBED");

  // create a copy that wont be affected by changes to document
  var flashs = new Array();
  for (var i=0; i<flashs_elm.length; i++)
  {
    flashs[i] = flashs_elm[i];
  }

  for (var i=0; i<flashs.length; i++)
  {
    if (flashs[i].attributes.getNamedItem('src') != null /*&& flashs[i].attributes.getNamedItem('src').nodeValue.indexOf(".swf") != -1*/)
    {
  	  var flash = pdoc.createElement("IMG");
  	  flash.setAttribute('src', SpawEngine.spaw_dir + 'img/spacer100.gif?imgtype=flash&src='+flashs[i].getAttribute("src"));
    	if (flashs[i].style.cssText != '') // save original style
    	{
        flash.setAttribute("__spaw_style", flashs[i].style.cssText);
        if (flashs[i].style.width != '')
          flash.setAttribute('width',flashs[i].style.width);
        if (flashs[i].style.height != '')
          flash.setAttribute('height',flashs[i].style.height);
      }  
      // set attributes
      for(var j=0; j<flashs[i].attributes.length; j++)
      {
        var attr = flashs[i].attributes[j];
        if (attr.nodeValue != null 
            && (flashs[i].getAttribute(attr.nodeName, 2)!=null || flashs[i].getAttribute(attr.nodeName, 0) != null)
            && attr.specified 
            && attr.nodeName.toLowerCase().indexOf("_moz") != 0
            && attr.nodeName.toLowerCase() != "src")
        {
          var attrval = flashs[i].getAttribute(attr.nodeName, 2);
          if (attrval == null)
            attrval = flashs[i].getAttribute(attr.nodeName, 0);
          flash.setAttribute(attr.nodeName.toLowerCase(), attrval);
        }
      }
  	  flash.style.cssText = "border: 1px solid #000000; background: url(" + SpawEngine.spaw_dir + "img/flash.gif);";

      flashs[i].parentNode.replaceChild(flash, flashs[i]);
    }
  }
}
// replaces image placeholders for flash movies into embed tags
SpawEditor.prototype.img2flash = function()
{
  var pdoc = this.getActivePageDoc();
  var imgs_elm = pdoc.getElementsByTagName("IMG");

  // create a copy that wont be affected by changes to document
  var imgs = new Array();
  for (var i=0; i<imgs_elm.length; i++)
  {
    imgs[i] = imgs_elm[i];
  }
  
  
  for (var i=0; i<imgs.length; i++)
  {
    if (imgs[i].src.indexOf("spacer100.gif?imgtype=flash") != -1)
    {
      var flash = pdoc.createElement('EMBED');
  	  flash.setAttribute('type','application/x-shockwave-flash');
  	  flash.setAttribute('src',imgs[i].src.substring(imgs[i].src.indexOf("src=")+4));

      // set attributes
      for(var j=0; j<imgs[i].attributes.length; j++)
      {
        var attr = imgs[i].attributes[j];
        if (attr.nodeValue != null 
            && (imgs[i].getAttribute(attr.nodeName, 2)!=null || imgs[i].getAttribute(attr.nodeName, 0) != null)
            && attr.specified 
            && attr.nodeName.toLowerCase().indexOf("_moz") != 0
            && attr.nodeName.toLowerCase() != "src"
            && attr.nodeName.toLowerCase() != "type"
            && attr.nodeName.toLowerCase() != "style")
        {
          var attrval = imgs[i].getAttribute(attr.nodeName, 2);
          if (attrval == null)
            attrval = imgs[i].getAttribute(attr.nodeName, 0);
          flash.setAttribute(attr.nodeName.toLowerCase(), attrval);
        }
      }
    	if (imgs[i].getAttribute("__spaw_style", 2) != null) // restore original style
    	{
        flash.style.cssText = imgs[i].getAttribute("__spaw_style", 2);
        flash.removeAttribute("__spaw_style");
      }  
        	   
  	  imgs[i].parentNode.replaceChild(flash, imgs[i]);
    }
  }
}

// context
SpawEditor.prototype.current_context;
SpawEditor.checkContext = function(editor, event)
{
  editor.checkContext(editor, event);
}
SpawEditor.prototype.checkContext = function(editor, event)
{
  if (SpawEngine.getActiveEditor() != this)
    SpawEngine.setActiveEditor(this);

  var sp = this.getSelectionParent();
  if (this.current_context != sp)
  {
    this.updateToolbar();
    this.current_context = sp;
  }  
}

// focus
SpawEditor.prototype.focus = function()
{
  var obj = (this.isInDesignMode())?this.getPageIframe(this.getActivePage().name):this.getPageInput(this.getActivePage().name);
  if (obj.contentWindow) // gecko
    obj.contentWindow.focus();
  else
    obj.focus();
}
// event handling
// array storing event handlers
SpawEngine.event_handlers = new Array();
SpawEngine.addEventHandler = function(evt_type, handler_fn, evt_target)
{
  var trg = (evt_target == null)?"page_doc":evt_target.toLowerCase();
  if (!SpawEngine.event_handlers[trg])
    SpawEngine.event_handlers[trg] = new Array();
  if (SpawEngine.event_handlers[trg][evt_type])
  {
    // this is not the first handler for this event
    SpawEngine.event_handlers[trg][evt_type].push(handler_fn);
  }
  else
  {
    // there are no handlers for this event
    if (evt_type.toLowerCase().substring(0,4) != "spaw")
    {
      // non-spaw event, register handlers
      var ev_obj;
      if (trg.substring(0,4) != "page" && trg != "form")
      {
        // not page or editor level events
        ev_obj = SpawEngine.getEventTargetObject(trg, null);
        if (ev_obj.attachEvent)
        {
          // ie
          ev_obj.attachEvent("on"+evt_type, new Function("event", 'SpawEngine.handleEvent("'+evt_type+'", event, "'+trg+'", null);'));
        }
        else
        {
          ev_obj.addEventListener(evt_type, new Function("event", 'SpawEngine.handleEvent("'+evt_type+'", event, "'+trg+'", null);'), false);
        }
      }
      else
      {
        var old_ev_obj;
        for (var si=0; si<SpawEngine.editors.length; si++)
        {
          if (trg == "form")
          {
            // editor level event
            ev_obj = SpawEngine.getEventTargetObject(trg, null, SpawEngine.editors[si]);
            if (ev_obj != old_ev_obj)
            {
              if (ev_obj.attachEvent)
              {
                // ie
                ev_obj.attachEvent("on"+evt_type, new Function("event", 'SpawEngine.handleEvent("'+evt_type+'", event, "'+trg+'","'+SpawEngine.editors[si].name+'");'));
              }
              else
              {
                ev_obj.addEventListener(evt_type, new Function("event", 'SpawEngine.handleEvent("'+evt_type+'", event, "'+trg+'","'+SpawEngine.editors[si].name+'");'), false);
              }
              old_ev_obj = ev_obj;
            }
          }
          else
          {
            // page level event
            for(var i=0; i<SpawEngine.editors[si].pages.length; i++)
            {
              ev_obj = SpawEngine.getEventTargetObject(trg, SpawEngine.editors[si].pages[i], SpawEngine.editors[si]);
              if (ev_obj.attachEvent)
              {
                // ie
                ev_obj.attachEvent("on"+evt_type, new Function("event", 'SpawEngine.handleEvent("'+evt_type+'", event, "'+trg+'","'+SpawEngine.editors[si].name+'");'));
              }
              else
              {
                ev_obj.addEventListener(evt_type, new Function("event", 'SpawEngine.handleEvent("'+evt_type+'", event, "'+trg+'","'+SpawEngine.editors[si].name+'");'), false);
              }
            }
          }
        }
      }
    }
    SpawEngine.event_handlers[trg][evt_type] = new Array();
    SpawEngine.event_handlers[trg][evt_type].push(handler_fn);
  }
}
SpawEngine.handleEvent = function(evt_type, evt, evt_target, editor_name)
{
  var trg = (evt_target == null)?"page_doc":evt_target.toLowerCase();
  var ed = editor_name?SpawEngine.getEditor(editor_name):SpawEngine.getActiveEditor();
  if (SpawEngine.event_handlers[trg] && SpawEngine.event_handlers[trg][evt_type])
  {
    for(var i=0; i<SpawEngine.event_handlers[trg][evt_type].length; i++)
    {
      eval(SpawEngine.event_handlers[trg][evt_type][i] + '(ed, evt)');
    }
  }
}
SpawEngine.getEventTargetObject = function(evt_target, page, editor)
{
  var trg = (evt_target == null)?"page_doc":evt_target.toLowerCase();
  var ev_obj;
  switch(trg)
  {
    case "page_iframe":
      ev_obj = editor.getPageIframeObject(page.name);
      break;
    case "page_doc":
      ev_obj = editor.getPageDoc(page.name);
      break;
    case "page_body":
      ev_obj = editor.getPageDoc(page.name).body;
      break;
    case "form":
      ev_obj = editor.getPageInput(editor.getActivePage().name).form;
      break;
    case "window":
      ev_obj = window;
      break;
    case "document":
      ev_obj = document;
      break;
    default:
      ev_obj = editor.getActivePageDoc();
      break;
  }
  return ev_obj;
}
// color class
function SpawColor()
{
}
SpawColor.prototype.hue = 359;
SpawColor.prototype.saturation = 100;
SpawColor.prototype.brightness = 100;
SpawColor.prototype.red = 255;
SpawColor.prototype.green = 255;
SpawColor.prototype.blue = 255;

SpawColor.prototype.setHSB = function(h, s, b)
{
  if (h != null)
    this.hue = h;
  if (s != null)
    this.saturation = s;
  if (b != null)
    this.brightness = b;
  this.updateRGBFromHSB();
}
SpawColor.prototype.setRGB = function(r, g, b)
{
  if (r != null)
    this.red = r;
  if (g != null)
    this.green = g;
  if (b != null)
    this.blue = b;
  this.updateHSBFromRGB();
}
SpawColor.prototype.setRGBFromHTML = function(val)
{
  var sval = val;
  if ((sval.length != 4 && sval.length != 7) || sval.charAt(0) != '#') 
  {
    // try named colors
    sval = this.getHTMLColorFromKeyword(sval);
  }

  if (sval != null && (sval.length == 4 || sval.length == 7) && sval.charAt(0) == '#')
  { 
    if (sval.length == 4)
    {
      this.red = this.hex2dec(sval.charAt(1)+sval.charAt(1));
      this.green = this.hex2dec(sval.charAt(2)+sval.charAt(2));
      this.blue = this.hex2dec(sval.charAt(3)+sval.charAt(3));
    }
    else
    {
      this.red = this.hex2dec(sval.substring(1,3));
      this.green = this.hex2dec(sval.substring(3,5));
      this.blue = this.hex2dec(sval.substring(5,7));
    }
    this.updateHSBFromRGB();
  }
}
SpawColor.prototype.getHTMLColorFromKeyword = function(kwd)
{
  var named_colors = new Array();
  named_colors['aliceblue']='#f0f8ff';
  named_colors['antiquewhite']='#faebd7';
  named_colors['aqua']='#00ffff';
  named_colors['aquamarine']='#7fffd4';
  named_colors['azure']='#f0ffff';
  named_colors['beige']='#f5f5dc';
  named_colors['bisque']='#ffe4c4';
  named_colors['black']='#000000';
  named_colors['blanchedalmond']='#ffebcd';
  named_colors['blue']='#0000ff';
  named_colors['blueviolet']='#8a2be2';
  named_colors['brown']='#a52a2a';
  named_colors['burlywood']='#deb887';
  named_colors['cadetblue']='#5f9ea0';
  named_colors['chartreuse']='#7fff00';
  named_colors['chocolate']='#d2691e';
  named_colors['coral']='#ff7f50';
  named_colors['cornflowerblue']='#6495ed';
  named_colors['cornsilk']='#fff8dc';
  named_colors['crimson']='#dc143c';
  named_colors['cyan']='#00ffff';
  named_colors['darkblue']='#00008b';
  named_colors['darkcyan']='#008b8b';
  named_colors['darkgoldenrod']='#b8860b';
  named_colors['darkgray']='#a9a9a9';
  named_colors['darkgreen']='#006400';
  named_colors['darkkhaki']='#bdb76b';
  named_colors['darkmagenta']='#8b008b';
  named_colors['darkolivegreen']='#556b2f';
  named_colors['darkorange']='#ff8c00';
  named_colors['darkorchid']='#9932cc';
  named_colors['darkred']='#8b0000';
  named_colors['darksalmon']='#e9967a';
  named_colors['darkseagreen']='#8fbc8f';
  named_colors['darkslateblue']='#483d8b';
  named_colors['darkslategray']='#2f4f4f';
  named_colors['darkturquoise']='#00ced1';
  named_colors['darkviolet']='#9400d3';
  named_colors['deeppink']='#ff1493';
  named_colors['deepskyblue']='#00bfff';
  named_colors['dimgray']='#696969';
  named_colors['dodgerblue']='#1e90ff';
  named_colors['firebrick']='#b22222';
  named_colors['floralwhite']='#fffaf0';
  named_colors['forestgreen']='#228b22';
  named_colors['fuchsia']='#ff00ff';
  named_colors['gainsboro']='#dcdcdc';
  named_colors['ghostwhite']='#f8f8ff';
  named_colors['gold']='#ffd700';
  named_colors['goldenrod']='#daa520';
  named_colors['gray']='#808080';
  named_colors['green']='#008000';
  named_colors['greenyellow']='#adff2f';
  named_colors['honeydew']='#f0fff0';
  named_colors['hotpink']='#ff69b4';
  named_colors['indianred']='#cd5c5c';
  named_colors['indigo']='#4b0082';
  named_colors['ivory']='#fffff0';
  named_colors['khaki']='#f0e68c';
  named_colors['lavender']='#e6e6fa';
  named_colors['lavenderblush']='#fff0f5';
  named_colors['lawngreen']='#7cfc00';
  named_colors['lemonchiffon']='#fffacd';
  named_colors['lightblue']='#add8e6';
  named_colors['lightcoral']='#f08080';
  named_colors['lightcyan']='#e0ffff';
  named_colors['lightgoldenrodyellow']='#fafad2';
  named_colors['lightgreen']='#90ee90';
  named_colors['lightgrey']='#d3d3d3';
  named_colors['lightpink']='#ffb6c1';
  named_colors['lightsalmon']='#ffa07a';
  named_colors['lightseagreen']='#20b2aa';
  named_colors['lightskyblue']='#87cefa';
  named_colors['lightslategray']='#778899';
  named_colors['lightsteelblue']='#b0c4de';
  named_colors['lightyellow']='#ffffe0';
  named_colors['lime']='#00ff00';
  named_colors['limegreen']='#32cd32';
  named_colors['linen']='#faf0e6';
  named_colors['magenta']='#ff00ff';
  named_colors['maroon']='#800000';
  named_colors['mediumaquamarine']='#66cdaa';
  named_colors['mediumblue']='#0000cd';
  named_colors['mediumorchid']='#ba55d3';
  named_colors['mediumpurple']='#9370db';
  named_colors['mediumseagreen']='#3cb371';
  named_colors['mediumslateblue']='#7b68ee';
  named_colors['mediumspringgreen']='#00fa9a';
  named_colors['mediumturquoise']='#48d1cc';
  named_colors['mediumvioletred']='#c71585';
  named_colors['midnightblue']='#191970';
  named_colors['mintcream']='#f5fffa';
  named_colors['mistyrose']='#ffe4e1';
  named_colors['moccasin']='#ffe4b5';
  named_colors['navajowhite']='#ffdead';
  named_colors['navy']='#000080';
  named_colors['oldlace']='#fdf5e6';
  named_colors['olive']='#808000';
  named_colors['olivedrab']='#6b8e23';
  named_colors['orange']='#ffa500';
  named_colors['orangered']='#ff4500';
  named_colors['orchid']='#da70d6';
  named_colors['palegoldenrod']='#eee8aa';
  named_colors['palegreen']='#98fb98';
  named_colors['paleturquoise']='#afeeee';
  named_colors['palevioletred']='#db7093';
  named_colors['papayawhip']='#ffefd5';
  named_colors['peachpuff']='#ffdab9';
  named_colors['peru']='#cd853f';
  named_colors['pink']='#ffc0cb';
  named_colors['plum']='#dda0dd';
  named_colors['powderblue']='#b0e0e6';
  named_colors['purple']='#800080';
  named_colors['red']='#ff0000';
  named_colors['rosybrown']='#bc8f8f';
  named_colors['royalblue']='#4169e1';
  named_colors['saddlebrown']='#8b4513';
  named_colors['salmon']='#fa8072';
  named_colors['sandybrown']='#f4a460';
  named_colors['seagreen']='#2e8b57';
  named_colors['seashell']='#fff5ee';
  named_colors['sienna']='#a0522d';
  named_colors['silver']='#c0c0c0';
  named_colors['skyblue']='#87ceeb';
  named_colors['slateblue']='#6a5acd';
  named_colors['slategray']='#708090';
  named_colors['snow']='#fffafa';
  named_colors['springgreen']='#00ff7f';
  named_colors['steelblue']='#4682b4';
  named_colors['tan']='#d2b48c';
  named_colors['teal']='#008080';
  named_colors['thistle']='#d8bfd8';
  named_colors['tomato']='#ff6347';
  named_colors['turquoise']='#40e0d0';
  named_colors['violet']='#ee82ee';
  named_colors['wheat']='#f5deb3';
  named_colors['white']='#ffffff';
  named_colors['whitesmoke']='#f5f5f5';
  named_colors['yellow']='#ffff00';
  named_colors['yellowgreen']=['#9acd32'];
  if (named_colors[kwd.toLowerCase()] != null)
    return named_colors[kwd.toLowerCase()];
  else
    return null;
}
SpawColor.parseRGB = function(rgbstr)
{
  var nc = new SpawColor();
  var r, g, b;
  if (!isNaN(parseInt(rgbstr)))
  {
    // number representation
    var n = parseInt(rgbstr);
    r = n%256;
    n = Math.floor(n/256);
    g = n%256;
    n = Math.floor(n/256);
    b = n%256;
    nc.setRGB(r, g, b);
  }
  else if (rgbstr.toLowerCase().indexOf('rgb(') == 0)
  {
    r = parseInt(rgbstr.substring(4, rgbstr.indexOf(',')));
    g = parseInt(rgbstr.substring(rgbstr.indexOf(',')+1, rgbstr.lastIndexOf(',')));
    b = parseInt(rgbstr.substring(rgbstr.lastIndexOf(',')+1, rgbstr.indexOf(')')));
    nc.setRGB(r, g, b);
  }
  else
  {
    nc.setRGBFromHTML(rgbstr);
  }
  return nc;
}

SpawColor.prototype.updateRGBFromHSB = function()
{
  var r,g,b;
  var fS = this.saturation/100;
  var fB = this.brightness/100;
  var hi = Math.floor(this.hue / 60) % 6;
  var f = this.hue / 60 - hi;
  var p = fB * (1 - fS);
  var q = fB * (1 - f * fS);
  var t = fB * (1 - (1 - f) * fS);
  switch (hi)
  {
  case 0:
    r = Math.round(fB * 255);
    g = Math.round(t * 255);
    b = Math.round(p * 255);
    break;
  case 1:
    r = Math.round(q * 255);
    g = Math.round(fB * 255);
    b = Math.round(p * 255);
    break;
  case 2:
    r = Math.round(p * 255);
    g = Math.round(fB * 255);
    b = Math.round(t * 255);
    break;
  case 3:
    r = Math.round(p * 255);
    g = Math.round(q * 255);
    b = Math.round(fB * 255);
    break;
  case 4:
    r = Math.round(t * 255);
    g = Math.round(p * 255);
    b = Math.round(fB * 255);
    break;
  case 5:
    r = Math.round(fB * 255);
    g = Math.round(p * 255);
    b = Math.round(q * 255);
    break;
  }
  this.red = r;
  this.green = g;
  this.blue = b;
}
SpawColor.prototype.updateHSBFromRGB = function()
{
  var h,s;
  var r = this.red/255;
  var g = this.green/255;
  var b = this.blue/255;
  var mx = Math.max(this.red, this.green, this.blue)/255;
  var mn = Math.min(this.red, this.green, this.blue)/255;
  if (mx == mn)
  {
    h = 0;
  }
  else if (mx == r && g>=b)
  {
    h = 60*(g - b)/(mx - mn);
  }
  else if (mx == r && g<b)
  {
    h = 60*(g - b)/(mx - mn) + 360;
  }
  else if (mx == g)
  {
    h = 60*(b - r)/(mx - mn) + 120;
  }
  else if (mx == b)
  {
    h = 60*(r - g)/(mx - mn) + 240;
  }
  
  if (mx == 0)
    s = 0;
  else
    s = Math.round((1 - mn/mx));
    
  this.hue = Math.round(h);
  this.saturation = Math.round(s*100);
  this.brightness = Math.round(mx*100);
}
SpawColor.prototype.dec2hex = function(dec)
{
  var result = '';
  var num = dec;
  while (num/16 >= 1 || num%16 > 0)
  {
    var ch = num%16;
    if (ch>=10)
    {
      switch(ch)
      {
        case 10:
          ch = 'a';
          break;
        case 11:
          ch = 'b';
          break;
        case 12:
          ch = 'c';
          break;
        case 13:
          ch = 'd';
          break;
        case 14:
          ch = 'e';
          break;
        case 15:
          ch = 'f';
          break;
      }
    }
    result = '' + ch + result;
    num = Math.floor(num/16);
  }
  return result;
}
SpawColor.prototype.hex2dec = function(hex)
{
  var l = hex.length;
  var p = 0;
  var result = 0;
  for(var i = l-1; i>=0; i--)
  {
    p = l-i-1;
    var c = hex.charAt(i);
    if (!isNaN(parseInt(c)))
    {
      result += parseInt(c)*Math.pow(16, p); 
    }
    else
    {
      switch(c.toLowerCase())
      {
        case 'a':
          result += 10*Math.pow(16, p);
          break; 
        case 'b':
          result += 11*Math.pow(16, p);
          break; 
        case 'c':
          result += 12*Math.pow(16, p);
          break; 
        case 'd':
          result += 13*Math.pow(16, p);
          break; 
        case 'e':
          result += 14*Math.pow(16, p);
          break; 
        case 'f':
          result += 15*Math.pow(16, p);
          break; 
      }
    }
  }
  return result; 
}
SpawColor.prototype.addZeroes = function(source, num)
{
  var result = source;
  while(result.length<num)
  {
    result = '0' + result;
  }
  return result;
}
SpawColor.prototype.getHtmlColor = function()
{
  return result = '#' + this.addZeroes(this.dec2hex(this.red),2) + this.addZeroes(this.dec2hex(this.green),2) + this.addZeroes(this.dec2hex(this.blue),2);  
}
// Tab class
function SpawTab(page)
{
  this.page = page;
}
SpawTab.prototype.page;
SpawTab.prototype.template;
SpawTab.prototype.active_template;
SpawTab.prototype.setInactive = function()
{
  var tab = document.getElementById(this.page.name + '_tab');
  if (tab)
  {
    tab.innerHTML = this.template;
  }
}
SpawTab.prototype.setActive = function()
{
  var tab = document.getElementById(this.page.name + '_tab');
  if (tab)
  {
    tab.innerHTML = this.active_template;
  }
}
// Page class
function SpawEditorPage(name, caption, direction)
{
  this.name = name;
  this.caption = caption;
  if (direction != null)
    this.direction = direction;
  else
    this.direction = "ltr";
}
SpawEditorPage.prototype.name;
SpawEditorPage.prototype.caption;
SpawEditorPage.prototype.direction;
SpawEditorPage.prototype.value;
SpawEditorPage.prototype.is_initialized = false;
SpawEditorPage.prototype.document;
// editing mode (design or html)
SpawEditorPage.prototype.editing_mode = "design";
// current editing mode toolbar item
SpawEditorPage.prototype.editing_mode_tbi;
// Context menu
function SpawContextMenu(editor)
{
  this.editor = editor;
}
SpawContextMenu.prototype.editor;
SpawContextMenu.prototype.enclosure;
SpawContextMenu.prototype.show = function(event)
{
  var last_tbn = '';
  this.enclosure = document.createElement("div");
  this.enclosure.className = this.editor.theme.prefix+'contextmenu';
  this.enclosure.style.position = "absolute";  
  this.enclosure.style.left = this.editor.getPageOffsetLeft() + event.clientX + "px";  
  this.enclosure.style.top = this.editor.getPageOffsetTop() + event.clientY + "px"; 
  this.enclosure.style.zIndex = 15000; 
  
  // add ite