Diff for /loncom/html/htmlarea/Attic/htmlarea.js between versions 1.2 and 1.3

version 1.2, 2004/06/01 22:37:37 version 1.3, 2004/06/01 23:46:10
Line 1 Line 1
 //  // htmlArea v3.0 - Copyright (c) 2002-2004 interactivetools.com, inc.
 // htmlArea v3.0 - Copyright (c) 2002 interactivetools.com, inc.  
 // This copyright notice MUST stay intact for use (see license.txt).  // This copyright notice MUST stay intact for use (see license.txt).
 //  //
   // Portions (c) dynarch.com, 2003-2004
   //
 // A free WYSIWYG editor replacement for <textarea> fields.  // A free WYSIWYG editor replacement for <textarea> fields.
 // For full source code and docs, visit http://www.interactivetools.com/  // For full source code and docs, visit http://www.interactivetools.com/
 //  //
 // Version 3.0 developed by Mihai Bazon for InteractiveTools.  // Version 3.0 developed by Mihai Bazon.
 //     http://students.infoiasi.ro/~mishoo  //   http://dynarch.com/mishoo
 //  //
 // $Id$  // $Id$
   
   if (typeof _editor_url == "string") {
    // Leave exactly one backslash at the end of _editor_url
    _editor_url = _editor_url.replace(/\x2f*$/, '/');
   } else {
    alert("WARNING: _editor_url is not set!  You should set this variable to the editor files path; it should preferably be an absolute path, like in '/htmlarea', but it can be relative if you prefer.  Further we will try to load the editor files correctly but we'll probably fail.");
    _editor_url = '';
   }
   
   // make sure we have a language
   if (typeof _editor_lang == "string") {
    _editor_lang = _editor_lang.toLowerCase();
   } else {
    _editor_lang = "en";
   }
   
 // Creates a new HTMLArea object.  Tries to replace the textarea with the given  // Creates a new HTMLArea object.  Tries to replace the textarea with the given
 // ID with it.  // ID with it.
 function HTMLArea(textarea, config) {  function HTMLArea(textarea, config) {
Line 24  function HTMLArea(textarea, config) { Line 40  function HTMLArea(textarea, config) {
  this._editMode = "wysiwyg";   this._editMode = "wysiwyg";
  this.plugins = {};   this.plugins = {};
  this._timerToolbar = null;   this._timerToolbar = null;
    this._timerUndo = null;
    this._undoQueue = new Array(this.config.undoSteps);
    this._undoPos = -1;
    this._customUndo = false;
  this._mdoc = document; // cache the document, we need it in plugins   this._mdoc = document; // cache the document, we need it in plugins
    this.doctype = '';
  }   }
 };  };
   
   // load some scripts
   (function() {
    var scripts = HTMLArea._scripts = [ _editor_url + "htmlarea.js",
       _editor_url + "dialog.js",
       _editor_url + "popupwin.js",
       _editor_url + "lang/" + _editor_lang + ".js" ];
    var head = document.getElementsByTagName("head")[0];
    // start from 1, htmlarea.js is already loaded
    for (var i = 1; i < scripts.length; ++i) {
    var script = document.createElement("script");
    script.src = scripts[i];
    head.appendChild(script);
    }
   })();
   
   // cache some regexps
   HTMLArea.RE_tagName = /(<\/|<)\s*([^ \t\n>]+)/ig;
   HTMLArea.RE_doctype = /(<!doctype((.|\n)*?)>)\n?/i;
   HTMLArea.RE_head    = /<head>((.|\n)*?)<\/head>/i;
   HTMLArea.RE_body    = /<body>((.|\n)*?)<\/body>/i;
   
 HTMLArea.Config = function () {  HTMLArea.Config = function () {
  this.version = "3.0";   this.version = "3.0";
   
Line 37  HTMLArea.Config = function () { Line 79  HTMLArea.Config = function () {
  // enable creation of a status bar?   // enable creation of a status bar?
  this.statusBar = true;   this.statusBar = true;
   
    // maximum size of the undo queue
    this.undoSteps = 20;
   
    // the time interval at which undo samples are taken
    this.undoTimeout = 500; // 1/2 sec.
   
  // the next parameter specifies whether the toolbar should be included   // the next parameter specifies whether the toolbar should be included
  // in the size or not.   // in the size or not.
  this.sizeIncludesToolbar = true;   this.sizeIncludesToolbar = true;
   
    // if true then HTMLArea will retrieve the full HTML, starting with the
    // <HTML> tag.
    this.fullPage = false;
   
  // style included in the iframe document   // style included in the iframe document
  this.pageStyle = "body { background-color: #fff; font-family: verdana,sans-serif; }";   this.pageStyle = "";
  if (typeof _editor_url != "undefined") {  
  this.editorURL = _editor_url;   // set to true if you want Word code to be cleaned upon Paste
  } else {   this.killWordOnPaste = false;
  this.editorURL = "";  
  }   // BaseURL included in the iframe document
    this.baseURL = document.baseURI || document.URL;
    if (this.baseURL && this.baseURL.match(/(.*)\/([^\/]+)/))
    this.baseURL = RegExp.$1 + "/";
   
  // URL-s   // URL-s
  this.imgURL = "images/";   this.imgURL = "images/";
  this.popupURL = "popups/";   this.popupURL = "popups/";
   
  // configuration for plugins  
  this.plugins = {};  
   
  /** CUSTOMIZING THE TOOLBAR   /** CUSTOMIZING THE TOOLBAR
  * -------------------------   * -------------------------
  *   *
Line 69  HTMLArea.Config = function () { Line 121  HTMLArea.Config = function () {
  [ "fontname", "space",   [ "fontname", "space",
   "fontsize", "space",    "fontsize", "space",
   "formatblock", "space",    "formatblock", "space",
   "bold", "italic", "underline", "separator",    "bold", "italic", "underline", "strikethrough", "separator",
   "strikethrough", "subscript", "superscript", "separator",    "subscript", "superscript", "separator",
   "copy", "cut", "paste", "space", "undo", "redo" ],    "copy", "cut", "paste", "space", "undo", "redo" ],
   
  [ "justifyleft", "justifycenter", "justifyright", "justifyfull", "separator",   [ "justifyleft", "justifycenter", "justifyright", "justifyfull", "separator",
     "lefttoright", "righttoleft", "separator",
   "insertorderedlist", "insertunorderedlist", "outdent", "indent", "separator",    "insertorderedlist", "insertunorderedlist", "outdent", "indent", "separator",
   "forecolor", "hilitecolor", "textindicator", "separator",    "forecolor", "hilitecolor", "separator",
   "inserthorizontalrule", "createlink", "insertimage", "inserttable", "htmlmode", "separator",    "inserthorizontalrule", "createlink", "insertimage", "inserttable", "htmlmode", "separator",
   "popupeditor", "separator", "showhelp", "about" ]    "popupeditor", "separator", "showhelp", "about" ]
  ];   ];
   
  this.fontname = {   this.fontname = {
  "Arial":   'arial,helvetica,sans-serif',   "Arial":   'arial,helvetica,sans-serif',
Line 116  HTMLArea.Config = function () { Line 169  HTMLArea.Config = function () {
  this.customSelects = {};   this.customSelects = {};
   
  function cut_copy_paste(e, cmd, obj) {   function cut_copy_paste(e, cmd, obj) {
  try {   e.execCommand(cmd);
  e.execCommand(cmd);  
  } catch (e) {  
  if (HTMLArea.is_gecko) {  
  alert("Some revisions of Mozilla/Gecko do not support programatic " +  
       "access to cut/copy/paste functions, for security reasons.  " +  
       "Your browser is one of them.  Please use the standard key combinations:\n" +  
       "CTRL-X for cut, CTRL-C for copy, CTRL-V for paste.");  
  obj.element.style.display = "none";  
  }  
  }  
  };   };
   
  // ADDING CUSTOM BUTTONS: please read below!   // ADDING CUSTOM BUTTONS: please read below!
Line 144  HTMLArea.Config = function () { Line 187  HTMLArea.Config = function () {
  //    - Icon: path to an icon image file for the button (TODO: use one image for all buttons!)   //    - Icon: path to an icon image file for the button (TODO: use one image for all buttons!)
  //    - Enabled in text mode: if false the button gets disabled for text-only mode; otherwise enabled all the time.   //    - Enabled in text mode: if false the button gets disabled for text-only mode; otherwise enabled all the time.
  this.btnList = {   this.btnList = {
  bold: [ "Bold", "images/ed_format_bold.gif", false, function(e) {e.execCommand("bold");} ],   bold: [ "Bold", "ed_format_bold.gif", false, function(e) {e.execCommand("bold");} ],
  italic: [ "Italic", "images/ed_format_italic.gif", false, function(e) {e.execCommand("italic");} ],   italic: [ "Italic", "ed_format_italic.gif", false, function(e) {e.execCommand("italic");} ],
  underline: [ "Underline", "images/ed_format_underline.gif", false, function(e) {e.execCommand("underline");} ],   underline: [ "Underline", "ed_format_underline.gif", false, function(e) {e.execCommand("underline");} ],
  strikethrough: [ "Strikethrough", "images/ed_format_strike.gif", false, function(e) {e.execCommand("strikethrough");} ],   strikethrough: [ "Strikethrough", "ed_format_strike.gif", false, function(e) {e.execCommand("strikethrough");} ],
  subscript: [ "Subscript", "images/ed_format_sub.gif", false, function(e) {e.execCommand("subscript");} ],   subscript: [ "Subscript", "ed_format_sub.gif", false, function(e) {e.execCommand("subscript");} ],
  superscript: [ "Superscript", "images/ed_format_sup.gif", false, function(e) {e.execCommand("superscript");} ],   superscript: [ "Superscript", "ed_format_sup.gif", false, function(e) {e.execCommand("superscript");} ],
  justifyleft: [ "Justify Left", "images/ed_align_left.gif", false, function(e) {e.execCommand("justifyleft");} ],   justifyleft: [ "Justify Left", "ed_align_left.gif", false, function(e) {e.execCommand("justifyleft");} ],
  justifycenter: [ "Justify Center", "images/ed_align_center.gif", false, function(e) {e.execCommand("justifycenter");} ],   justifycenter: [ "Justify Center", "ed_align_center.gif", false, function(e) {e.execCommand("justifycenter");} ],
  justifyright: [ "Justify Right", "images/ed_align_right.gif", false, function(e) {e.execCommand("justifyright");} ],   justifyright: [ "Justify Right", "ed_align_right.gif", false, function(e) {e.execCommand("justifyright");} ],
  justifyfull: [ "Justify Full", "images/ed_align_justify.gif", false, function(e) {e.execCommand("justifyfull");} ],   justifyfull: [ "Justify Full", "ed_align_justify.gif", false, function(e) {e.execCommand("justifyfull");} ],
  insertorderedlist: [ "Ordered List", "images/ed_list_num.gif", false, function(e) {e.execCommand("insertorderedlist");} ],   insertorderedlist: [ "Ordered List", "ed_list_num.gif", false, function(e) {e.execCommand("insertorderedlist");} ],
  insertunorderedlist: [ "Bulleted List", "images/ed_list_bullet.gif", false, function(e) {e.execCommand("insertunorderedlist");} ],   insertunorderedlist: [ "Bulleted List", "ed_list_bullet.gif", false, function(e) {e.execCommand("insertunorderedlist");} ],
  outdent: [ "Decrease Indent", "images/ed_indent_less.gif", false, function(e) {e.execCommand("outdent");} ],   outdent: [ "Decrease Indent", "ed_indent_less.gif", false, function(e) {e.execCommand("outdent");} ],
  indent: [ "Increase Indent", "images/ed_indent_more.gif", false, function(e) {e.execCommand("indent");} ],   indent: [ "Increase Indent", "ed_indent_more.gif", false, function(e) {e.execCommand("indent");} ],
  forecolor: [ "Font Color", "images/ed_color_fg.gif", false, function(e) {e.execCommand("forecolor");} ],   forecolor: [ "Font Color", "ed_color_fg.gif", false, function(e) {e.execCommand("forecolor");} ],
  hilitecolor: [ "Background Color", "images/ed_color_bg.gif", false, function(e) {e.execCommand("hilitecolor");} ],   hilitecolor: [ "Background Color", "ed_color_bg.gif", false, function(e) {e.execCommand("hilitecolor");} ],
  inserthorizontalrule: [ "Horizontal Rule", "images/ed_hr.gif", false, function(e) {e.execCommand("inserthorizontalrule");} ],   inserthorizontalrule: [ "Horizontal Rule", "ed_hr.gif", false, function(e) {e.execCommand("inserthorizontalrule");} ],
  createlink: [ "Insert Web Link", "images/ed_link.gif", false, function(e) {e.execCommand("createlink", true);} ],   createlink: [ "Insert Web Link", "ed_link.gif", false, function(e) {e.execCommand("createlink", true);} ],
  insertimage: [ "Insert Image", "images/ed_image.gif", false, function(e) {e.execCommand("insertimage");} ],   insertimage: [ "Insert/Modify Image", "ed_image.gif", false, function(e) {e.execCommand("insertimage");} ],
  inserttable: [ "Insert Table", "images/insert_table.gif", false, function(e) {e.execCommand("inserttable");} ],   inserttable: [ "Insert Table", "insert_table.gif", false, function(e) {e.execCommand("inserttable");} ],
  htmlmode: [ "Toggle HTML Source", "images/ed_html.gif", true, function(e) {e.execCommand("htmlmode");} ],   htmlmode: [ "Toggle HTML Source", "ed_html.gif", true, function(e) {e.execCommand("htmlmode");} ],
  popupeditor: [ "Enlarge Editor", "images/fullscreen_maximize.gif", true, function(e) {e.execCommand("popupeditor");} ],   popupeditor: [ "Enlarge Editor", "fullscreen_maximize.gif", true, function(e) {e.execCommand("popupeditor");} ],
  about: [ "About this editor", "images/ed_about.gif", true, function(e) {e.execCommand("about");} ],   about: [ "About this editor", "ed_about.gif", true, function(e) {e.execCommand("about");} ],
  showhelp: [ "Help using editor", "images/ed_help.gif", true, function(e) {e.execCommand("showhelp");} ],   showhelp: [ "Help using editor", "ed_help.gif", true, function(e) {e.execCommand("showhelp");} ],
  undo: [ "Undoes your last action", "images/ed_undo.gif", false, function(e) {e.execCommand("undo");} ],   undo: [ "Undoes your last action", "ed_undo.gif", false, function(e) {e.execCommand("undo");} ],
  redo: [ "Redoes your last action", "images/ed_redo.gif", false, function(e) {e.execCommand("redo");} ],   redo: [ "Redoes your last action", "ed_redo.gif", false, function(e) {e.execCommand("redo");} ],
  cut: [ "Cut selection", "images/ed_cut.gif", false, cut_copy_paste ],   cut: [ "Cut selection", "ed_cut.gif", false, cut_copy_paste ],
  copy: [ "Copy selection", "images/ed_copy.gif", false, cut_copy_paste ],   copy: [ "Copy selection", "ed_copy.gif", false, cut_copy_paste ],
  paste: [ "Paste from clipboard", "images/ed_paste.gif", false, cut_copy_paste ]   paste: [ "Paste from clipboard", "ed_paste.gif", false, cut_copy_paste ],
    lefttoright: [ "Direction left to right", "ed_left_to_right.gif", false, function(e) {e.execCommand("lefttoright");} ],
    righttoleft: [ "Direction right to left", "ed_right_to_left.gif", false, function(e) {e.execCommand("righttoleft");} ]
  };   };
  /* ADDING CUSTOM BUTTONS   /* ADDING CUSTOM BUTTONS
  * ---------------------   * ---------------------
Line 197  HTMLArea.Config = function () { Line 242  HTMLArea.Config = function () {
  * An alternate (also more convenient and recommended) way to   * An alternate (also more convenient and recommended) way to
  * accomplish this is to use the registerButton function below.   * accomplish this is to use the registerButton function below.
  */   */
  // initialize tooltips from the I18N module   // initialize tooltips from the I18N module and generate correct image path
  for (var i in this.btnList) {   for (var i in this.btnList) {
  var btn = this.btnList[i];   var btn = this.btnList[i];
    btn[1] = _editor_url + this.imgURL + btn[1];
  if (typeof HTMLArea.I18N.tooltips[i] != "undefined") {   if (typeof HTMLArea.I18N.tooltips[i] != "undefined") {
  btn[0] = HTMLArea.I18N.tooltips[i];   btn[0] = HTMLArea.I18N.tooltips[i];
  }   }
Line 235  HTMLArea.Config.prototype.registerButton Line 281  HTMLArea.Config.prototype.registerButton
  }   }
  // check for existing id   // check for existing id
  if (typeof this.customSelects[the_id] != "undefined") {   if (typeof this.customSelects[the_id] != "undefined") {
  alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists.");   // alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists.");
  }   }
  if (typeof this.btnList[the_id] != "undefined") {   if (typeof this.btnList[the_id] != "undefined") {
  alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists.");   // alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists.");
  }   }
  switch (typeof id) {   switch (typeof id) {
     case "string": this.btnList[id] = [ tooltip, image, textMode, action, context ]; break;      case "string": this.btnList[id] = [ tooltip, image, textMode, action, context ]; break;
Line 255  HTMLArea.Config.prototype.registerButton Line 301  HTMLArea.Config.prototype.registerButton
 HTMLArea.Config.prototype.registerDropdown = function(object) {  HTMLArea.Config.prototype.registerDropdown = function(object) {
  // check for existing id   // check for existing id
  if (typeof this.customSelects[object.id] != "undefined") {   if (typeof this.customSelects[object.id] != "undefined") {
  alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists.");   // alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists.");
  }   }
  if (typeof this.btnList[object.id] != "undefined") {   if (typeof this.btnList[object.id] != "undefined") {
  alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists.");   // alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists.");
  }   }
  this.customSelects[object.id] = object;   this.customSelects[object.id] = object;
 };  };
   
   /** Call this function to remove some buttons/drop-down boxes from the toolbar.
    * Pass as the only parameter a string containing button/drop-down names
    * delimited by spaces.  Note that the string should also begin with a space
    * and end with a space.  Example:
    *
    *   config.hideSomeButtons(" fontname fontsize textindicator ");
    *
    * It's useful because it's easier to remove stuff from the defaul toolbar than
    * create a brand new toolbar ;-)
    */
   HTMLArea.Config.prototype.hideSomeButtons = function(remove) {
    var toolbar = this.toolbar;
    for (var i in toolbar) {
    var line = toolbar[i];
    for (var j = line.length; --j >= 0; ) {
    if (remove.indexOf(" " + line[j] + " ") >= 0) {
    var len = 1;
    if (/separator|space/.test(line[j + 1])) {
    len = 2;
    }
    line.splice(j, len);
    }
    }
    }
   };
   
 /** Helper function: replace all TEXTAREA-s in the document with HTMLArea-s. */  /** Helper function: replace all TEXTAREA-s in the document with HTMLArea-s. */
 HTMLArea.replaceAll = function(config) {  HTMLArea.replaceAll = function(config) {
  var tas = document.getElementsByTagName("textarea");   var tas = document.getElementsByTagName("textarea");
Line 271  HTMLArea.replaceAll = function(config) { Line 343  HTMLArea.replaceAll = function(config) {
   
 /** Helper function: replaces the TEXTAREA with the given ID with HTMLArea. */  /** Helper function: replaces the TEXTAREA with the given ID with HTMLArea. */
 HTMLArea.replace = function(id, config) {  HTMLArea.replace = function(id, config) {
  var ta = document.getElementById(id);   var ta = HTMLArea.getElementById("textarea", id);
  return ta ? (new HTMLArea(ta, config)).generate() : null;   return ta ? (new HTMLArea(ta, config)).generate() : null;
 };  };
   
Line 477  HTMLArea.prototype._createToolbar = func Line 549  HTMLArea.prototype._createToolbar = func
  }   }
  });   });
  var img = document.createElement("img");   var img = document.createElement("img");
  img.src = editor.imgURL(btn[1]);   img.src = btn[1];
  img.style.width = "18px";   img.style.width = "18px";
  img.style.height = "18px";   img.style.height = "18px";
  el.appendChild(img);   el.appendChild(img);
Line 525  HTMLArea.prototype._createToolbar = func Line 597  HTMLArea.prototype._createToolbar = func
 };  };
   
 HTMLArea.prototype._createStatusBar = function() {  HTMLArea.prototype._createStatusBar = function() {
  var div = document.createElement("div");   var statusbar = document.createElement("div");
  div.className = "statusBar";   statusbar.className = "statusBar";
  this._htmlArea.appendChild(div);   this._htmlArea.appendChild(statusbar);
  this._statusBar = div;   this._statusBar = statusbar;
  div.appendChild(document.createTextNode(HTMLArea.I18N.msg["Path"] + ": "));   // statusbar.appendChild(document.createTextNode(HTMLArea.I18N.msg["Path"] + ": "));
  // creates a holder for the path view   // creates a holder for the path view
  div = document.createElement("span");   div = document.createElement("span");
  div.className = "statusBarTree";   div.className = "statusBarTree";
    div.innerHTML = HTMLArea.I18N.msg["Path"] + ": ";
  this._statusBarTree = div;   this._statusBarTree = div;
  this._statusBar.appendChild(div);   this._statusBar.appendChild(div);
  if (!this.config.statusBar) {   if (!this.config.statusBar) {
  // disable it...   // disable it...
  div.style.display = "none";   statusbar.style.display = "none";
  }   }
 };  };
   
Line 548  HTMLArea.prototype.generate = function ( Line 621  HTMLArea.prototype.generate = function (
  var textarea = this._textArea;   var textarea = this._textArea;
  if (typeof textarea == "string") {   if (typeof textarea == "string") {
  // it's not element but ID   // it's not element but ID
  this._textArea = textarea = document.getElementById(textarea);   this._textArea = textarea = HTMLArea.getElementById("textarea", textarea);
  }   }
  this._ta_size = {   this._ta_size = {
  w: textarea.offsetWidth,   w: textarea.offsetWidth,
Line 567  HTMLArea.prototype.generate = function ( Line 640  HTMLArea.prototype.generate = function (
  if (textarea.form) {   if (textarea.form) {
  // we have a form, on submit get the HTMLArea content and   // we have a form, on submit get the HTMLArea content and
  // update original textarea.   // update original textarea.
  textarea.form.onsubmit = function() {   var f = textarea.form;
    if (typeof f.onsubmit == "function") {
    var funcref = f.onsubmit;
    if (typeof f.__msh_prevOnSubmit == "undefined") {
    f.__msh_prevOnSubmit = [];
    }
    f.__msh_prevOnSubmit.push(funcref);
    }
    f.onsubmit = function() {
  editor._textArea.value = editor.getHTML();   editor._textArea.value = editor.getHTML();
    var a = this.__msh_prevOnSubmit;
    // call previous submit methods if they were there.
    if (typeof a != "undefined") {
    for (var i in a) {
    a[i]();
    }
    }
  };   };
  }   }
   
Line 636  HTMLArea.prototype.generate = function ( Line 724  HTMLArea.prototype.generate = function (
  // FIXME: don't know what else to do here.  Normally   // FIXME: don't know what else to do here.  Normally
  // we'll never reach this point.   // we'll never reach this point.
  if (HTMLArea.is_gecko) {   if (HTMLArea.is_gecko) {
  setTimeout(initIframe, 10);   setTimeout(initIframe, 100);
  return false;   return false;
  } else {   } else {
  alert("ERROR: IFRAME can't be initialized.");   alert("ERROR: IFRAME can't be initialized.");
Line 647  HTMLArea.prototype.generate = function ( Line 735  HTMLArea.prototype.generate = function (
  doc.designMode = "on";   doc.designMode = "on";
  }   }
  editor._doc = doc;   editor._doc = doc;
  doc.open();   if (!editor.config.fullPage) {
  var html = "<html>\n";   doc.open();
  html += "<head>\n";   var html = "<html>\n";
  html += "<style>" + editor.config.pageStyle + "</style>\n";   html += "<head>\n";
  html += "</head>\n";   if (editor.config.baseURL)
  html += "<body>\n";   html += '<base href="' + editor.config.baseURL + '" />';
  html += editor._textArea.value;   html += "<style> html,body { border: 0px; } " +
  html += "</body>\n";   editor.config.pageStyle + "</style>\n";
  html += "</html>";   html += "</head>\n";
  doc.write(html);   html += "<body>\n";
  doc.close();   html += editor._textArea.value;
    html += "</body>\n";
    html += "</html>";
    doc.write(html);
    doc.close();
    } else {
    var html = editor._textArea.value;
    if (html.match(HTMLArea.RE_doctype)) {
    editor.setDoctype(RegExp.$1);
    html = html.replace(HTMLArea.RE_doctype, "");
    }
    doc.open();
    doc.write(html);
    doc.close();
    }
   
  if (HTMLArea.is_ie) {   if (HTMLArea.is_ie) {
  // enable editable mode for IE. For some reason this   // enable editable mode for IE. For some reason this
Line 673  HTMLArea.prototype.generate = function ( Line 775  HTMLArea.prototype.generate = function (
  function (event) {   function (event) {
  return editor._editorEvent(HTMLArea.is_ie ? editor._iframe.contentWindow.event : event);   return editor._editorEvent(HTMLArea.is_ie ? editor._iframe.contentWindow.event : event);
  });   });
  editor.updateToolbar();  
    // check if any plugins have registered refresh handlers
    for (var i in editor.plugins) {
    var plugin = editor.plugins[i].instance;
    if (typeof plugin.onGenerate == "function")
    plugin.onGenerate();
    }
   
    setTimeout(function() {
    editor.updateToolbar();
    }, 250);
   
    if (typeof editor.onGenerate == "function")
    editor.onGenerate();
  };   };
  setTimeout(initIframe, HTMLArea.is_gecko ? 10 : 0);   setTimeout(initIframe, 100);
 };  };
   
 // Switches editor mode; parameter can be "textmode" or "wysiwyg".  If no  // Switches editor mode; parameter can be "textmode" or "wysiwyg".  If no
Line 696  HTMLArea.prototype.setMode = function(mo Line 811  HTMLArea.prototype.setMode = function(mo
     case "wysiwyg":      case "wysiwyg":
  if (HTMLArea.is_gecko) {   if (HTMLArea.is_gecko) {
  // disable design mode before changing innerHTML   // disable design mode before changing innerHTML
  this._doc.designMode = "off";   try {
    this._doc.designMode = "off";
    } catch(e) {};
  }   }
  this._doc.body.innerHTML = this.getHTML();   if (!this.config.fullPage)
    this._doc.body.innerHTML = this.getHTML();
    else
    this.setFullHTML(this.getHTML());
  this._iframe.style.display = "block";   this._iframe.style.display = "block";
  this._textArea.style.display = "none";   this._textArea.style.display = "none";
  if (HTMLArea.is_gecko) {   if (HTMLArea.is_gecko) {
  // we need to refresh that info for Moz-1.3a   // we need to refresh that info for Moz-1.3a
  this._doc.designMode = "on";   try {
    this._doc.designMode = "on";
    } catch(e) {};
  }   }
  if (this.config.statusBar) {   if (this.config.statusBar) {
  this._statusBar.innerHTML = '';   this._statusBar.innerHTML = '';
Line 719  HTMLArea.prototype.setMode = function(mo Line 841  HTMLArea.prototype.setMode = function(mo
  this.focusEditor();   this.focusEditor();
 };  };
   
   HTMLArea.prototype.setFullHTML = function(html) {
    var save_multiline = RegExp.multiline;
    RegExp.multiline = true;
    if (html.match(HTMLArea.RE_doctype)) {
    this.setDoctype(RegExp.$1);
    html = html.replace(HTMLArea.RE_doctype, "");
    }
    RegExp.multiline = save_multiline;
    if (!HTMLArea.is_ie) {
    if (html.match(HTMLArea.RE_head))
    this._doc.getElementsByTagName("head")[0].innerHTML = RegExp.$1;
    if (html.match(HTMLArea.RE_body))
    this._doc.getElementsByTagName("body")[0].innerHTML = RegExp.$1;
    } else {
    var html_re = /<html>((.|\n)*?)<\/html>/i;
    html = html.replace(html_re, "$1");
    this._doc.open();
    this._doc.write(html);
    this._doc.close();
    this._doc.body.contentEditable = true;
    return true;
    }
   };
   
 /***************************************************  /***************************************************
  *  Category: PLUGINS   *  Category: PLUGINS
  ***************************************************/   ***************************************************/
   
   // this is the variant of the function above where the plugin arguments are
   // already packed in an array.  Externally, it should be only used in the
   // full-screen editor code, in order to initialize plugins with the same
   // parameters as in the opener window.
   HTMLArea.prototype.registerPlugin2 = function(plugin, args) {
    if (typeof plugin == "string")
    plugin = eval(plugin);
    var obj = new plugin(this, args);
    if (obj) {
    var clone = {};
    var info = plugin._pluginInfo;
    for (var i in info)
    clone[i] = info[i];
    clone.instance = obj;
    clone.args = args;
    this.plugins[plugin._pluginInfo.name] = clone;
    } else
    alert("Can't register plugin " + plugin.toString() + ".");
   };
   
 // Create the specified plugin and register it with this HTMLArea  // Create the specified plugin and register it with this HTMLArea
 HTMLArea.prototype.registerPlugin = function(pluginName) {  HTMLArea.prototype.registerPlugin = function() {
  this.plugins[pluginName] = eval("new " + pluginName + "(this);");   var plugin = arguments[0];
    var args = [];
    for (var i = 1; i < arguments.length; ++i)
    args.push(arguments[i]);
    this.registerPlugin2(plugin, args);
 };  };
   
 // static function that loads the required plugin and lang file, based on the  // static function that loads the required plugin and lang file, based on the
 // language loaded already for HTMLArea.  You better make sure that the plugin  // language loaded already for HTMLArea.  You better make sure that the plugin
 // _has_ that language, otherwise shit might happen ;-)  // _has_ that language, otherwise shit might happen ;-)
 HTMLArea.loadPlugin = function(pluginName) {  HTMLArea.loadPlugin = function(pluginName) {
  var editorurl = '';   var dir = _editor_url + "plugins/" + pluginName;
  if (typeof _editor_url != "undefined") {  
  editorurl = _editor_url + "/";  
  }  
  var dir = editorurl + "plugins/" + pluginName;  
  var plugin = pluginName.replace(/([a-z])([A-Z])([a-z])/g,   var plugin = pluginName.replace(/([a-z])([A-Z])([a-z])/g,
  function (str, l1, l2, l3) {   function (str, l1, l2, l3) {
  return l1 + "-" + l2.toLowerCase() + l3;   return l1 + "-" + l2.toLowerCase() + l3;
  }).toLowerCase() + ".js";   }).toLowerCase() + ".js";
  document.write("<script type='text/javascript' src='" + dir + "/" + plugin + "'></script>");   var plugin_file = dir + "/" + plugin;
  document.write("<script type='text/javascript' src='" + dir + "/lang/" + HTMLArea.I18N.lang + ".js'></script>");   var plugin_lang = dir + "/lang/" + HTMLArea.I18N.lang + ".js";
    HTMLArea._scripts.push(plugin_file, plugin_lang);
    document.write("<script type='text/javascript' src='" + plugin_file + "'></script>");
    document.write("<script type='text/javascript' src='" + plugin_lang + "'></script>");
   };
   
   HTMLArea.loadStyle = function(style, plugin) {
    var url = _editor_url || '';
    if (typeof plugin != "undefined") {
    url += "plugins/" + plugin + "/";
    }
    url += style;
    document.write("<style type='text/css'>@import url(" + url + ");</style>");
 };  };
   HTMLArea.loadStyle("htmlarea.css");
   
 /***************************************************  /***************************************************
  *  Category: EDITOR UTILITIES   *  Category: EDITOR UTILITIES
  ***************************************************/   ***************************************************/
   
   // The following function is a slight variation of the word cleaner code posted
   // by Weeezl (user @ InteractiveTools forums).
   HTMLArea.prototype._wordClean = function() {
    var D = this.getInnerHTML();
    if (D.indexOf('class=Mso') >= 0) {
   
    // make one line
    D = D.replace(/\r\n/g, ' ').
    replace(/\n/g, ' ').
    replace(/\r/g, ' ').
    replace(/\&nbsp\;/g,' ');
   
    // keep tags, strip attributes
    D = D.replace(/ class=[^\s|>]*/gi,'').
    //replace(/<p [^>]*TEXT-ALIGN: justify[^>]*>/gi,'<p align="justify">').
    replace(/ style=\"[^>]*\"/gi,'').
    replace(/ align=[^\s|>]*/gi,'');
   
    //clean up tags
    D = D.replace(/<b [^>]*>/gi,'<b>').
    replace(/<i [^>]*>/gi,'<i>').
    replace(/<li [^>]*>/gi,'<li>').
    replace(/<ul [^>]*>/gi,'<ul>');
   
    // replace outdated tags
    D = D.replace(/<b>/gi,'<strong>').
    replace(/<\/b>/gi,'</strong>');
   
    // mozilla doesn't like <em> tags
    D = D.replace(/<em>/gi,'<i>').
    replace(/<\/em>/gi,'</i>');
   
    // kill unwanted tags
    D = D.replace(/<\?xml:[^>]*>/g, '').       // Word xml
    replace(/<\/?st1:[^>]*>/g,'').     // Word SmartTags
    replace(/<\/?[a-z]\:[^>]*>/g,'').  // All other funny Word non-HTML stuff
    replace(/<\/?font[^>]*>/gi,'').    // Disable if you want to keep font formatting
    replace(/<\/?span[^>]*>/gi,' ').
    replace(/<\/?div[^>]*>/gi,' ').
    replace(/<\/?pre[^>]*>/gi,' ').
    replace(/<\/?h[1-6][^>]*>/gi,' ');
   
    //remove empty tags
    //D = D.replace(/<strong><\/strong>/gi,'').
    //replace(/<i><\/i>/gi,'').
    //replace(/<P[^>]*><\/P>/gi,'');
   
    // nuke double tags
    oldlen = D.length + 1;
    while(oldlen > D.length) {
    oldlen = D.length;
    // join us now and free the tags, we'll be free hackers, we'll be free... ;-)
    D = D.replace(/<([a-z][a-z]*)> *<\/\1>/gi,' ').
    replace(/<([a-z][a-z]*)> *<([a-z][^>]*)> *<\/\1>/gi,'<$2>');
    }
    D = D.replace(/<([a-z][a-z]*)><\1>/gi,'<$1>').
    replace(/<\/([a-z][a-z]*)><\/\1>/gi,'<\/$1>');
   
    // nuke double spaces
    D = D.replace(/  */gi,' ');
   
    this.setHTML(D);
    this.updateToolbar();
    }
   };
   
 HTMLArea.prototype.forceRedraw = function() {  HTMLArea.prototype.forceRedraw = function() {
  this._doc.body.style.visibility = "hidden";   this._doc.body.style.visibility = "hidden";
  this._doc.body.style.visibility = "visible";   this._doc.body.style.visibility = "visible";
Line 765  HTMLArea.prototype.focusEditor = functio Line 1010  HTMLArea.prototype.focusEditor = functio
  return this._doc;   return this._doc;
 };  };
   
   // takes a snapshot of the current text (for undo)
   HTMLArea.prototype._undoTakeSnapshot = function() {
    ++this._undoPos;
    if (this._undoPos >= this.config.undoSteps) {
    // remove the first element
    this._undoQueue.shift();
    --this._undoPos;
    }
    // use the fasted method (getInnerHTML);
    var take = true;
    var txt = this.getInnerHTML();
    if (this._undoPos > 0)
    take = (this._undoQueue[this._undoPos - 1] != txt);
    if (take) {
    this._undoQueue[this._undoPos] = txt;
    } else {
    this._undoPos--;
    }
   };
   
   HTMLArea.prototype.undo = function() {
    if (this._undoPos > 0) {
    var txt = this._undoQueue[--this._undoPos];
    if (txt) this.setHTML(txt);
    else ++this._undoPos;
    }
   };
   
   HTMLArea.prototype.redo = function() {
    if (this._undoPos < this._undoQueue.length - 1) {
    var txt = this._undoQueue[++this._undoPos];
    if (txt) this.setHTML(txt);
    else --this._undoPos;
    }
   };
   
 // updates enabled/disable/active state of the toolbar elements  // updates enabled/disable/active state of the toolbar elements
 HTMLArea.prototype.updateToolbar = function(noStatus) {  HTMLArea.prototype.updateToolbar = function(noStatus) {
  var doc = this._doc;   var doc = this._doc;
Line 773  HTMLArea.prototype.updateToolbar = funct Line 1054  HTMLArea.prototype.updateToolbar = funct
  if (!text) {   if (!text) {
  ancestors = this.getAllAncestors();   ancestors = this.getAllAncestors();
  if (this.config.statusBar && !noStatus) {   if (this.config.statusBar && !noStatus) {
  this._statusBarTree.innerHTML = ''; // clear   this._statusBarTree.innerHTML = HTMLArea.I18N.msg["Path"] + ": "; // clear
  for (var i = ancestors.length; --i >= 0;) {   for (var i = ancestors.length; --i >= 0;) {
  var el = ancestors[i];   var el = ancestors[i];
  if (!el) {   if (!el) {
Line 864  HTMLArea.prototype.updateToolbar = funct Line 1145  HTMLArea.prototype.updateToolbar = funct
     case "fontname":      case "fontname":
     case "fontsize":      case "fontsize":
     case "formatblock":      case "formatblock":
  if (!text) {   if (!text) try {
  var value = ("" + doc.queryCommandValue(cmd)).toLowerCase();   var value = ("" + doc.queryCommandValue(cmd)).toLowerCase();
  if (!value) {   if (!value) {
  // FIXME: what do we do here?   // FIXME: what do we do here?
Line 886  HTMLArea.prototype.updateToolbar = funct Line 1167  HTMLArea.prototype.updateToolbar = funct
  }   }
  ++k;   ++k;
  }   }
  }   } catch(e) {};
  break;   break;
     case "textindicator":      case "textindicator":
  if (!text) {   if (!text) {
Line 907  HTMLArea.prototype.updateToolbar = funct Line 1188  HTMLArea.prototype.updateToolbar = funct
  }   }
  break;   break;
     case "htmlmode": btn.state("active", text); break;      case "htmlmode": btn.state("active", text); break;
       case "lefttoright":
       case "righttoleft":
    var el = this.getParentElement();
    while (el && !HTMLArea.isBlockElement(el))
    el = el.parentNode;
    if (el)
    btn.state("active", (el.style.direction == ((cmd == "righttoleft") ? "rtl" : "ltr")));
    break;
     default:      default:
  try {   try {
  btn.state("active", (!text && doc.queryCommandState(cmd)));   btn.state("active", (!text && doc.queryCommandState(cmd)));
  } catch (e) {}   } catch (e) {}
  }   }
  }   }
    // take undo snapshots
    if (this._customUndo && !this._timerUndo) {
    this._undoTakeSnapshot();
    var editor = this;
    this._timerUndo = setTimeout(function() {
    editor._timerUndo = null;
    }, this.config.undoTimeout);
    }
    // check if any plugins have registered refresh handlers
    for (var i in this.plugins) {
    var plugin = this.plugins[i].instance;
    if (typeof plugin.onUpdateToolbar == "function")
    plugin.onUpdateToolbar();
    }
 };  };
   
 /** Returns a node after which we can insert other nodes, in the current  /** Returns a node after which we can insert other nodes, in the current
Line 968  HTMLArea.prototype.getParentElement = fu Line 1271  HTMLArea.prototype.getParentElement = fu
  var sel = this._getSelection();   var sel = this._getSelection();
  var range = this._createRange(sel);   var range = this._createRange(sel);
  if (HTMLArea.is_ie) {   if (HTMLArea.is_ie) {
  return range.parentElement ? range.parentElement() : this._doc.body;   switch (sel.type) {
  } else {      case "Text":
       case "None":
    // It seems that even for selection of type "None",
    // there _is_ a parent element and it's value is not
    // only correct, but very important to us.  MSIE is
    // certainly the buggiest browser in the world and I
    // wonder, God, how can Earth stand it?
    return range.parentElement();
       case "Control":
    return range.item(0);
       default:
    return this._doc.body;
    }
    } else try {
  var p = range.commonAncestorContainer;   var p = range.commonAncestorContainer;
    if (!range.collapsed && range.startContainer == range.endContainer &&
       range.startOffset - range.endOffset <= 1 && range.startContainer.hasChildNodes())
    p = range.startContainer.childNodes[range.startOffset];
    /*
    alert(range.startContainer + ":" + range.startOffset + "\n" +
         range.endContainer + ":" + range.endOffset);
    */
  while (p.nodeType == 3) {   while (p.nodeType == 3) {
  p = p.parentNode;   p = p.parentNode;
  }   }
  return p;   return p;
    } catch (e) {
    return null;
  }   }
 };  };
   
Line 1051  HTMLArea.prototype.getSelectedHTML = fun Line 1376  HTMLArea.prototype.getSelectedHTML = fun
  if (HTMLArea.is_ie) {   if (HTMLArea.is_ie) {
  existing = range.htmlText;   existing = range.htmlText;
  } else {   } else {
  existing = HTMLArea.getHTML(range.cloneContents(), false);   existing = HTMLArea.getHTML(range.cloneContents(), false, this);
  }   }
  return existing;   return existing;
 };  };
   
 // Called when the user clicks on "InsertImage" button  /// Return true if we have some selection
 HTMLArea.prototype._insertImage = function() {  HTMLArea.prototype.hasSelectedText = function() {
    // FIXME: come _on_ mishoo, you can do better than this ;-)
    return this.getSelectedHTML() != '';
   };
   
   HTMLArea.prototype._createLink = function(link) {
    var editor = this;
    var outparam = null;
    if (typeof link == "undefined") {
    link = this.getParentElement();
    if (link && !/^a$/i.test(link.tagName))
    link = null;
    }
    if (link) outparam = {
    f_href   : HTMLArea.is_ie ? editor.stripBaseURL(link.href) : link.getAttribute("href"),
    f_title  : link.title,
    f_target : link.target
    };
    this._popupDialog("link.html", function(param) {
    if (!param)
    return false;
    var a = link;
    if (!a) {
    editor._doc.execCommand("createlink", false, param.f_href);
    a = editor.getParentElement();
    var sel = editor._getSelection();
    var range = editor._createRange(sel);
    if (!HTMLArea.is_ie) {
    a = range.startContainer;
    if (!/^a$/i.test(a.tagName))
    a = a.nextSibling;
    }
    } else a.href = param.f_href.trim();
    if (!/^a$/i.test(a.tagName))
    return false;
    a.target = param.f_target.trim();
    a.title = param.f_title.trim();
    editor.selectNodeContents(a);
    editor.updateToolbar();
    }, outparam);
   };
   
   // Called when the user clicks on "InsertImage" button.  If an image is already
   // there, it will just modify it's properties.
   HTMLArea.prototype._insertImage = function(image) {
  var editor = this; // for nested functions   var editor = this; // for nested functions
    var outparam = null;
    if (typeof image == "undefined") {
    image = this.getParentElement();
    if (image && !/^img$/i.test(image.tagName))
    image = null;
    }
    if (image) outparam = {
    f_url    : HTMLArea.is_ie ? editor.stripBaseURL(image.src) : image.getAttribute("src"),
    f_alt    : image.alt,
    f_border : image.border,
    f_align  : image.align,
    f_vert   : image.vspace,
    f_horiz  : image.hspace
    };
  this._popupDialog("insert_image.html", function(param) {   this._popupDialog("insert_image.html", function(param) {
  if (!param) { // user must have pressed Cancel   if (!param) { // user must have pressed Cancel
  return false;   return false;
  }   }
  var sel = editor._getSelection();   var img = image;
  var range = editor._createRange(sel);   if (!img) {
  editor._doc.execCommand("insertimage", false, param["f_url"]);   var sel = editor._getSelection();
  var img = null;   var range = editor._createRange(sel);
  if (HTMLArea.is_ie) {   editor._doc.execCommand("insertimage", false, param.f_url);
  img = range.parentElement();   if (HTMLArea.is_ie) {
  // wonder if this works...   img = range.parentElement();
  if (img.tagName.toLowerCase() != "img") {   // wonder if this works...
  img = img.previousSibling;   if (img.tagName.toLowerCase() != "img") {
    img = img.previousSibling;
    }
    } else {
    img = range.startContainer.previousSibling;
  }   }
  } else {   } else {
  img = range.startContainer.previousSibling;   img.src = param.f_url;
  }   }
  for (field in param) {   for (field in param) {
  var value = param[field];   var value = param[field];
  if (!value) {  
  continue;  
  }  
  switch (field) {   switch (field) {
     case "f_alt"    : img.alt = value; break;      case "f_alt"    : img.alt = value; break;
     case "f_border" : img.border = parseInt(value); break;      case "f_border" : img.border = parseInt(value || "0"); break;
     case "f_align"  : img.align = value; break;      case "f_align"  : img.align = value; break;
     case "f_vert"   : img.vspace = parseInt(value); break;      case "f_vert"   : img.vspace = parseInt(value || "0"); break;
     case "f_horiz"  : img.hspace = parseInt(value); break;      case "f_horiz"  : img.hspace = parseInt(value || "0"); break;
  }   }
  }   }
  }, null);   }, outparam);
 };  };
   
 // Called when the user clicks the Insert Table button  // Called when the user clicks the Insert Table button
Line 1172  HTMLArea.prototype._comboSelected = func Line 1556  HTMLArea.prototype._comboSelected = func
 HTMLArea.prototype.execCommand = function(cmdID, UI, param) {  HTMLArea.prototype.execCommand = function(cmdID, UI, param) {
  var editor = this; // for nested functions   var editor = this; // for nested functions
  this.focusEditor();   this.focusEditor();
  switch (cmdID.toLowerCase()) {   cmdID = cmdID.toLowerCase();
    switch (cmdID) {
     case "htmlmode" : this.setMode(); break;      case "htmlmode" : this.setMode(); break;
     case "hilitecolor":      case "hilitecolor":
  (HTMLArea.is_ie) && (cmdID = "backcolor");   (HTMLArea.is_ie) && (cmdID = "backcolor");
Line 1184  HTMLArea.prototype.execCommand = functio Line 1569  HTMLArea.prototype.execCommand = functio
  }, HTMLArea._colorToRgb(this._doc.queryCommandValue(cmdID)));   }, HTMLArea._colorToRgb(this._doc.queryCommandValue(cmdID)));
  break;   break;
     case "createlink":      case "createlink":
  if (HTMLArea.is_ie || !UI) {   this._createLink();
  this._doc.execCommand(cmdID, UI, param);  
  } else {  
  // browser is Mozilla & wants UI  
  var param;  
  if ((param = prompt("Enter URL"))) {  
  this._doc.execCommand(cmdID, false, param);  
  }  
  }  
  break;   break;
     case "popupeditor":      case "popupeditor":
    // this object will be passed to the newly opened window
    HTMLArea._object = this;
  if (HTMLArea.is_ie) {   if (HTMLArea.is_ie) {
  window.open(this.popupURL("fullscreen.html"), "ha_fullscreen",   //if (confirm(HTMLArea.I18N.msg["IE-sucks-full-screen"]))
     "toolbar=no,location=no,directories=no,status=no,menubar=no," +   {
     "scrollbars=no,resizable=yes,width=640,height=480");   window.open(this.popupURL("fullscreen.html"), "ha_fullscreen",
       "toolbar=no,location=no,directories=no,status=no,menubar=no," +
       "scrollbars=no,resizable=yes,width=640,height=480");
    }
  } else {   } else {
  window.open(this.popupURL("fullscreen.html"), "ha_fullscreen",   window.open(this.popupURL("fullscreen.html"), "ha_fullscreen",
     "toolbar=no,menubar=no,personalbar=no,width=640,height=480," +      "toolbar=no,menubar=no,personalbar=no,width=640,height=480," +
     "scrollbars=no,resizable=yes");      "scrollbars=no,resizable=yes");
  }   }
  // pass this object to the newly opened window   break;
  HTMLArea._object = this;      case "undo":
       case "redo":
    if (this._customUndo)
    this[cmdID]();
    else
    this._doc.execCommand(cmdID, UI, param);
  break;   break;
     case "inserttable": this._insertTable(); break;      case "inserttable": this._insertTable(); break;
     case "insertimage": this._insertImage(); break;      case "insertimage": this._insertImage(); break;
     case "about"    : this._popupDialog("about.html", null, null); break;      case "about"    : this._popupDialog("about.html", null, this); break;
     case "showhelp" : window.open("reference.html", "ha_help"); break;      case "showhelp" : window.open(_editor_url + "reference.html", "ha_help"); break;
   
       case "killword": this._wordClean(); break;
   
       case "cut":
       case "copy":
       case "paste":
    try {
    if (this.config.killWordOnPaste)
    this._wordClean();
    this._doc.execCommand(cmdID, UI, param);
    } catch (e) {
    if (HTMLArea.is_gecko) {
    if (confirm("Unprivileged scripts cannot access Cut/Copy/Paste programatically " +
       "for security reasons.  Click OK to see a technical note at mozilla.org " +
       "which shows you how to allow a script to access the clipboard."))
    window.open("http://mozilla.org/editor/midasdemo/securityprefs.html");
    }
    }
    break;
       case "lefttoright":
       case "righttoleft":
    var dir = (cmdID == "righttoleft") ? "rtl" : "ltr";
    var el = this.getParentElement();
    while (el && !HTMLArea.isBlockElement(el))
    el = el.parentNode;
    if (el) {
    if (el.style.direction == dir)
    el.style.direction = "";
    else
    el.style.direction = dir;
    }
    break;
     default: this._doc.execCommand(cmdID, UI, param);      default: this._doc.execCommand(cmdID, UI, param);
  }   }
  this.updateToolbar();   this.updateToolbar();
Line 1222  HTMLArea.prototype.execCommand = functio Line 1641  HTMLArea.prototype.execCommand = functio
 HTMLArea.prototype._editorEvent = function(ev) {  HTMLArea.prototype._editorEvent = function(ev) {
  var editor = this;   var editor = this;
  var keyEvent = (HTMLArea.is_ie && ev.type == "keydown") || (ev.type == "keypress");   var keyEvent = (HTMLArea.is_ie && ev.type == "keydown") || (ev.type == "keypress");
    if (keyEvent) {
    for (var i in editor.plugins) {
    var plugin = editor.plugins[i].instance;
    if (typeof plugin.onKeyPress == "function") plugin.onKeyPress(ev);
    }
    }
  if (keyEvent && ev.ctrlKey) {   if (keyEvent && ev.ctrlKey) {
  var sel = null;   var sel = null;
  var range = null;   var range = null;
Line 1251  HTMLArea.prototype._editorEvent = functi Line 1676  HTMLArea.prototype._editorEvent = functi
     case 'e': cmd = "justifycenter"; break;      case 'e': cmd = "justifycenter"; break;
     case 'r': cmd = "justifyright"; break;      case 'r': cmd = "justifyright"; break;
     case 'j': cmd = "justifyfull"; break;      case 'j': cmd = "justifyfull"; break;
       case 'z': cmd = "undo"; break;
       case 'y': cmd = "redo"; break;
       case 'v': cmd = "paste"; break;
   
       case '0': cmd = "killword"; break;
   
  // headings   // headings
     case '1':      case '1':
Line 1298  HTMLArea.prototype._editorEvent = functi Line 1728  HTMLArea.prototype._editorEvent = functi
 // retrieve the HTML  // retrieve the HTML
 HTMLArea.prototype.getHTML = function() {  HTMLArea.prototype.getHTML = function() {
  switch (this._editMode) {   switch (this._editMode) {
     case "wysiwyg"  : return HTMLArea.getHTML(this._doc.body, false);      case "wysiwyg"  :
    if (!this.config.fullPage) {
    return HTMLArea.getHTML(this._doc.body, false, this);
    } else
    return this.doctype + "\n" + HTMLArea.getHTML(this._doc.documentElement, true, this);
     case "textmode" : return this._textArea.value;      case "textmode" : return this._textArea.value;
     default    : alert("Mode <" + mode + "> not defined!");      default    : alert("Mode <" + mode + "> not defined!");
  }   }
Line 1308  HTMLArea.prototype.getHTML = function() Line 1742  HTMLArea.prototype.getHTML = function()
 // retrieve the HTML (fastest version, but uses innerHTML)  // retrieve the HTML (fastest version, but uses innerHTML)
 HTMLArea.prototype.getInnerHTML = function() {  HTMLArea.prototype.getInnerHTML = function() {
  switch (this._editMode) {   switch (this._editMode) {
     case "wysiwyg"  : return this._doc.body.innerHTML;      case "wysiwyg"  :
    if (!this.config.fullPage)
    return this._doc.body.innerHTML;
    else
    return this.doctype + "\n" + this._doc.documentElement.innerHTML;
     case "textmode" : return this._textArea.value;      case "textmode" : return this._textArea.value;
     default    : alert("Mode <" + mode + "> not defined!");      default    : alert("Mode <" + mode + "> not defined!");
  }   }
Line 1318  HTMLArea.prototype.getInnerHTML = functi Line 1756  HTMLArea.prototype.getInnerHTML = functi
 // completely change the HTML inside  // completely change the HTML inside
 HTMLArea.prototype.setHTML = function(html) {  HTMLArea.prototype.setHTML = function(html) {
  switch (this._editMode) {   switch (this._editMode) {
     case "wysiwyg"  : this._doc.body.innerHTML = html; break;      case "wysiwyg"  :
    if (!this.config.fullPage)
    this._doc.body.innerHTML = html;
    else
    // this._doc.documentElement.innerHTML = html;
    this._doc.body.innerHTML = html;
    break;
     case "textmode" : this._textArea.value = html; break;      case "textmode" : this._textArea.value = html; break;
     default    : alert("Mode <" + mode + "> not defined!");      default    : alert("Mode <" + mode + "> not defined!");
  }   }
  return false;   return false;
 };  };
   
   // sets the given doctype (useful when config.fullPage is true)
   HTMLArea.prototype.setDoctype = function(doctype) {
    this.doctype = doctype;
   };
   
 /***************************************************  /***************************************************
  *  Category: UTILITY FUNCTIONS   *  Category: UTILITY FUNCTIONS
  ***************************************************/   ***************************************************/
Line 1342  HTMLArea.is_gecko  = (navigator.product Line 1791  HTMLArea.is_gecko  = (navigator.product
 // variable used to pass the object to the popup editor window.  // variable used to pass the object to the popup editor window.
 HTMLArea._object = null;  HTMLArea._object = null;
   
   // function that returns a clone of the given object
   HTMLArea.cloneObject = function(obj) {
    var newObj = new Object;
   
    // check for array objects
    if (obj.constructor.toString().indexOf("function Array(") == 1) {
    newObj = obj.constructor();
    }
   
    // check for function objects (as usual, IE is fucked up)
    if (obj.constructor.toString().indexOf("function Function(") == 1) {
    newObj = obj; // just copy reference to it
    } else for (var n in obj) {
    var node = obj[n];
    if (typeof node == 'object') { newObj[n] = HTMLArea.cloneObject(node); }
    else                         { newObj[n] = node; }
    }
   
    return newObj;
   };
   
 // FIXME!!! this should return false for IE < 5.5  // FIXME!!! this should return false for IE < 5.5
 HTMLArea.checkSupportedBrowser = function() {  HTMLArea.checkSupportedBrowser = function() {
  if (HTMLArea.is_gecko) {   if (HTMLArea.is_gecko) {
  if (navigator.productSub < 20021201) {   if (navigator.productSub < 20021201) {
  window.status="You need at least Mozilla-1.3 Alpha. " +   alert("You need at least Mozilla-1.3 Alpha.\n" +
       "Sorry, your Gecko is not supported.";        "Sorry, your Gecko is not supported.");
  return false;   return false;
  }   }
  if (navigator.productSub < 20030210) {   if (navigator.productSub < 20030210) {
  window.status="Mozilla < 1.3 Beta is not supported! " +   alert("Mozilla < 1.3 Beta is not supported!\n" +
       "I'll try, though, but it might not work.";        "I'll try, though, but it might not work.");
  }   }
  }   }
  return HTMLArea.is_gecko || HTMLArea.is_ie;   return HTMLArea.is_gecko || HTMLArea.is_ie;
Line 1376  HTMLArea.prototype._createRange = functi Line 1846  HTMLArea.prototype._createRange = functi
  } else {   } else {
  this.focusEditor();   this.focusEditor();
  if (typeof sel != "undefined") {   if (typeof sel != "undefined") {
  return sel.getRangeAt(0);   try {
    return sel.getRangeAt(0);
    } catch(e) {
    return this._doc.createRange();
    }
  } else {   } else {
  return this._doc.createRange();   return this._doc.createRange();
  }   }
Line 1464  HTMLArea.isBlockElement = function(el) { Line 1938  HTMLArea.isBlockElement = function(el) {
 };  };
   
 HTMLArea.needsClosingTag = function(el) {  HTMLArea.needsClosingTag = function(el) {
  var closingTags = " script style div span tr td tbody table em strong font a ";   var closingTags = " head script style div span tr td tbody table em strong font a title ";
  return (closingTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1);   return (closingTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1);
 };  };
   
Line 1482  HTMLArea.htmlEncode = function(str) { Line 1956  HTMLArea.htmlEncode = function(str) {
   
 // Retrieves the HTML code from the given node. This is a replacement for  // Retrieves the HTML code from the given node. This is a replacement for
 // getting innerHTML, using standard DOM calls.  // getting innerHTML, using standard DOM calls.
 HTMLArea.getHTML = function(root, outputRoot) {  HTMLArea.getHTML = function(root, outputRoot, editor) {
  var html = "";   var html = "";
  switch (root.nodeType) {   switch (root.nodeType) {
     case 1: // Node.ELEMENT_NODE      case 1: // Node.ELEMENT_NODE
     case 11: // Node.DOCUMENT_FRAGMENT_NODE      case 11: // Node.DOCUMENT_FRAGMENT_NODE
  var closed;   var closed;
  var i;   var i;
  if (outputRoot) {   var root_tag = (root.nodeType == 1) ? root.tagName.toLowerCase() : '';
    if (HTMLArea.is_ie && root_tag == "head") {
    if (outputRoot)
    html += "<head>";
    // lowercasize
    var save_multiline = RegExp.multiline;
    RegExp.multiline = true;
    var txt = root.innerHTML.replace(HTMLArea.RE_tagName, function(str, p1, p2) {
    return p1 + p2.toLowerCase();
    });
    RegExp.multiline = save_multiline;
    html += txt;
    if (outputRoot)
    html += "</head>";
    break;
    } else if (outputRoot) {
  closed = (!(root.hasChildNodes() || HTMLArea.needsClosingTag(root)));   closed = (!(root.hasChildNodes() || HTMLArea.needsClosingTag(root)));
  html = "<" + root.tagName.toLowerCase();   html = "<" + root.tagName.toLowerCase();
  var attrs = root.attributes;   var attrs = root.attributes;
Line 1499  HTMLArea.getHTML = function(root, output Line 1988  HTMLArea.getHTML = function(root, output
  continue;   continue;
  }   }
  var name = a.nodeName.toLowerCase();   var name = a.nodeName.toLowerCase();
  if (/_moz/.test(name)) {   if (/_moz|contenteditable|_msh/.test(name)) {
  // Mozilla reports some special tags   // avoid certain attributes
  // here; we don't need them.  
  continue;   continue;
  }   }
  var value;   var value;
Line 1513  HTMLArea.getHTML = function(root, output Line 2001  HTMLArea.getHTML = function(root, output
  // I'm starting to HATE JavaScript   // I'm starting to HATE JavaScript
  // development.  Browser differences   // development.  Browser differences
  // suck.   // suck.
  if (typeof root[a.nodeName] != "undefined") {   //
    // Using Gecko the values of href and src are converted to absolute links
    // unless we get them using nodeValue()
    if (typeof root[a.nodeName] != "undefined" && name != "href" && name != "src") {
  value = root[a.nodeName];   value = root[a.nodeName];
  } else {   } else {
  value = a.nodeValue;   value = a.nodeValue;
    // IE seems not willing to return the original values - it converts to absolute
    // links using a.nodeValue, a.value, a.stringValue, root.getAttribute("href")
    // So we have to strip the baseurl manually -/
    if (HTMLArea.is_ie && (name == "href" || name == "src")) {
    value = editor.stripBaseURL(value);
    }
  }   }
  } else { // IE fails to put style in attributes list   } else { // IE fails to put style in attributes list
  // FIXME: cssText reported by IE is UPPERCASE   // FIXME: cssText reported by IE is UPPERCASE
  value = root.style.cssText;   value = root.style.cssText;
  }   }
  if (/_moz/.test(value)) {   if (/(_moz|^$)/.test(value)) {
  // Mozilla reports some special tags   // Mozilla reports some special tags
  // here; we don't need them.   // here; we don't need them.
  continue;   continue;
Line 1532  HTMLArea.getHTML = function(root, output Line 2029  HTMLArea.getHTML = function(root, output
  html += closed ? " />" : ">";   html += closed ? " />" : ">";
  }   }
  for (i = root.firstChild; i; i = i.nextSibling) {   for (i = root.firstChild; i; i = i.nextSibling) {
  html += HTMLArea.getHTML(i, true);   html += HTMLArea.getHTML(i, true, editor);
  }   }
  if (outputRoot && !closed) {   if (outputRoot && !closed) {
  html += "</" + root.tagName.toLowerCase() + ">";   html += "</" + root.tagName.toLowerCase() + ">";
  }   }
  break;   break;
     case 3: // Node.TEXT_NODE      case 3: // Node.TEXT_NODE
  html = HTMLArea.htmlEncode(root.data);   // If a text node is alone in an element and all spaces, replace it with an non breaking one
    // This partially undoes the damage done by moz, which translates '&nbsp;'s into spaces in the data element
    if ( !root.previousSibling && !root.nextSibling && root.data.match(/^\s*$/i) ) html = '&nbsp;';
    else html = HTMLArea.htmlEncode(root.data);
  break;   break;
     case 8: // Node.COMMENT_NODE      case 8: // Node.COMMENT_NODE
  html = "<!--" + root.data + "-->";   html = "<!--" + root.data + "-->";
Line 1548  HTMLArea.getHTML = function(root, output Line 2048  HTMLArea.getHTML = function(root, output
  return html;   return html;
 };  };
   
   HTMLArea.prototype.stripBaseURL = function(string) {
    var baseurl = this.config.baseURL;
   
    // strip to last directory in case baseurl points to a file
    baseurl = baseurl.replace(/[^\/]+$/, '');
    var basere = new RegExp(baseurl);
    string = string.replace(basere, "");
   
    // strip host-part of URL which is added by MSIE to links relative to server root
    baseurl = baseurl.replace(/^(https?:\/\/[^\/]+)(.*)$/, '$1');
    basere = new RegExp(baseurl);
    return string.replace(basere, "");
   };
   
   String.prototype.trim = function() {
    a = this.replace(/^\s+/, '');
    return a.replace(/\s+$/, '');
   };
   
 // creates a rgb-style color from a number  // creates a rgb-style color from a number
 HTMLArea._makeColor = function(v) {  HTMLArea._makeColor = function(v) {
  if (typeof v != "number") {   if (typeof v != "number") {
Line 1563  HTMLArea._makeColor = function(v) { Line 2082  HTMLArea._makeColor = function(v) {
   
 // returns hexadecimal color representation from a number or a rgb-style color.  // returns hexadecimal color representation from a number or a rgb-style color.
 HTMLArea._colorToRgb = function(v) {  HTMLArea._colorToRgb = function(v) {
    if (!v)
    return '';
   
  // returns the hex representation of one byte (2 digits)   // returns the hex representation of one byte (2 digits)
  function hex(d) {   function hex(d) {
  return (d < 16) ? ("0" + d.toString(16)) : d.toString(16);   return (d < 16) ? ("0" + d.toString(16)) : d.toString(16);
Line 1590  HTMLArea._colorToRgb = function(v) { Line 2112  HTMLArea._colorToRgb = function(v) {
  return null;   return null;
  }   }
   
  if (v[0] == "#") {   if (v.substr(0, 1) == "#") {
  // already hex rgb (hopefully :D )   // already hex rgb (hopefully :D )
  return v;   return v;
  }   }
Line 1611  HTMLArea.prototype._popupDialog = functi Line 2133  HTMLArea.prototype._popupDialog = functi
 // paths  // paths
   
 HTMLArea.prototype.imgURL = function(file, plugin) {  HTMLArea.prototype.imgURL = function(file, plugin) {
  if (typeof plugin == "undefined") {   if (typeof plugin == "undefined")
  return this.config.editorURL + file;   return _editor_url + file;
  } else {   else
  return this.config.editorURL + "plugins/" + plugin + "/img/" + file;   return _editor_url + "plugins/" + plugin + "/img/" + file;
  }  
 };  };
   
 HTMLArea.prototype.popupURL = function(file) {  HTMLArea.prototype.popupURL = function(file) {
  return this.config.editorURL + this.config.popupURL + file;   var url = "";
    if (file.match(/^plugin:\/\/(.*?)\/(.*)/)) {
    var plugin = RegExp.$1;
    var popup = RegExp.$2;
    if (!/\.html$/.test(popup))
    popup += ".html";
    url = _editor_url + "plugins/" + plugin + "/popups/" + popup;
    } else
    url = _editor_url + this.config.popupURL + file;
    return url;
 };  };
   
   /**
    * FIX: Internet Explorer returns an item having the _name_ equal to the given
    * id, even if it's not having any id.  This way it can return a different form
    * field even if it's not a textarea.  This workarounds the problem by
    * specifically looking to search only elements having a certain tag name.
    */
   HTMLArea.getElementById = function(tag, id) {
    var el, i, objs = document.getElementsByTagName(tag);
    for (i = objs.length; --i >= 0 && (el = objs[i]);)
    if (el.id == id)
    return el;
    return null;
   };
   
   
   
 // EOF  // EOF
 // Local variables: //  // Local variables: //
 // c-basic-offset:8 //  // c-basic-offset:8 //

Removed from v.1.2  
changed lines
  Added in v.1.3


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>