Bug 445146 Move editor/ui from mozilla-central to comm-central, build changes r=KaiRo, move r/sr=Neil
authorMark Banner <bugzilla@standard8.plus.com>
Wed, 03 Sep 2008 14:35:06 +0100
changeset 250 ee719a0502491fc663bda942dcfc52c0825938d3
parent 249 78b3d6c649f71eff41fe3f486c6cc4f4b899fd35
child 251 7550895945478f458eb26e622fdb26fc450e2570
push idunknown
push userunknown
push dateunknown
reviewersKaiRo, move
bugs445146
Bug 445146 Move editor/ui from mozilla-central to comm-central, build changes r=KaiRo, move r/sr=Neil
allmakefiles.sh
editor/ui/Makefile.in
editor/ui/composer.js
editor/ui/composer/content/ComposerCommands.js
editor/ui/composer/content/EditorAllTags.css
editor/ui/composer/content/EditorContent.css
editor/ui/composer/content/EditorContextMenu.js
editor/ui/composer/content/EditorContextMenuOverlay.xul
editor/ui/composer/content/EditorParagraphMarks.css
editor/ui/composer/content/StructBarContextMenu.js
editor/ui/composer/content/composerOverlay.xul
editor/ui/composer/content/editor.js
editor/ui/composer/content/editor.xul
editor/ui/composer/content/editorApplicationOverlay.js
editor/ui/composer/content/editorMailOverlay.xul
editor/ui/composer/content/editorNavigatorOverlay.xul
editor/ui/composer/content/editorOverlay.js
editor/ui/composer/content/editorOverlay.xul
editor/ui/composer/content/editorPrefsOverlay.xul
editor/ui/composer/content/editorSmileyOverlay.xul
editor/ui/composer/content/editorTasksOverlay.xul
editor/ui/composer/content/editorUtilities.js
editor/ui/composer/content/images/bringtofront-disabled.gif
editor/ui/composer/content/images/bringtofront.gif
editor/ui/composer/content/images/sendtoback-disabled.gif
editor/ui/composer/content/images/sendtoback.gif
editor/ui/composer/content/images/tag-a.gif
editor/ui/composer/content/images/tag-abr.gif
editor/ui/composer/content/images/tag-acr.gif
editor/ui/composer/content/images/tag-adr.gif
editor/ui/composer/content/images/tag-anchor.gif
editor/ui/composer/content/images/tag-app.gif
editor/ui/composer/content/images/tag-ara.gif
editor/ui/composer/content/images/tag-b.gif
editor/ui/composer/content/images/tag-bas.gif
editor/ui/composer/content/images/tag-bdo.gif
editor/ui/composer/content/images/tag-big.gif
editor/ui/composer/content/images/tag-blq.gif
editor/ui/composer/content/images/tag-body.gif
editor/ui/composer/content/images/tag-br.gif
editor/ui/composer/content/images/tag-bsf.gif
editor/ui/composer/content/images/tag-btn.gif
editor/ui/composer/content/images/tag-cit.gif
editor/ui/composer/content/images/tag-clg.gif
editor/ui/composer/content/images/tag-cod.gif
editor/ui/composer/content/images/tag-col.gif
editor/ui/composer/content/images/tag-cpt.gif
editor/ui/composer/content/images/tag-ctr.gif
editor/ui/composer/content/images/tag-dd.gif
editor/ui/composer/content/images/tag-del.gif
editor/ui/composer/content/images/tag-dfn.gif
editor/ui/composer/content/images/tag-dir.gif
editor/ui/composer/content/images/tag-div.gif
editor/ui/composer/content/images/tag-dl.gif
editor/ui/composer/content/images/tag-dt.gif
editor/ui/composer/content/images/tag-em.gif
editor/ui/composer/content/images/tag-fld.gif
editor/ui/composer/content/images/tag-fnt.gif
editor/ui/composer/content/images/tag-for.gif
editor/ui/composer/content/images/tag-frm.gif
editor/ui/composer/content/images/tag-fst.gif
editor/ui/composer/content/images/tag-h1.gif
editor/ui/composer/content/images/tag-h2.gif
editor/ui/composer/content/images/tag-h3.gif
editor/ui/composer/content/images/tag-h4.gif
editor/ui/composer/content/images/tag-h5.gif
editor/ui/composer/content/images/tag-h6.gif
editor/ui/composer/content/images/tag-hed.gif
editor/ui/composer/content/images/tag-hr.gif
editor/ui/composer/content/images/tag-html.gif
editor/ui/composer/content/images/tag-i.gif
editor/ui/composer/content/images/tag-ifr.gif
editor/ui/composer/content/images/tag-img.gif
editor/ui/composer/content/images/tag-inp.gif
editor/ui/composer/content/images/tag-ins.gif
editor/ui/composer/content/images/tag-isx.gif
editor/ui/composer/content/images/tag-kbd.gif
editor/ui/composer/content/images/tag-lbl.gif
editor/ui/composer/content/images/tag-lgn.gif
editor/ui/composer/content/images/tag-li.gif
editor/ui/composer/content/images/tag-lnk.gif
editor/ui/composer/content/images/tag-lst.gif
editor/ui/composer/content/images/tag-map.gif
editor/ui/composer/content/images/tag-men.gif
editor/ui/composer/content/images/tag-met.gif
editor/ui/composer/content/images/tag-nbr.gif
editor/ui/composer/content/images/tag-nfr.gif
editor/ui/composer/content/images/tag-nsc.gif
editor/ui/composer/content/images/tag-obj.gif
editor/ui/composer/content/images/tag-ol.gif
editor/ui/composer/content/images/tag-opg.gif
editor/ui/composer/content/images/tag-opt.gif
editor/ui/composer/content/images/tag-p.gif
editor/ui/composer/content/images/tag-pln.gif
editor/ui/composer/content/images/tag-pre.gif
editor/ui/composer/content/images/tag-prm.gif
editor/ui/composer/content/images/tag-q.gif
editor/ui/composer/content/images/tag-s.gif
editor/ui/composer/content/images/tag-scr.gif
editor/ui/composer/content/images/tag-slc.gif
editor/ui/composer/content/images/tag-sml.gif
editor/ui/composer/content/images/tag-smp.gif
editor/ui/composer/content/images/tag-spn.gif
editor/ui/composer/content/images/tag-stk.gif
editor/ui/composer/content/images/tag-stl.gif
editor/ui/composer/content/images/tag-stn.gif
editor/ui/composer/content/images/tag-sub.gif
editor/ui/composer/content/images/tag-sup.gif
editor/ui/composer/content/images/tag-tbd.gif
editor/ui/composer/content/images/tag-tbl.gif
editor/ui/composer/content/images/tag-td.gif
editor/ui/composer/content/images/tag-tft.gif
editor/ui/composer/content/images/tag-th.gif
editor/ui/composer/content/images/tag-thd.gif
editor/ui/composer/content/images/tag-tr.gif
editor/ui/composer/content/images/tag-tt.gif
editor/ui/composer/content/images/tag-ttl.gif
editor/ui/composer/content/images/tag-txt.gif
editor/ui/composer/content/images/tag-u.gif
editor/ui/composer/content/images/tag-ul.gif
editor/ui/composer/content/images/tag-userdefined.gif
editor/ui/composer/content/images/tag-var.gif
editor/ui/composer/content/images/tag-xmp.gif
editor/ui/composer/content/pref-composer.js
editor/ui/composer/content/pref-composer.xul
editor/ui/composer/content/pref-editing.xul
editor/ui/composer/content/pref-toolbars.xul
editor/ui/composer/content/publishprefs.js
editor/ui/dialogs/content/EdAEAttributes.js
editor/ui/dialogs/content/EdAECSSAttributes.js
editor/ui/dialogs/content/EdAEHTMLAttributes.js
editor/ui/dialogs/content/EdAEJSEAttributes.js
editor/ui/dialogs/content/EdAdvancedEdit.js
editor/ui/dialogs/content/EdAdvancedEdit.xul
editor/ui/dialogs/content/EdButtonProps.js
editor/ui/dialogs/content/EdButtonProps.xul
editor/ui/dialogs/content/EdColorPicker.js
editor/ui/dialogs/content/EdColorPicker.xul
editor/ui/dialogs/content/EdColorProps.js
editor/ui/dialogs/content/EdColorProps.xul
editor/ui/dialogs/content/EdConvertToTable.js
editor/ui/dialogs/content/EdConvertToTable.xul
editor/ui/dialogs/content/EdDialogCommon.js
editor/ui/dialogs/content/EdDialogOverlay.xul
editor/ui/dialogs/content/EdDialogTemplate.js
editor/ui/dialogs/content/EdDialogTemplate.xul
editor/ui/dialogs/content/EdDictionary.js
editor/ui/dialogs/content/EdDictionary.xul
editor/ui/dialogs/content/EdFieldSetProps.js
editor/ui/dialogs/content/EdFieldSetProps.xul
editor/ui/dialogs/content/EdFormProps.js
editor/ui/dialogs/content/EdFormProps.xul
editor/ui/dialogs/content/EdHLineProps.js
editor/ui/dialogs/content/EdHLineProps.xul
editor/ui/dialogs/content/EdImageMap.js
editor/ui/dialogs/content/EdImageMap.xul
editor/ui/dialogs/content/EdImageMapHotSpot.js
editor/ui/dialogs/content/EdImageMapHotSpot.xul
editor/ui/dialogs/content/EdImageMapPage.html
editor/ui/dialogs/content/EdImageMapShapes.js
editor/ui/dialogs/content/EdImageOverlay.js
editor/ui/dialogs/content/EdImageOverlay.xul
editor/ui/dialogs/content/EdImageProps.js
editor/ui/dialogs/content/EdImageProps.xul
editor/ui/dialogs/content/EdInputImage.js
editor/ui/dialogs/content/EdInputImage.xul
editor/ui/dialogs/content/EdInputProps.js
editor/ui/dialogs/content/EdInputProps.xul
editor/ui/dialogs/content/EdInsSrc.js
editor/ui/dialogs/content/EdInsSrc.xul
editor/ui/dialogs/content/EdInsertChars.js
editor/ui/dialogs/content/EdInsertChars.xul
editor/ui/dialogs/content/EdInsertTOC.js
editor/ui/dialogs/content/EdInsertTOC.xul
editor/ui/dialogs/content/EdInsertTable.js
editor/ui/dialogs/content/EdInsertTable.xul
editor/ui/dialogs/content/EdLabelProps.js
editor/ui/dialogs/content/EdLabelProps.xul
editor/ui/dialogs/content/EdLinkChecker.js
editor/ui/dialogs/content/EdLinkChecker.xul
editor/ui/dialogs/content/EdLinkProps.js
editor/ui/dialogs/content/EdLinkProps.xul
editor/ui/dialogs/content/EdListProps.js
editor/ui/dialogs/content/EdListProps.xul
editor/ui/dialogs/content/EdNamedAnchorProps.js
editor/ui/dialogs/content/EdNamedAnchorProps.xul
editor/ui/dialogs/content/EdPageProps.js
editor/ui/dialogs/content/EdPageProps.xul
editor/ui/dialogs/content/EdReplace.js
editor/ui/dialogs/content/EdReplace.xul
editor/ui/dialogs/content/EdSelectProps.js
editor/ui/dialogs/content/EdSelectProps.xul
editor/ui/dialogs/content/EdSnapToGrid.js
editor/ui/dialogs/content/EdSnapToGrid.xul
editor/ui/dialogs/content/EdSpellCheck.js
editor/ui/dialogs/content/EdSpellCheck.xul
editor/ui/dialogs/content/EdTableProps.js
editor/ui/dialogs/content/EdTableProps.xul
editor/ui/dialogs/content/EdTextAreaProps.js
editor/ui/dialogs/content/EdTextAreaProps.xul
editor/ui/dialogs/content/EditConflict.js
editor/ui/dialogs/content/EditConflict.xul
editor/ui/dialogs/content/EditorPublish.js
editor/ui/dialogs/content/EditorPublish.xul
editor/ui/dialogs/content/EditorPublishOverlay.xul
editor/ui/dialogs/content/EditorPublishProgress.js
editor/ui/dialogs/content/EditorPublishProgress.xul
editor/ui/dialogs/content/EditorPublishSettings.js
editor/ui/dialogs/content/EditorPublishSettings.xul
editor/ui/dialogs/content/EditorSaveAsCharset.js
editor/ui/dialogs/content/EditorSaveAsCharset.xul
editor/ui/jar.mn
editor/ui/locales/Makefile.in
editor/ui/locales/en-US/chrome/composer/editor.dtd
editor/ui/locales/en-US/chrome/composer/editor.properties
editor/ui/locales/en-US/chrome/composer/editorNavigatorOverlay.dtd
editor/ui/locales/en-US/chrome/composer/editorOverlay.dtd
editor/ui/locales/en-US/chrome/composer/editorPrefsOverlay.dtd
editor/ui/locales/en-US/chrome/composer/editorSmileyOverlay.dtd
editor/ui/locales/en-US/chrome/composer/pref-composer.dtd
editor/ui/locales/en-US/chrome/composer/pref-editing.dtd
editor/ui/locales/en-US/chrome/composer/pref-toolbars.dtd
editor/ui/locales/en-US/chrome/dialogs/EdAdvancedEdit.dtd
editor/ui/locales/en-US/chrome/dialogs/EdColorPicker.dtd
editor/ui/locales/en-US/chrome/dialogs/EdConvertToTable.dtd
editor/ui/locales/en-US/chrome/dialogs/EdDialogOverlay.dtd
editor/ui/locales/en-US/chrome/dialogs/EdLinkChecker.dtd
editor/ui/locales/en-US/chrome/dialogs/EdNamedAnchorProperties.dtd
editor/ui/locales/en-US/chrome/dialogs/EditConflict.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorButtonProperties.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorColorProperties.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorFieldSetProperties.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorFormProperties.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorHLineProperties.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorImageMap.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorImageMapHotSpot.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorImageProperties.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorInputProperties.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorInsertChars.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorInsertSource.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorInsertTOC.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorInsertTable.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorLabelProperties.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorLinkProperties.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorListProperties.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorPageProperties.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorPersonalDictionary.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorPublish.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorPublishProgress.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorReplace.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorSaveAsCharset.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorSelectProperties.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorSnapToGrid.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorSpellCheck.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorTableProperties.dtd
editor/ui/locales/en-US/chrome/dialogs/EditorTextAreaProperties.dtd
editor/ui/locales/en-US/chrome/region/region.properties
editor/ui/locales/jar.mn
editor/ui/makefiles.sh
editor/ui/nsComposerCmdLineHandler.js
mail/build.mk
mail/locales/Makefile.in
suite/build.mk
suite/locales/Makefile.in
--- a/allmakefiles.sh
+++ b/allmakefiles.sh
@@ -59,16 +59,20 @@ add_makefiles "
 Makefile
 config/autoconf.mk
 "
 
 if [ "$MOZ_LDAP_XPCOM" ]; then
   . "${srcdir}/directory/makefiles.sh"
 fi
 
+if [ "$MOZ_COMPOSER" ]; then
+  . "${srcdir}/editor/ui/makefiles.sh"
+fi
+
 if [ "$MOZ_MAIL_NEWS" ]; then
   . "${srcdir}/mailnews/makefiles.sh"
 fi
 
 if [ "$MOZ_CALENDAR" ]; then
   . "${srcdir}/calendar/shared_makefiles.sh"
   . "${srcdir}/calendar/lightning/makefiles.sh"
 fi
new file mode 100644
--- /dev/null
+++ b/editor/ui/Makefile.in
@@ -0,0 +1,54 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications.
+# Portions created by the Initial Developer are Copyright (C) 2001
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#  Brian Ryner <bryner@brianryner.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+DIRS           = \
+               locales \
+               $(NULL)
+
+ifndef MOZ_STANDALONE_COMPOSER
+PREF_JS_EXPORTS = $(srcdir)/composer.js
+EXTRA_COMPONENTS = nsComposerCmdLineHandler.js
+endif
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/editor/ui/composer.js
@@ -0,0 +1,138 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Benjamin Smedberg <bsmedberg@covad.net>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Default preferences for seamonkey composer. This file
+ * was copied from mozilla/modules/libpref/src/init/editor.js
+ *
+ * If you're looking for the default prefs of standalone
+ * composer, see mozilla/composer/app/profile/all.js
+ */
+
+pref("editor.author",                       "");
+
+pref("editor.text_color",                   "#000000");
+pref("editor.link_color",                   "#0000FF");
+pref("editor.active_link_color",            "#000088");
+pref("editor.followed_link_color",          "#FF0000");
+pref("editor.background_color",             "#FFFFFF");
+pref("editor.use_background_image",         false);
+pref("editor.default_background_image",     "");
+pref("editor.use_custom_default_colors", 1);
+
+pref("editor.hrule.height",                 2);
+pref("editor.hrule.width",                  100);
+pref("editor.hrule.width_percent",          true);
+pref("editor.hrule.shading",                true);
+pref("editor.hrule.align",                  1); // center
+
+pref("editor.table.maintain_structure", true);
+
+pref("editor.prettyprint", true);
+
+pref("editor.throbber.url","chrome://editor-region/locale/region.properties");
+
+pref("editor.toolbars.showbutton.new", true);
+pref("editor.toolbars.showbutton.open", true);
+pref("editor.toolbars.showbutton.save", true);
+pref("editor.toolbars.showbutton.publish", true);
+pref("editor.toolbars.showbutton.preview", true);
+pref("editor.toolbars.showbutton.cut", false);
+pref("editor.toolbars.showbutton.copy", false);
+pref("editor.toolbars.showbutton.paste", false);
+pref("editor.toolbars.showbutton.print", true);
+pref("editor.toolbars.showbutton.find", false);
+pref("editor.toolbars.showbutton.image", true);
+pref("editor.toolbars.showbutton.hline", false);
+pref("editor.toolbars.showbutton.table", true);
+pref("editor.toolbars.showbutton.link", true);
+pref("editor.toolbars.showbutton.namedAnchor", false);
+
+pref("editor.toolbars.showbutton.bold", true);
+pref("editor.toolbars.showbutton.italic", true);
+pref("editor.toolbars.showbutton.underline", true);
+pref("editor.toolbars.showbutton.DecreaseFontSize", true);
+pref("editor.toolbars.showbutton.IncreaseFontSize", true);
+pref("editor.toolbars.showbutton.ul", true);
+pref("editor.toolbars.showbutton.ol", true);
+pref("editor.toolbars.showbutton.outdent", true);
+pref("editor.toolbars.showbutton.indent", true);
+
+pref("editor.toolbars.showbutton.absolutePosition", true);
+pref("editor.toolbars.showbutton.decreaseZIndex", true);
+pref("editor.toolbars.showbutton.increaseZIndex", true);
+
+pref("editor.history.url_maximum", 10);
+
+pref("editor.publish.",                      "");
+pref("editor.lastFileLocation.image",        "");
+pref("editor.lastFileLocation.html",         "");
+pref("editor.save_associated_files",         true);
+pref("editor.always_show_publish_dialog",    false);
+
+/*
+ * What are the entities that you want Mozilla to save using mnemonic
+ * names rather than numeric codes? E.g. If set, we'll output &nbsp;
+ * otherwise, we may output 0xa0 depending on the charset.
+ *
+ * "none"   : don't use any entity names; only use numeric codes.
+ * "basic"  : use entity names just for &nbsp; &amp; &lt; &gt; &quot; for 
+ *            interoperability/exchange with products that don't support more
+ *            than that.
+ * "latin1" : use entity names for 8bit accented letters and other special
+ *            symbols between 128 and 255.
+ * "html"   : use entity names for 8bit accented letters, greek letters, and
+ *            other special markup symbols as defined in HTML4.
+ */
+//pref("editor.encode_entity",                 "html");
+
+#ifndef XP_MACOSX
+#ifdef XP_UNIX
+pref("editor.disable_spell_checker", false);
+pref("editor.dont_lock_spell_files", true);
+#endif
+#endif
+
+pref("editor.CR_creates_new_p",      false);
+
+// Pasting images from the clipboard, order of encoding preference: 
+// JPEG-PNG-GIF=0, PNG-JPEG-GIF=1, GIF-JPEG-PNG=2
+pref("clipboard.paste_image_type", 1);
+#ifdef XP_WIN
+pref("clipboard.paste_image_quality", 92); // for JPEG on Windows only
+#endif
new file mode 100644
--- /dev/null
+++ b/editor/ui/composer/content/ComposerCommands.js
@@ -0,0 +1,3956 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Simon Fraser (sfraser@netscape.com)
+ *   Ryan Cassin (rcassin@supernova.org)
+ *   Kathleen Brade (brade@netscape.com)
+ *   Daniel Glazman (glazman@netscape.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* Implementations of nsIControllerCommand for composer commands */
+
+var gComposerJSCommandControllerID = 0;
+
+
+//-----------------------------------------------------------------------------------
+function SetupHTMLEditorCommands()
+{
+  var commandTable = GetComposerCommandTable();
+  if (!commandTable)
+    return;
+  
+  // Include everthing a text editor does
+  SetupTextEditorCommands();
+
+  //dump("Registering HTML editor commands\n");
+
+  commandTable.registerCommand("cmd_renderedHTMLEnabler", nsDummyHTMLCommand);
+
+  commandTable.registerCommand("cmd_grid",  nsGridCommand);
+
+  commandTable.registerCommand("cmd_listProperties",  nsListPropertiesCommand);
+  commandTable.registerCommand("cmd_pageProperties",  nsPagePropertiesCommand);
+  commandTable.registerCommand("cmd_colorProperties", nsColorPropertiesCommand);
+  commandTable.registerCommand("cmd_advancedProperties", nsAdvancedPropertiesCommand);
+  commandTable.registerCommand("cmd_objectProperties",   nsObjectPropertiesCommand);
+  commandTable.registerCommand("cmd_removeNamedAnchors", nsRemoveNamedAnchorsCommand);
+  commandTable.registerCommand("cmd_editLink",        nsEditLinkCommand);
+  
+  commandTable.registerCommand("cmd_form",          nsFormCommand);
+  commandTable.registerCommand("cmd_inputtag",      nsInputTagCommand);
+  commandTable.registerCommand("cmd_inputimage",    nsInputImageCommand);
+  commandTable.registerCommand("cmd_textarea",      nsTextAreaCommand);
+  commandTable.registerCommand("cmd_select",        nsSelectCommand);
+  commandTable.registerCommand("cmd_button",        nsButtonCommand);
+  commandTable.registerCommand("cmd_label",         nsLabelCommand);
+  commandTable.registerCommand("cmd_fieldset",      nsFieldSetCommand);
+  commandTable.registerCommand("cmd_isindex",       nsIsIndexCommand);
+  commandTable.registerCommand("cmd_image",         nsImageCommand);
+  commandTable.registerCommand("cmd_hline",         nsHLineCommand);
+  commandTable.registerCommand("cmd_link",          nsLinkCommand);
+  commandTable.registerCommand("cmd_anchor",        nsAnchorCommand);
+  commandTable.registerCommand("cmd_insertHTMLWithDialog", nsInsertHTMLWithDialogCommand);
+  commandTable.registerCommand("cmd_insertBreak",   nsInsertBreakCommand);
+  commandTable.registerCommand("cmd_insertBreakAll",nsInsertBreakAllCommand);
+
+  commandTable.registerCommand("cmd_table",              nsInsertOrEditTableCommand);
+  commandTable.registerCommand("cmd_editTable",          nsEditTableCommand);
+  commandTable.registerCommand("cmd_SelectTable",        nsSelectTableCommand);
+  commandTable.registerCommand("cmd_SelectRow",          nsSelectTableRowCommand);
+  commandTable.registerCommand("cmd_SelectColumn",       nsSelectTableColumnCommand);
+  commandTable.registerCommand("cmd_SelectCell",         nsSelectTableCellCommand);
+  commandTable.registerCommand("cmd_SelectAllCells",     nsSelectAllTableCellsCommand);
+  commandTable.registerCommand("cmd_InsertTable",        nsInsertTableCommand);
+  commandTable.registerCommand("cmd_InsertRowAbove",     nsInsertTableRowAboveCommand);
+  commandTable.registerCommand("cmd_InsertRowBelow",     nsInsertTableRowBelowCommand);
+  commandTable.registerCommand("cmd_InsertColumnBefore", nsInsertTableColumnBeforeCommand);
+  commandTable.registerCommand("cmd_InsertColumnAfter",  nsInsertTableColumnAfterCommand);
+  commandTable.registerCommand("cmd_InsertCellBefore",   nsInsertTableCellBeforeCommand);
+  commandTable.registerCommand("cmd_InsertCellAfter",    nsInsertTableCellAfterCommand);
+  commandTable.registerCommand("cmd_DeleteTable",        nsDeleteTableCommand);
+  commandTable.registerCommand("cmd_DeleteRow",          nsDeleteTableRowCommand);
+  commandTable.registerCommand("cmd_DeleteColumn",       nsDeleteTableColumnCommand);
+  commandTable.registerCommand("cmd_DeleteCell",         nsDeleteTableCellCommand);
+  commandTable.registerCommand("cmd_DeleteCellContents", nsDeleteTableCellContentsCommand);
+  commandTable.registerCommand("cmd_JoinTableCells",     nsJoinTableCellsCommand);
+  commandTable.registerCommand("cmd_SplitTableCell",     nsSplitTableCellCommand);
+  commandTable.registerCommand("cmd_TableOrCellColor",   nsTableOrCellColorCommand);
+  commandTable.registerCommand("cmd_NormalizeTable",     nsNormalizeTableCommand);
+  commandTable.registerCommand("cmd_smiley",             nsSetSmiley);
+  commandTable.registerCommand("cmd_ConvertToTable",     nsConvertToTable);
+}
+
+function SetupTextEditorCommands()
+{
+  var commandTable = GetComposerCommandTable();
+  if (!commandTable)
+    return;
+  
+  //dump("Registering plain text editor commands\n");
+  
+  commandTable.registerCommand("cmd_find",       nsFindCommand);
+  commandTable.registerCommand("cmd_findNext",   nsFindAgainCommand);
+  commandTable.registerCommand("cmd_findPrev",   nsFindAgainCommand);
+  commandTable.registerCommand("cmd_rewrap",     nsRewrapCommand);
+  commandTable.registerCommand("cmd_spelling",   nsSpellingCommand);
+  commandTable.registerCommand("cmd_validate",   nsValidateCommand);
+  commandTable.registerCommand("cmd_checkLinks", nsCheckLinksCommand);
+  commandTable.registerCommand("cmd_insertChars", nsInsertCharsCommand);
+}
+
+function SetupComposerWindowCommands()
+{
+  // Don't need to do this if already done
+  if (gComposerWindowControllerID)
+    return;
+
+  // Create a command controller and register commands
+  //   specific to Web Composer window (file-related commands, HTML Source...)
+  //   We can't use the composer controller created on the content window else
+  //     we can't process commands when in HTMLSource editor
+  // IMPORTANT: For each of these commands, the doCommand method 
+  //            must first call FinishHTMLSource() 
+  //            to go from HTML Source mode to any other edit mode
+
+  var windowControllers = window.controllers;
+
+  if (!windowControllers) return;
+
+  var commandTable;
+  var composerController;
+  var editorController;
+  try {
+    composerController = Components.classes["@mozilla.org/embedcomp/base-command-controller;1"].createInstance();
+
+    editorController = composerController.QueryInterface(Components.interfaces.nsIControllerContext);
+    editorController.init(null); // init it without passing in a command table
+
+    // Get the nsIControllerCommandTable interface we need to register commands
+    var interfaceRequestor = composerController.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
+    commandTable = interfaceRequestor.getInterface(Components.interfaces.nsIControllerCommandTable);
+  }
+  catch (e)
+  {
+    dump("Failed to create composerController\n");
+    return;
+  }
+
+
+  if (!commandTable)
+  {
+    dump("Failed to get interface for nsIControllerCommandManager\n");
+    return;
+  }
+
+  // File-related commands
+  commandTable.registerCommand("cmd_open",           nsOpenCommand);
+  commandTable.registerCommand("cmd_save",           nsSaveCommand);
+  commandTable.registerCommand("cmd_saveAs",         nsSaveAsCommand);
+  commandTable.registerCommand("cmd_exportToText",   nsExportToTextCommand);
+  commandTable.registerCommand("cmd_saveAndChangeEncoding",  nsSaveAndChangeEncodingCommand);
+  commandTable.registerCommand("cmd_publish",        nsPublishCommand);
+  commandTable.registerCommand("cmd_publishAs",      nsPublishAsCommand);
+  commandTable.registerCommand("cmd_publishSettings",nsPublishSettingsCommand);
+  commandTable.registerCommand("cmd_revert",         nsRevertCommand);
+  commandTable.registerCommand("cmd_openRemote",     nsOpenRemoteCommand);
+  commandTable.registerCommand("cmd_preview",        nsPreviewCommand);
+  commandTable.registerCommand("cmd_editSendPage",   nsSendPageCommand);
+  commandTable.registerCommand("cmd_print",          nsPrintCommand);
+  commandTable.registerCommand("cmd_printSetup",     nsPrintSetupCommand);
+  commandTable.registerCommand("cmd_quit",           nsQuitCommand);
+  commandTable.registerCommand("cmd_close",          nsCloseCommand);
+  commandTable.registerCommand("cmd_preferences",    nsPreferencesCommand);
+
+  // Edit Mode commands
+  if (GetCurrentEditorType() == "html")
+  {
+    commandTable.registerCommand("cmd_NormalMode",         nsNormalModeCommand);
+    commandTable.registerCommand("cmd_AllTagsMode",        nsAllTagsModeCommand);
+    commandTable.registerCommand("cmd_HTMLSourceMode",     nsHTMLSourceModeCommand);
+    commandTable.registerCommand("cmd_PreviewMode",        nsPreviewModeCommand);
+    commandTable.registerCommand("cmd_FinishHTMLSource",   nsFinishHTMLSource);
+    commandTable.registerCommand("cmd_CancelHTMLSource",   nsCancelHTMLSource);
+    commandTable.registerCommand("cmd_updateStructToolbar", nsUpdateStructToolbarCommand);
+  }
+
+  windowControllers.insertControllerAt(0, editorController);
+
+  // Store the controller ID so we can be sure to get the right one later
+  gComposerWindowControllerID = windowControllers.getControllerId(editorController);
+}
+
+//-----------------------------------------------------------------------------------
+function GetComposerCommandTable()
+{
+  var controller;
+  if (gComposerJSCommandControllerID)
+  {
+    try { 
+      controller = window.content.controllers.getControllerById(gComposerJSCommandControllerID);
+    } catch (e) {}
+  }
+  if (!controller)
+  {
+    //create it
+    controller = Components.classes["@mozilla.org/embedcomp/base-command-controller;1"].createInstance();
+
+    var editorController = controller.QueryInterface(Components.interfaces.nsIControllerContext);
+    editorController.init(null);
+    editorController.setCommandContext(GetCurrentEditorElement());
+    window.content.controllers.insertControllerAt(0, controller);
+  
+    // Store the controller ID so we can be sure to get the right one later
+    gComposerJSCommandControllerID = window.content.controllers.getControllerId(controller);
+  }
+
+  if (controller)
+  {
+    var interfaceRequestor = controller.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
+    return interfaceRequestor.getInterface(Components.interfaces.nsIControllerCommandTable);
+  }
+  return null;
+}
+
+//-----------------------------------------------------------------------------------
+function goUpdateCommandState(command)
+{
+  try
+  {
+    var controller = top.document.commandDispatcher.getControllerForCommand(command);
+    if (!(controller instanceof Components.interfaces.nsICommandController))
+      return;
+
+    var params = newCommandParams();
+    if (!params) return;
+
+    controller.getCommandStateWithParams(command, params);
+
+    switch (command)
+    {
+      case "cmd_bold":
+      case "cmd_italic":
+      case "cmd_underline":
+      case "cmd_var":
+      case "cmd_samp":
+      case "cmd_code":
+      case "cmd_acronym":
+      case "cmd_abbr":
+      case "cmd_cite":
+      case "cmd_strong":
+      case "cmd_em":
+      case "cmd_superscript":
+      case "cmd_subscript":
+      case "cmd_strikethrough":
+      case "cmd_tt":
+      case "cmd_nobreak":
+      case "cmd_ul":
+      case "cmd_ol":
+        pokeStyleUI(command, params.getBooleanValue("state_all"));
+        break;
+
+      case "cmd_paragraphState":
+      case "cmd_align":
+      case "cmd_highlight":
+      case "cmd_backgroundColor":
+      case "cmd_fontColor":
+      case "cmd_fontFace":
+      case "cmd_fontSize":
+      case "cmd_absPos":
+        pokeMultiStateUI(command, params);
+        break;
+
+      case "cmd_decreaseZIndex":
+      case "cmd_increaseZIndex":
+      case "cmd_indent":
+      case "cmd_outdent":
+      case "cmd_increaseFont":
+      case "cmd_decreaseFont":
+      case "cmd_removeStyles":
+      case "cmd_smiley":
+        break;
+
+      default: dump("no update for command: " +command+"\n");
+    }
+  }
+  catch (e) { dump("An error occurred updating the "+command+" command: \n"+e+"\n"); }
+}
+
+function goUpdateComposerMenuItems(commandset)
+{
+  //dump("Updating commands for " + commandset.id + "\n");
+
+  for (var i = 0; i < commandset.childNodes.length; i++)
+  {
+    var commandNode = commandset.childNodes[i];
+    var commandID = commandNode.id;
+    if (commandID)
+    {
+      goUpdateCommand(commandID);  // enable or disable
+      if (commandNode.hasAttribute("state"))
+        goUpdateCommandState(commandID);
+    }
+  }
+}
+
+//-----------------------------------------------------------------------------------
+function goDoCommandParams(command, params)
+{
+  try
+  {
+    var controller = top.document.commandDispatcher.getControllerForCommand(command);
+    if (controller && controller.isCommandEnabled(command))
+    {
+      if (controller instanceof Components.interfaces.nsICommandController)
+      {
+        controller.doCommandWithParams(command, params);
+
+        // the following two lines should be removed when we implement observers
+        if (params)
+          controller.getCommandStateWithParams(command, params);
+      }
+      else
+      {
+        controller.doCommand(command);
+      }
+      ResetStructToolbar();
+    }
+  }
+  catch (e)
+  {
+    dump("An error occurred executing the "+command+" command\n");
+  }
+}
+
+function pokeStyleUI(uiID, aDesiredState)
+{
+ try {
+  var commandNode = top.document.getElementById(uiID);
+  if (!commandNode)
+    return;
+
+  var uiState = ("true" == commandNode.getAttribute("state"));
+  if (aDesiredState != uiState)
+  {
+    var newState;
+    if (aDesiredState)
+      newState = "true";
+    else
+      newState = "false";
+    commandNode.setAttribute("state", newState);
+  }
+ } catch(e) { dump("poking UI for "+uiID+" failed: "+e+"\n"); }
+}
+
+function doStyleUICommand(cmdStr)
+{
+  try
+  {
+    var cmdParams = newCommandParams();
+    goDoCommandParams(cmdStr, cmdParams);
+    if (cmdParams)
+      pokeStyleUI(cmdStr, cmdParams.getBooleanValue("state_all"));
+
+    ResetStructToolbar();
+  } catch(e) {}
+}
+
+function pokeMultiStateUI(uiID, cmdParams)
+{
+  try
+  {
+    var commandNode = document.getElementById(uiID);
+    if (!commandNode)
+      return;
+
+    var isMixed = cmdParams.getBooleanValue("state_mixed");
+    var desiredAttrib;
+    if (isMixed)
+      desiredAttrib = "mixed";
+    else {
+      var valuetype = cmdParams.getValueType("state_attribute");
+      if (valuetype == Components.interfaces.nsICommandParams.eStringType) {
+        desiredAttrib = cmdParams.getCStringValue("state_attribute");      
+      } else {
+        desiredAttrib = cmdParams.getStringValue("state_attribute");      
+      }
+
+    }
+
+    var uiState = commandNode.getAttribute("state");
+    if (desiredAttrib != uiState)
+    {
+      commandNode.setAttribute("state", desiredAttrib);
+    }
+  } catch(e) {}
+}
+
+function doStatefulCommand(commandID, newState)
+{
+  var commandNode = document.getElementById(commandID);
+  if (commandNode)
+      commandNode.setAttribute("state", newState);
+  gContentWindow.focus();   // needed for command dispatch to work
+
+  try
+  {
+    var cmdParams = newCommandParams();
+    if (!cmdParams) return;
+
+    cmdParams.setStringValue("state_attribute", newState);
+    goDoCommandParams(commandID, cmdParams);
+
+    pokeMultiStateUI(commandID, cmdParams);
+
+    ResetStructToolbar();
+  } catch(e) { dump("error thrown in doStatefulCommand: "+e+"\n"); }
+}
+
+//-----------------------------------------------------------------------------------
+function PrintObject(obj)
+{
+  dump("-----" + obj + "------\n");
+  var names = "";
+  for (var i in obj)
+  {
+    if (i == "value")
+      names += i + ": " + obj.value + "\n";
+    else if (i == "id")
+      names += i + ": " + obj.id + "\n";
+    else
+      names += i + "\n";
+  }
+  
+  dump(names + "-----------\n");
+}
+
+//-----------------------------------------------------------------------------------
+function PrintNodeID(id)
+{
+  PrintObject(document.getElementById(id));
+}
+
+//-----------------------------------------------------------------------------------
+var nsDummyHTMLCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    // do nothing
+    dump("Hey, who's calling the dummy command?\n");
+  }
+
+};
+
+//-----------------------------------------------------------------------------------
+var nsOpenCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return true;    // we can always do this
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
+    fp.init(window, GetString("OpenHTMLFile"), nsIFilePicker.modeOpen);
+
+    SetFilePickerDirectory(fp, "html");
+
+    // When loading into Composer, direct user to prefer HTML files and text files,
+    //   so we call separately to control the order of the filter list
+    fp.appendFilters(nsIFilePicker.filterHTML);
+    fp.appendFilters(nsIFilePicker.filterText);
+    fp.appendFilters(nsIFilePicker.filterAll);
+
+    /* doesn't handle *.shtml files */
+    try {
+      fp.show();
+      /* need to handle cancel (uncaught exception at present) */
+    }
+    catch (ex) {
+      dump("filePicker.chooseInputFile threw an exception\n");
+    }
+  
+    /* This checks for already open window and activates it... 
+     * note that we have to test the native path length
+     *  since file.URL will be "file:///" if no filename picked (Cancel button used)
+     */
+    if (fp.file && fp.file.path.length > 0) {
+      SaveFilePickerDirectory(fp, "html");
+      editPage(fp.fileURL.spec, window, false);
+    }
+  }
+};
+
+// STRUCTURE TOOLBAR
+//
+var nsUpdateStructToolbarCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    UpdateStructToolbar();
+    return true;
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+  doCommand: function(aCommand)  {}
+}
+
+// ******* File output commands and utilities ******** //
+//-----------------------------------------------------------------------------------
+var nsSaveCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    // Always allow saving when editing a remote document,
+    //  otherwise the document modified state would prevent that
+    //  when you first open a remote file.
+    try {
+      var docUrl = GetDocumentUrl();
+      return IsDocumentEditable() &&
+        (IsDocumentModified() || IsHTMLSourceChanged() ||
+         IsUrlAboutBlank(docUrl) || GetScheme(docUrl) != "file");
+    } catch (e) {return false;}
+  },
+  
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    var result = false;
+    var editor = GetCurrentEditor();
+    if (editor)
+    {
+      FinishHTMLSource();
+      result = SaveDocument(IsUrlAboutBlank(GetDocumentUrl()), false, editor.contentsMIMEType);
+      window.content.focus();
+    }
+    return result;
+  }
+}
+
+var nsSaveAsCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    var editor = GetCurrentEditor();
+    if (editor)
+    {
+      FinishHTMLSource();
+      var result = SaveDocument(true, false, editor.contentsMIMEType);
+      window.content.focus();
+      return result;
+    }
+    return false;
+  }
+}
+
+var nsExportToTextCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    if (GetCurrentEditor())
+    {
+      FinishHTMLSource();
+      var result = SaveDocument(true, true, "text/plain");
+      window.content.focus();
+      return result;
+    }
+    return false;
+  }
+}
+
+var nsSaveAndChangeEncodingCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {    
+    FinishHTMLSource();
+    window.ok = false;
+    window.exportToText = false;
+    var oldTitle = GetDocumentTitle();
+    window.openDialog("chrome://editor/content/EditorSaveAsCharset.xul","_blank", "chrome,close,titlebar,modal,resizable=yes");
+
+    if (GetDocumentTitle() != oldTitle)
+      UpdateWindowTitle();
+
+    if (window.ok)
+    {
+      if (window.exportToText)
+      {
+        window.ok = SaveDocument(true, true, "text/plain");
+      }
+      else
+      {
+        var editor = GetCurrentEditor();
+        window.ok = SaveDocument(true, false, editor ? editor.contentsMIMEType : null);
+      }
+    }
+
+    window.content.focus();
+    return window.ok;
+  }
+};
+
+var nsPublishCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    if (IsDocumentEditable())
+    {
+      // Always allow publishing when editing a local document,
+      //  otherwise the document modified state would prevent that
+      //  when you first open any local file.
+      try {
+        var docUrl = GetDocumentUrl();
+        return IsDocumentModified() || IsHTMLSourceChanged()
+               || IsUrlAboutBlank(docUrl) || GetScheme(docUrl) == "file";
+      } catch (e) {return false;}
+    }
+    return false;
+  },
+  
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    if (GetCurrentEditor())
+    {
+      var docUrl = GetDocumentUrl();
+      var filename = GetFilename(docUrl);
+      var publishData;
+      var showPublishDialog = false;
+
+      // First check pref to always show publish dialog
+      try {
+        var prefs = GetPrefs();
+        if (prefs)
+          showPublishDialog = prefs.getBoolPref("editor.always_show_publish_dialog");
+      } catch(e) {}
+
+      if (!showPublishDialog && filename)
+      {
+        // Try to get publish data from the document url
+        publishData = CreatePublishDataFromUrl(docUrl);
+
+        // If none, use default publishing site? Need a pref for this
+        //if (!publishData)
+        //  publishData = GetPublishDataFromSiteName(GetDefaultPublishSiteName(), filename);
+      }
+
+      if (showPublishDialog || !publishData)
+      {
+        // Show the publish dialog
+        publishData = {};
+        window.ok = false;
+        var oldTitle = GetDocumentTitle();
+        window.openDialog("chrome://editor/content/EditorPublish.xul","_blank", 
+                          "chrome,close,titlebar,modal", "", "", publishData);
+        if (GetDocumentTitle() != oldTitle)
+          UpdateWindowTitle();
+
+        window.content.focus();
+        if (!window.ok)
+          return false;
+      }
+      if (publishData)
+      {
+        FinishHTMLSource();
+        return Publish(publishData);
+      }
+    }
+    return false;
+  }
+}
+
+var nsPublishAsCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable());
+  },
+  
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    if (GetCurrentEditor())
+    {
+      FinishHTMLSource();
+
+      window.ok = false;
+      var publishData = {};
+      var oldTitle = GetDocumentTitle();
+      window.openDialog("chrome://editor/content/EditorPublish.xul","_blank", 
+                        "chrome,close,titlebar,modal", "", "", publishData);
+      if (GetDocumentTitle() != oldTitle)
+        UpdateWindowTitle();
+
+      window.content.focus();
+      if (window.ok)
+        return Publish(publishData);
+    }
+    return false;
+  }
+}
+
+// ------- output utilites   ----- //
+
+// returns a fileExtension string
+function GetExtensionBasedOnMimeType(aMIMEType)
+{
+  try {
+    var mimeService = null;
+    mimeService = Components.classes["@mozilla.org/mime;1"].getService();
+    mimeService = mimeService.QueryInterface(Components.interfaces.nsIMIMEService);
+
+    var fileExtension = mimeService.getPrimaryExtension(aMIMEType, null);
+
+    // the MIME service likes to give back ".htm" for text/html files,
+    // so do a special-case fix here.
+    if (fileExtension == "htm")
+      fileExtension = "html";
+
+    return fileExtension;
+  }
+  catch (e) {}
+  return "";
+}
+
+function GetSuggestedFileName(aDocumentURLString, aMIMEType)
+{
+  var extension = GetExtensionBasedOnMimeType(aMIMEType);
+  if (extension)
+    extension = "." + extension;
+
+  // check for existing file name we can use
+  if (aDocumentURLString.length >= 0 && !IsUrlAboutBlank(aDocumentURLString))
+  {
+    var docURI = null;
+    try {
+
+      var ioService = GetIOService();
+      docURI = ioService.newURI(aDocumentURLString, GetCurrentEditor().documentCharacterSet, null);
+      docURI = docURI.QueryInterface(Components.interfaces.nsIURL);
+
+      // grab the file name
+      var url = docURI.fileBaseName;
+      if (url)
+        return url+extension;
+    } catch(e) {}
+  } 
+
+  // check if there is a title we can use
+  var title = GetDocumentTitle();
+  // generate a valid filename, if we can't just go with "untitled"
+  return GenerateValidFilename(title, extension) || GetString("untitled") + extension;
+}
+
+// returns file picker result
+function PromptForSaveLocation(aDoSaveAsText, aEditorType, aMIMEType, aDocumentURLString)
+{
+  var dialogResult = {};
+  dialogResult.filepickerClick = nsIFilePicker.returnCancel;
+  dialogResult.resultingURI = "";
+  dialogResult.resultingLocalFile = null;
+
+  var fp = null;
+  try {
+    fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
+  } catch (e) {}
+  if (!fp) return dialogResult;
+
+  // determine prompt string based on type of saving we'll do
+  var promptString;
+  if (aDoSaveAsText || aEditorType == "text")
+    promptString = GetString("ExportToText");
+  else
+    promptString = GetString("SaveDocumentAs")
+
+  fp.init(window, promptString, nsIFilePicker.modeSave);
+
+  // Set filters according to the type of output
+  if (aDoSaveAsText)
+    fp.appendFilters(nsIFilePicker.filterText);
+  else
+    fp.appendFilters(nsIFilePicker.filterHTML);
+  fp.appendFilters(nsIFilePicker.filterAll);
+
+  // now let's actually set the filepicker's suggested filename
+  var suggestedFileName = GetSuggestedFileName(aDocumentURLString, aMIMEType);
+  if (suggestedFileName)
+    fp.defaultString = suggestedFileName;
+
+  // set the file picker's current directory
+  // assuming we have information needed (like prior saved location)
+  try {
+    var ioService = GetIOService();
+    var fileHandler = GetFileProtocolHandler();
+    
+    var isLocalFile = true;
+    try {
+      var docURI = ioService.newURI(aDocumentURLString, GetCurrentEditor().documentCharacterSet, null);
+      isLocalFile = docURI.schemeIs("file");
+    }
+    catch (e) {}
+
+    var parentLocation = null;
+    if (isLocalFile)
+    {
+      var fileLocation = fileHandler.getFileFromURLSpec(aDocumentURLString); // this asserts if url is not local
+      parentLocation = fileLocation.parent;
+    }
+    if (parentLocation)
+    {
+      // Save current filepicker's default location
+      if ("gFilePickerDirectory" in window)
+        gFilePickerDirectory = fp.displayDirectory;
+
+      fp.displayDirectory = parentLocation;
+    }
+    else
+    {
+      // Initialize to the last-used directory for the particular type (saved in prefs)
+      SetFilePickerDirectory(fp, aEditorType);
+    }
+  }
+  catch(e) {}
+
+  dialogResult.filepickerClick = fp.show();
+  if (dialogResult.filepickerClick != nsIFilePicker.returnCancel)
+  {
+    // reset urlstring to new save location
+    dialogResult.resultingURIString = fileHandler.getURLSpecFromFile(fp.file);
+    dialogResult.resultingLocalFile = fp.file;
+    SaveFilePickerDirectory(fp, aEditorType);
+  }
+  else if ("gFilePickerDirectory" in window && gFilePickerDirectory)
+    fp.displayDirectory = gFilePickerDirectory; 
+
+  return dialogResult;
+}
+
+// returns a boolean (whether to continue (true) or not (false) because user canceled)
+function PromptAndSetTitleIfNone()
+{
+  if (GetDocumentTitle()) // we have a title; no need to prompt!
+    return true;
+
+  var promptService = GetPromptService();
+  if (!promptService) return false;
+
+  var result = {value:null};
+  var captionStr = GetString("DocumentTitle");
+  var msgStr = GetString("NeedDocTitle") + '\n' + GetString("DocTitleHelp");
+  var confirmed = promptService.prompt(window, captionStr, msgStr, result, null, {value:0});
+  if (confirmed)
+    SetDocumentTitle(TrimString(result.value));
+
+  return confirmed;
+}
+
+var gPersistObj;
+
+// Don't forget to do these things after calling OutputFileWithPersistAPI:
+// we need to update the uri before notifying listeners
+//    if (doUpdateURI)
+//      SetDocumentURI(docURI);
+//    UpdateWindowTitle();
+//    if (!aSaveCopy)
+//      editor.resetModificationCount();
+      // this should cause notification to listeners that document has changed
+
+const webPersist = Components.interfaces.nsIWebBrowserPersist;
+function OutputFileWithPersistAPI(editorDoc, aDestinationLocation, aRelatedFilesParentDir, aMimeType)
+{
+  gPersistObj = null;
+  var editor = GetCurrentEditor();
+  try {
+    var imeEditor = editor.QueryInterface(Components.interfaces.nsIEditorIMESupport);
+    imeEditor.forceCompositionEnd();
+    } catch (e) {}
+
+  var isLocalFile = false;
+  try {
+    var tmp1 = aDestinationLocation.QueryInterface(Components.interfaces.nsIFile);
+    isLocalFile = true;
+  } 
+  catch (e) {
+    try {
+      var tmp = aDestinationLocation.QueryInterface(Components.interfaces.nsIURI);
+      isLocalFile = tmp.schemeIs("file");
+    }
+    catch (e) {}
+  }
+
+  try {
+    // we should supply a parent directory if/when we turn on functionality to save related documents
+    var persistObj = Components.classes["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].createInstance(webPersist);
+    persistObj.progressListener = gEditorOutputProgressListener;
+    
+    var wrapColumn = GetWrapColumn();
+    var outputFlags = GetOutputFlags(aMimeType, wrapColumn);
+
+    // for 4.x parity as well as improving readability of file locally on server
+    // this will always send crlf for upload (http/ftp)
+    if (!isLocalFile) // if we aren't saving locally then send both cr and lf
+    {
+      outputFlags |= webPersist.ENCODE_FLAGS_CR_LINEBREAKS | webPersist.ENCODE_FLAGS_LF_LINEBREAKS;
+
+      // we want to serialize the output for all remote publishing
+      // some servers can handle only one connection at a time
+      // some day perhaps we can make this user-configurable per site?
+      persistObj.persistFlags = persistObj.persistFlags | webPersist.PERSIST_FLAGS_SERIALIZE_OUTPUT;
+    }
+
+    // note: we always want to set the replace existing files flag since we have
+    // already given user the chance to not replace an existing file (file picker)
+    // or the user picked an option where the file is implicitly being replaced (save)
+    persistObj.persistFlags = persistObj.persistFlags 
+                            | webPersist.PERSIST_FLAGS_NO_BASE_TAG_MODIFICATIONS
+                            | webPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES
+                            | webPersist.PERSIST_FLAGS_DONT_FIXUP_LINKS
+                            | webPersist.PERSIST_FLAGS_DONT_CHANGE_FILENAMES
+                            | webPersist.PERSIST_FLAGS_FIXUP_ORIGINAL_DOM;
+    persistObj.saveDocument(editorDoc, aDestinationLocation, aRelatedFilesParentDir, 
+                            aMimeType, outputFlags, wrapColumn);
+    gPersistObj = persistObj;
+  }
+  catch(e) { dump("caught an error, bail\n"); return false; }
+
+  return true;
+}
+
+// returns output flags based on mimetype, wrapCol and prefs
+function GetOutputFlags(aMimeType, aWrapColumn)
+{
+  var outputFlags = 0;
+  var editor = GetCurrentEditor();
+  var outputEntity = (editor && editor.documentCharacterSet == "ISO-8859-1")
+    ? webPersist.ENCODE_FLAGS_ENCODE_LATIN1_ENTITIES
+    : webPersist.ENCODE_FLAGS_ENCODE_BASIC_ENTITIES;
+  if (aMimeType == "text/plain")
+  {
+    // When saving in "text/plain" format, always do formatting
+    outputFlags |= webPersist.ENCODE_FLAGS_FORMATTED;
+  }
+  else
+  {
+    try {
+      // Should we prettyprint? Check the pref
+      var prefs = GetPrefs();
+      if (prefs.getBoolPref("editor.prettyprint"))
+        outputFlags |= webPersist.ENCODE_FLAGS_FORMATTED;
+
+      // How much entity names should we output? Check the pref
+      var encodeEntity = prefs.getCharPref("editor.encode_entity");
+      switch (encodeEntity) {
+        case "basic"  : outputEntity = webPersist.ENCODE_FLAGS_ENCODE_BASIC_ENTITIES; break;
+        case "latin1" : outputEntity = webPersist.ENCODE_FLAGS_ENCODE_LATIN1_ENTITIES; break;
+        case "html"   : outputEntity = webPersist.ENCODE_FLAGS_ENCODE_HTML_ENTITIES; break;
+        case "none"   : outputEntity = 0; break;
+      }
+    }
+    catch (e) {}
+  }
+  outputFlags |= outputEntity;
+
+  if (aWrapColumn > 0)
+    outputFlags |= webPersist.ENCODE_FLAGS_WRAP;
+
+  return outputFlags;
+}
+
+// returns number of column where to wrap
+const nsIWebBrowserPersist = Components.interfaces.nsIWebBrowserPersist;
+function GetWrapColumn()
+{
+  try {
+    return GetCurrentEditor().wrapWidth;
+  } catch (e) {}
+  return 0;
+}
+
+function GetPromptService()
+{
+  var promptService;
+  try {
+    promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService();
+    promptService = promptService.QueryInterface(Components.interfaces.nsIPromptService);
+  }
+  catch (e) {}
+  return promptService;
+}
+
+const gShowDebugOutputStateChange = false;
+const gShowDebugOutputProgress = false;
+const gShowDebugOutputStatusChange = false;
+
+const gShowDebugOutputLocationChange = false;
+const gShowDebugOutputSecurityChange = false;
+
+const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
+const nsIChannel = Components.interfaces.nsIChannel;
+
+const kErrorBindingAborted = 2152398850;
+const kErrorBindingRedirected = 2152398851;
+const kFileNotFound = 2152857618;
+
+var gEditorOutputProgressListener =
+{
+  onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus)
+  {
+    var editor = GetCurrentEditor();
+
+    // Use this to access onStateChange flags
+    var requestSpec;
+    try {
+      var channel = aRequest.QueryInterface(nsIChannel);
+      requestSpec = StripUsernamePasswordFromURI(channel.URI);
+    } catch (e) {
+      if ( gShowDebugOutputStateChange)
+        dump("***** onStateChange; NO REQUEST CHANNEL\n");
+    }
+
+    var pubSpec;
+    if (gPublishData)
+      pubSpec = gPublishData.publishUrl + gPublishData.docDir + gPublishData.filename;
+
+    if (gShowDebugOutputStateChange)
+    {
+      dump("\n***** onStateChange request: " + requestSpec + "\n");
+      dump("      state flags: ");
+
+      if (aStateFlags & nsIWebProgressListener.STATE_START)
+        dump(" STATE_START, ");
+      if (aStateFlags & nsIWebProgressListener.STATE_STOP)
+        dump(" STATE_STOP, ");
+      if (aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK)
+        dump(" STATE_IS_NETWORK ");
+
+      dump("\n * requestSpec="+requestSpec+", pubSpec="+pubSpec+", aStatus="+aStatus+"\n");
+
+      DumpDebugStatus(aStatus);
+    }
+    // The rest only concerns publishing, so bail out if no dialog
+    if (!gProgressDialog)
+      return;
+
+    // Detect start of file upload of any file:
+    // (We ignore any START messages after gPersistObj says publishing is finished
+    if ((aStateFlags & nsIWebProgressListener.STATE_START)
+         && gPersistObj && requestSpec
+         && (gPersistObj.currentState != gPersistObj.PERSIST_STATE_FINISHED))
+    {
+      try {
+        // Add url to progress dialog's list showing each file uploading
+        gProgressDialog.SetProgressStatus(GetFilename(requestSpec), "busy");
+      } catch(e) {}
+    }
+
+    // Detect end of file upload of any file:
+    if (aStateFlags & nsIWebProgressListener.STATE_STOP)
+    {
+      // ignore aStatus == kErrorBindingAborted; check http response for possible errors
+      try {
+        // check http channel for response: 200 range is ok; other ranges are not
+        var httpChannel = aRequest.QueryInterface(Components.interfaces.nsIHttpChannel);
+        var httpResponse = httpChannel.responseStatus;
+        if (httpResponse < 200 || httpResponse >= 300)
+          aStatus = httpResponse;   // not a real error but enough to pass check below
+        else if (aStatus == kErrorBindingAborted)
+          aStatus = 0;
+
+        if (gShowDebugOutputStateChange)
+          dump("http response is: "+httpResponse+"\n");
+      } 
+      catch(e) 
+      {
+        if (aStatus == kErrorBindingAborted)
+          aStatus = 0;
+      }
+
+      // We abort publishing for all errors except if image src file is not found
+      var abortPublishing = (aStatus != 0 && aStatus != kFileNotFound);
+
+      // Notify progress dialog when we receive the STOP
+      //  notification for a file if there was an error 
+      //  or a successful finish
+      //  (Check requestSpec to be sure message is for destination url)
+      if (aStatus != 0 
+           || (requestSpec && requestSpec.indexOf(GetScheme(gPublishData.publishUrl)) == 0))
+      {
+        try {
+          gProgressDialog.SetProgressFinished(GetFilename(requestSpec), aStatus);
+        } catch(e) {}
+      }
+
+
+      if (abortPublishing)
+      {
+        // Cancel publishing
+        gPersistObj.cancelSave();
+
+        // Don't do any commands after failure
+        gCommandAfterPublishing = null;
+
+        // Restore original document to undo image src url adjustments
+        if (gRestoreDocumentSource)
+        {
+          try {
+            editor.rebuildDocumentFromSource(gRestoreDocumentSource);
+
+            // Clear transaction cache since we just did a potentially 
+            //  very large insert and this will eat up memory
+            editor.transactionManager.clear();
+          }
+          catch (e) {}
+        }
+
+        // Notify progress dialog that we're finished
+        //  and keep open to show error
+        gProgressDialog.SetProgressFinished(null, 0);
+
+        // We don't want to change location or reset mod count, etc.
+        return;
+      }
+
+      //XXX HACK: "file://" protocol is not supported in network code
+      //    (bug 151867 filed to add this support, bug 151869 filed
+      //     to remove this and other code in nsIWebBrowserPersist)
+      //    nsIWebBrowserPersist *does* copy the file(s), but we don't 
+      //    get normal onStateChange messages.
+
+      // Case 1: If images are included, we get fairly normal
+      //    STATE_START/STATE_STOP & STATE_IS_NETWORK messages associated with the image files,
+      //    thus we must finish HTML file progress below
+
+      // Case 2: If just HTML file is uploaded, we get STATE_START and STATE_STOP
+      //    notification with a null "requestSpec", and 
+      //    the gPersistObj is destroyed before we get here!
+      //    So create an new object so we can flow through normal processing below
+      if (!requestSpec && GetScheme(gPublishData.publishUrl) == "file"
+          && (!gPersistObj || gPersistObj.currentState == nsIWebBrowserPersist.PERSIST_STATE_FINISHED))
+      {
+        aStateFlags |= nsIWebProgressListener.STATE_IS_NETWORK;
+        if (!gPersistObj)
+        {          
+          gPersistObj =
+          {
+            result : aStatus,
+            currentState : nsIWebBrowserPersist.PERSIST_STATE_FINISHED
+          }
+        }
+      }
+
+      // STATE_IS_NETWORK signals end of publishing, as does the gPersistObj.currentState
+      if (aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK
+          && gPersistObj.currentState == nsIWebBrowserPersist.PERSIST_STATE_FINISHED)
+      {
+        if (GetScheme(gPublishData.publishUrl) == "file")
+        {
+          //XXX "file://" hack: We don't get notified about the HTML file, so end progress for it
+          // (This covers both "Case 1 and 2" described above)
+          gProgressDialog.SetProgressFinished(gPublishData.filename, gPersistObj.result);
+        }
+
+        if (gPersistObj.result == 0)
+        {
+          // All files are finished and publishing succeeded (some images may have failed)
+          try {
+            // Make a new docURI from the "browse location" in case "publish location" was FTP
+            // We need to set document uri before notifying listeners
+            var docUrl = GetDocUrlFromPublishData(gPublishData);
+            SetDocumentURI(GetIOService().newURI(docUrl, editor.documentCharacterSet, null));
+
+            UpdateWindowTitle();
+
+            // this should cause notification to listeners that doc has changed
+            editor.resetModificationCount();
+
+            // Set UI based on whether we're editing a remote or local url
+            SetSaveAndPublishUI(urlstring);
+
+          } catch (e) {}
+
+          // Save publishData to prefs
+          if (gPublishData)
+          {
+            if (gPublishData.savePublishData)
+            {
+              // We published successfully, so we can safely
+              //  save docDir and otherDir to prefs
+              gPublishData.saveDirs = true;
+              SavePublishDataToPrefs(gPublishData);
+            }
+            else
+              SavePassword(gPublishData);
+          }
+
+          // Ask progress dialog to close, but it may not
+          // if user checked checkbox to keep it open
+          gProgressDialog.RequestCloseDialog();
+        }
+        else
+        {
+          // We previously aborted publishing because of error:
+          //   Calling gPersistObj.cancelSave() resulted in a non-zero gPersistObj.result,
+          //   so notify progress dialog we're finished
+          gProgressDialog.SetProgressFinished(null, 0);
+        }
+      }
+    }
+  },
+
+  onProgressChange : function(aWebProgress, aRequest, aCurSelfProgress,
+                              aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress)
+  {
+    if (!gPersistObj)
+      return;
+
+    if (gShowDebugOutputProgress)
+    {
+      dump("\n onProgressChange: gPersistObj.result="+gPersistObj.result+"\n");
+      try {
+      var channel = aRequest.QueryInterface(nsIChannel);
+      dump("***** onProgressChange request: " + channel.URI.spec + "\n");
+      }
+      catch (e) {}
+      dump("*****       self:  "+aCurSelfProgress+" / "+aMaxSelfProgress+"\n");
+      dump("*****       total: "+aCurTotalProgress+" / "+aMaxTotalProgress+"\n\n");
+
+      if (gPersistObj.currentState == gPersistObj.PERSIST_STATE_READY)
+        dump(" Persister is ready to save data\n\n");
+      else if (gPersistObj.currentState == gPersistObj.PERSIST_STATE_SAVING)
+        dump(" Persister is saving data.\n\n");
+      else if (gPersistObj.currentState == gPersistObj.PERSIST_STATE_FINISHED)
+        dump(" PERSISTER HAS FINISHED SAVING DATA\n\n\n");
+    }
+  },
+
+  onLocationChange : function(aWebProgress, aRequest, aLocation)
+  {
+    if (gShowDebugOutputLocationChange)
+    {
+      dump("***** onLocationChange: "+aLocation.spec+"\n");
+      try {
+        var channel = aRequest.QueryInterface(nsIChannel);
+        dump("*****          request: " + channel.URI.spec + "\n");
+      }
+      catch(e) {}
+    }
+  },
+
+  onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
+  {
+    if (gShowDebugOutputStatusChange)
+    {
+      dump("***** onStatusChange: "+aMessage+"\n");
+      try {
+        var channel = aRequest.QueryInterface(nsIChannel);
+        dump("*****        request: " + channel.URI.spec + "\n");
+      }
+      catch (e) { dump("          couldn't get request\n"); }
+      
+      DumpDebugStatus(aStatus);
+
+      if (gPersistObj)
+      {
+        if(gPersistObj.currentState == gPersistObj.PERSIST_STATE_READY)
+          dump(" Persister is ready to save data\n\n");
+        else if(gPersistObj.currentState == gPersistObj.PERSIST_STATE_SAVING)
+          dump(" Persister is saving data.\n\n");
+        else if(gPersistObj.currentState == gPersistObj.PERSIST_STATE_FINISHED)
+          dump(" PERSISTER HAS FINISHED SAVING DATA\n\n\n");
+      }
+    }
+  },
+
+  onSecurityChange : function(aWebProgress, aRequest, state)
+  {
+    if (gShowDebugOutputSecurityChange)
+    {
+      try {
+        var channel = aRequest.QueryInterface(nsIChannel);
+        dump("***** onSecurityChange request: " + channel.URI.spec + "\n");
+      } catch (e) {}
+    }
+  },
+
+  QueryInterface : function(aIID)
+  {
+    if (aIID.equals(Components.interfaces.nsIWebProgressListener)
+    || aIID.equals(Components.interfaces.nsISupports)
+    || aIID.equals(Components.interfaces.nsISupportsWeakReference)
+    || aIID.equals(Components.interfaces.nsIPrompt)
+    || aIID.equals(Components.interfaces.nsIAuthPrompt))
+      return this;
+    throw Components.results.NS_NOINTERFACE;
+  },
+
+// nsIPrompt
+  alert : function(dlgTitle, text)
+  {
+    AlertWithTitle(dlgTitle, text, gProgressDialog ? gProgressDialog : window);
+  },
+  alertCheck : function(dialogTitle, text, checkBoxLabel, checkObj)
+  {
+    AlertWithTitle(dialogTitle, text);
+  },
+  confirm : function(dlgTitle, text)
+  {
+    return ConfirmWithTitle(dlgTitle, text, null, null);
+  },
+  confirmCheck : function(dlgTitle, text, checkBoxLabel, checkObj)
+  {
+    var promptServ = GetPromptService();
+    if (!promptServ)
+      return;
+
+    promptServ.confirmEx(window, dlgTitle, text, nsIPromptService.STD_OK_CANCEL_BUTTONS,
+                         "", "", "", checkBoxLabel, checkObj);
+  },
+  confirmEx : function(dlgTitle, text, btnFlags, btn0Title, btn1Title, btn2Title, checkBoxLabel, checkVal)
+  {
+    var promptServ = GetPromptService();
+    if (!promptServ)
+     return 0;
+
+    return promptServ.confirmEx(window, dlgTitle, text, btnFlags,
+                        btn0Title, btn1Title, btn2Title,
+                        checkBoxLabel, checkVal);
+  },
+  prompt : function(dlgTitle, text, inoutText, checkBoxLabel, checkObj)
+  {
+    var promptServ = GetPromptService();
+    if (!promptServ)
+     return false;
+
+    return promptServ.prompt(window, dlgTitle, text, inoutText, checkBoxLabel, checkObj);
+  },
+  promptPassword : function(dlgTitle, text, pwObj, checkBoxLabel, savePWObj)
+  {
+
+    var promptServ = GetPromptService();
+    if (!promptServ)
+     return false;
+
+    var ret = false;
+    try {
+      // Note difference with nsIAuthPrompt::promptPassword, which has 
+      // just "in" savePassword param, while nsIPrompt is "inout"
+      // Initialize with user's previous preference for this site
+      if (gPublishData)
+        savePWObj.value = gPublishData.savePassword;
+
+      ret = promptServ.promptPassword(gProgressDialog ? gProgressDialog : window,
+                                      dlgTitle, text, pwObj, checkBoxLabel, savePWObj);
+
+      if (!ret)
+        setTimeout(CancelPublishing, 0);
+
+      if (ret && gPublishData)
+        UpdateUsernamePasswordFromPrompt(gPublishData, gPublishData.username, pwObj.value, savePWObj.value);
+    } catch(e) {}
+
+    return ret;
+  },
+  promptUsernameAndPassword : function(dlgTitle, text, userObj, pwObj, checkBoxLabel, savePWObj)
+  {
+    var ret = PromptUsernameAndPassword(dlgTitle, text, savePWObj.value, userObj, pwObj);
+    if (!ret)
+      setTimeout(CancelPublishing, 0);
+
+    return ret;
+  },
+  select : function(dlgTitle, text, count, selectList, outSelection)
+  {
+    var promptServ = GetPromptService();
+    if (!promptServ)
+      return false;
+
+    return promptServ.select(window, dlgTitle, text, count, selectList, outSelection);
+  },
+
+// nsIAuthPrompt
+  prompt : function(dlgTitle, text, pwrealm, savePW, defaultText, result)
+  {
+    var promptServ = GetPromptService();
+    if (!promptServ)
+      return false;
+
+    var savePWObj = {value:savePW};
+    var ret = promptServ.prompt(gProgressDialog ? gProgressDialog : window,
+                                dlgTitle, text, defaultText, pwrealm, savePWObj);
+    if (!ret)
+      setTimeout(CancelPublishing, 0);
+    return ret;
+  },
+
+  promptUsernameAndPassword : function(dlgTitle, text, pwrealm, savePW, userObj, pwObj)
+  {
+    var ret = PromptUsernameAndPassword(dlgTitle, text, savePW, userObj, pwObj);
+    if (!ret)
+      setTimeout(CancelPublishing, 0);
+    return ret;
+  },
+
+  promptPassword : function(dlgTitle, text, pwrealm, savePW, pwObj)
+  {
+    var ret = false;
+    try {
+      var promptServ = GetPromptService();
+      if (!promptServ)
+        return false;
+
+      // Note difference with nsIPrompt::promptPassword, which has 
+      // "inout" savePassword param, while nsIAuthPrompt is just "in"
+      // Also nsIAuth doesn't supply "checkBoxLabel"
+      // Initialize with user's previous preference for this site
+      var savePWObj = {value:savePW};
+      // Initialize with user's previous preference for this site
+      if (gPublishData)
+        savePWObj.value = gPublishData.savePassword;
+
+      ret = promptServ.promptPassword(gProgressDialog ? gProgressDialog : window,
+                                      dlgTitle, text, pwObj, GetString("SavePassword"), savePWObj);
+
+      if (!ret)
+        setTimeout(CancelPublishing, 0);
+
+      if (ret && gPublishData)
+        UpdateUsernamePasswordFromPrompt(gPublishData, gPublishData.username, pwObj.value, savePWObj.value);
+    } catch(e) {}
+
+    return ret;
+  }
+}
+
+function PromptUsernameAndPassword(dlgTitle, text, savePW, userObj, pwObj)
+{
+  // HTTP prompts us twice even if user Cancels from 1st attempt!
+  // So never put up dialog if there's no publish data
+  if (!gPublishData)
+    return false
+
+  var ret = false;
+  try {
+    var promptServ = GetPromptService();
+    if (!promptServ)
+      return false;
+
+    var savePWObj = {value:savePW};
+
+    // Initialize with user's previous preference for this site
+    if (gPublishData)
+    {
+      // HTTP put uses this dialog if either username or password is bad,
+      //   so prefill username input field with the previous value for modification
+      savePWObj.value = gPublishData.savePassword;
+      if (!userObj.value)
+        userObj.value = gPublishData.username;
+    }
+
+    ret = promptServ.promptUsernameAndPassword(gProgressDialog ? gProgressDialog : window, 
+                                               dlgTitle, text, userObj, pwObj, 
+                                               GetString("SavePassword"), savePWObj);
+    if (ret && gPublishData)
+      UpdateUsernamePasswordFromPrompt(gPublishData, userObj.value, pwObj.value, savePWObj.value);
+
+  } catch (e) {}
+
+  return ret;
+}
+
+function DumpDebugStatus(aStatus)
+{
+  // see nsError.h and netCore.h and ftpCore.h
+
+  if (aStatus == kErrorBindingAborted)
+    dump("***** status is NS_BINDING_ABORTED\n");
+  else if (aStatus == kErrorBindingRedirected)
+    dump("***** status is NS_BINDING_REDIRECTED\n");
+  else if (aStatus == 2152398859) // in netCore.h 11
+    dump("***** status is ALREADY_CONNECTED\n");
+  else if (aStatus == 2152398860) // in netCore.h 12
+    dump("***** status is NOT_CONNECTED\n");
+  else if (aStatus == 2152398861) //  in nsISocketTransportService.idl 13
+    dump("***** status is CONNECTION_REFUSED\n");
+  else if (aStatus == 2152398862) // in nsISocketTransportService.idl 14
+    dump("***** status is NET_TIMEOUT\n");
+  else if (aStatus == 2152398863) // in netCore.h 15
+    dump("***** status is IN_PROGRESS\n");
+  else if (aStatus == 2152398864) // 0x804b0010 in netCore.h 16
+    dump("***** status is OFFLINE\n");
+  else if (aStatus == 2152398865) // in netCore.h 17
+    dump("***** status is NO_CONTENT\n");
+  else if (aStatus == 2152398866) // in netCore.h 18
+    dump("***** status is UNKNOWN_PROTOCOL\n");
+  else if (aStatus == 2152398867) // in netCore.h 19
+    dump("***** status is PORT_ACCESS_NOT_ALLOWED\n");
+  else if (aStatus == 2152398868) // in nsISocketTransportService.idl 20
+    dump("***** status is NET_RESET\n");
+  else if (aStatus == 2152398869) // in ftpCore.h 21
+    dump("***** status is FTP_LOGIN\n");
+  else if (aStatus == 2152398870) // in ftpCore.h 22
+    dump("***** status is FTP_CWD\n");
+  else if (aStatus == 2152398871) // in ftpCore.h 23
+    dump("***** status is FTP_PASV\n");
+  else if (aStatus == 2152398872) // in ftpCore.h 24
+    dump("***** status is FTP_PWD\n");
+  else if (aStatus == 2152857601)
+    dump("***** status is UNRECOGNIZED_PATH\n");
+  else if (aStatus == 2152857602)
+    dump("***** status is UNRESOLABLE SYMLINK\n");
+  else if (aStatus == 2152857604)
+    dump("***** status is UNKNOWN_TYPE\n");
+  else if (aStatus == 2152857605)
+    dump("***** status is DESTINATION_NOT_DIR\n");
+  else if (aStatus == 2152857606)
+    dump("***** status is TARGET_DOES_NOT_EXIST\n");
+  else if (aStatus == 2152857608)
+    dump("***** status is ALREADY_EXISTS\n");
+  else if (aStatus == 2152857609)
+    dump("***** status is INVALID_PATH\n");
+  else if (aStatus == 2152857610)
+    dump("***** status is DISK_FULL\n");
+  else if (aStatus == 2152857612)
+    dump("***** status is NOT_DIRECTORY\n");
+  else if (aStatus == 2152857613)
+    dump("***** status is IS_DIRECTORY\n");
+  else if (aStatus == 2152857614)
+    dump("***** status is IS_LOCKED\n");
+  else if (aStatus == 2152857615)
+    dump("***** status is TOO_BIG\n");
+  else if (aStatus == 2152857616)
+    dump("***** status is NO_DEVICE_SPACE\n");
+  else if (aStatus == 2152857617)
+    dump("***** status is NAME_TOO_LONG\n");
+  else if (aStatus == 2152857618) // 80520012
+    dump("***** status is FILE_NOT_FOUND\n");
+  else if (aStatus == 2152857619)
+    dump("***** status is READ_ONLY\n");
+  else if (aStatus == 2152857620)
+    dump("***** status is DIR_NOT_EMPTY\n");
+  else if (aStatus == 2152857621)
+    dump("***** status is ACCESS_DENIED\n");
+  else if (aStatus == 2152398878)
+    dump("***** status is ? (No connection or time out?)\n");
+  else
+    dump("***** status is " + aStatus + "\n");
+}
+
+// Update any data that the user supplied in a prompt dialog
+function UpdateUsernamePasswordFromPrompt(publishData, username, password, savePassword)
+{
+  if (!publishData)
+    return;
+  
+  // Set flag to save publish data after publishing if it changed in dialog 
+  //  and the "SavePassword" checkbox was checked
+  //  or we already had site data for this site
+  // (Thus we don't automatically create a site until user brings up Publish As dialog)
+  publishData.savePublishData = (gPublishData.username != username || gPublishData.password != password)
+                                && (savePassword || !publishData.notInSiteData);
+
+  publishData.username = username;
+  publishData.password = password;
+  publishData.savePassword = savePassword;
+}
+
+const kSupportedTextMimeTypes =
+[
+  "text/plain",
+  "text/css",
+  "text/rdf",
+  "text/xsl",
+  "text/javascript",
+  "text/ecmascript",
+  "application/javascript",
+  "application/ecmascript",
+  "application/x-javascript",
+  "text/xul",
+  "application/vnd.mozilla.xul+xml"
+];
+
+function IsSupportedTextMimeType(aMimeType)
+{
+  for (var i = 0; i < kSupportedTextMimeTypes.length; i++)
+  {
+    if (kSupportedTextMimeTypes[i] == aMimeType)
+      return true;
+  }
+  return false;
+}
+
+// throws an error or returns true if user attempted save; false if user canceled save
+function SaveDocument(aSaveAs, aSaveCopy, aMimeType)
+{
+  var editor = GetCurrentEditor();
+  if (!aMimeType || aMimeType == "" || !editor)
+    throw Components.results.NS_ERROR_NOT_INITIALIZED;
+
+  var editorDoc = editor.document;
+  if (!editorDoc)
+    throw Components.results.NS_ERROR_NOT_INITIALIZED;
+
+  // if we don't have the right editor type bail (we handle text and html)
+  var editorType = GetCurrentEditorType();
+  if (editorType != "text" && editorType != "html" 
+      && editorType != "htmlmail" && editorType != "textmail")
+    throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+
+  var saveAsTextFile = IsSupportedTextMimeType(aMimeType);
+
+  // check if the file is to be saved is a format we don't understand; if so, bail
+  if (aMimeType != "text/html" && !saveAsTextFile)
+    throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+
+  if (saveAsTextFile)
+    aMimeType = "text/plain";
+
+  var urlstring = GetDocumentUrl();
+  var mustShowFileDialog = (aSaveAs || IsUrlAboutBlank(urlstring) || (urlstring == ""));
+
+  // If editing a remote URL, force SaveAs dialog
+  if (!mustShowFileDialog && GetScheme(urlstring) != "file")
+    mustShowFileDialog = true;
+
+  var replacing = !aSaveAs;
+  var titleChanged = false;
+  var doUpdateURI = false;
+  var tempLocalFile = null;
+
+  if (mustShowFileDialog)
+  {
+	  try {
+	    // Prompt for title if we are saving to HTML
+	    if (!saveAsTextFile && (editorType == "html"))
+	    {
+	      var userContinuing = PromptAndSetTitleIfNone(); // not cancel
+	      if (!userContinuing)
+	        return false;
+	    }
+
+	    var dialogResult = PromptForSaveLocation(saveAsTextFile, editorType, aMimeType, urlstring);
+	    if (dialogResult.filepickerClick == nsIFilePicker.returnCancel)
+	      return false;
+
+	    replacing = (dialogResult.filepickerClick == nsIFilePicker.returnReplace);
+	    urlstring = dialogResult.resultingURIString;
+	    tempLocalFile = dialogResult.resultingLocalFile;
+ 
+      // update the new URL for the webshell unless we are saving a copy
+      if (!aSaveCopy)
+        doUpdateURI = true;
+   } catch (e) {  return false; }
+  } // mustShowFileDialog
+
+  var success = true;
+  var ioService;
+  try {
+    // if somehow we didn't get a local file but we did get a uri, 
+    // attempt to create the localfile if it's a "file" url
+    var docURI;
+    if (!tempLocalFile)
+    {
+      ioService = GetIOService();
+      docURI = ioService.newURI(urlstring, editor.documentCharacterSet, null);
+      
+      if (docURI.schemeIs("file"))
+      {
+        var fileHandler = GetFileProtocolHandler();
+        tempLocalFile = fileHandler.getFileFromURLSpec(urlstring).QueryInterface(Components.interfaces.nsILocalFile);
+      }
+    }
+
+    // this is the location where the related files will go
+    var relatedFilesDir = null;
+    
+    // First check pref for saving associated files
+    var saveAssociatedFiles = false;
+    try {
+      var prefs = GetPrefs();
+      saveAssociatedFiles = prefs.getBoolPref("editor.save_associated_files");
+    } catch (e) {}
+
+    // Only change links or move files if pref is set 
+    //  and we are saving to a new location
+    if (saveAssociatedFiles && aSaveAs)
+    {
+      try {
+        if (tempLocalFile)
+        {
+          // if we are saving to the same parent directory, don't set relatedFilesDir
+          // grab old location, chop off file
+          // grab new location, chop off file, compare
+          var oldLocation = GetDocumentUrl();
+          var oldLocationLastSlash = oldLocation.lastIndexOf("\/");
+          if (oldLocationLastSlash != -1)
+            oldLocation = oldLocation.slice(0, oldLocationLastSlash);
+
+          var relatedFilesDirStr = urlstring;
+          var newLocationLastSlash = relatedFilesDirStr.lastIndexOf("\/");
+          if (newLocationLastSlash != -1)
+            relatedFilesDirStr = relatedFilesDirStr.slice(0, newLocationLastSlash);
+          if (oldLocation == relatedFilesDirStr || IsUrlAboutBlank(oldLocation))
+            relatedFilesDir = null;
+          else
+            relatedFilesDir = tempLocalFile.parent;
+        }
+        else
+        {
+          var lastSlash = urlstring.lastIndexOf("\/");
+          if (lastSlash != -1)
+          {
+            var relatedFilesDirString = urlstring.slice(0, lastSlash + 1);  // include last slash
+            ioService = GetIOService();
+            relatedFilesDir = ioService.newURI(relatedFilesDirString, editor.documentCharacterSet, null);
+          }
+        }
+      } catch(e) { relatedFilesDir = null; }
+    }
+
+    var destinationLocation;
+    if (tempLocalFile)
+      destinationLocation = tempLocalFile;
+    else
+      destinationLocation = docURI;
+
+    success = OutputFileWithPersistAPI(editorDoc, destinationLocation, relatedFilesDir, aMimeType);
+  }
+  catch (e)
+  {
+    success = false;
+  }
+
+  if (success)
+  {
+    try {
+      if (doUpdateURI)
+      {
+         // If a local file, we must create a new uri from nsILocalFile
+        if (tempLocalFile)
+          docURI = GetFileProtocolHandler().newFileURI(tempLocalFile);
+
+        // We need to set new document uri before notifying listeners
+        SetDocumentURI(docURI);
+      }
+
+      // Update window title to show possibly different filename
+      // This also covers problem that after undoing a title change,
+      //   window title loses the extra [filename] part that this adds
+      UpdateWindowTitle();
+
+      if (!aSaveCopy)
+        editor.resetModificationCount();
+      // this should cause notification to listeners that document has changed
+
+      // Set UI based on whether we're editing a remote or local url
+      SetSaveAndPublishUI(urlstring);
+    } catch (e) {}
+  }
+  else
+  {
+    var saveDocStr = GetString("SaveDocument");
+    var failedStr = GetString("SaveFileFailed");
+    AlertWithTitle(saveDocStr, failedStr);
+  }
+  return success;
+}
+
+function SetDocumentURI(uri)
+{
+  try {
+    // XXX WE'LL NEED TO GET "CURRENT" CONTENT FRAME ONCE MULTIPLE EDITORS ARE ALLOWED
+    GetCurrentEditorElement().docShell.setCurrentURI(uri);
+  } catch (e) { dump("SetDocumentURI:\n"+e +"\n"); }
+}
+
+
+//-------------------------------  Publishing
+var gPublishData;
+var gProgressDialog;
+var gCommandAfterPublishing = null;
+var gRestoreDocumentSource;
+
+function Publish(publishData)
+{
+  if (!publishData)
+    return false;
+
+  // Set data in global for username password requests
+  //  and to do "post saving" actions after monitoring nsIWebProgressListener messages
+  //  and we are sure file transfer was successful
+  gPublishData = publishData;
+
+  gPublishData.docURI = CreateURIFromPublishData(publishData, true);
+  if (!gPublishData.docURI)
+  {
+    AlertWithTitle(GetString("Publish"), GetString("PublishFailed"));
+    return false;
+  }
+
+  if (gPublishData.publishOtherFiles)
+    gPublishData.otherFilesURI = CreateURIFromPublishData(publishData, false);
+  else
+    gPublishData.otherFilesURI = null;
+
+  if (gShowDebugOutputStateChange)
+  {
+    dump("\n *** publishData: PublishUrl="+publishData.publishUrl+", BrowseUrl="+publishData.browseUrl+
+      ", Username="+publishData.username+", Dir="+publishData.docDir+
+      ", Filename="+publishData.filename+"\n");
+    dump(" * gPublishData.docURI.spec w/o pass="+StripPassword(gPublishData.docURI.spec)+", PublishOtherFiles="+gPublishData.publishOtherFiles+"\n");
+  }
+
+  // XXX Missing username will make FTP fail 
+  // and it won't call us for prompt dialog (bug 132320)
+  // (It does prompt if just password is missing)
+  // So we should do the prompt ourselves before trying to publish
+  if (GetScheme(publishData.publishUrl) == "ftp" && !publishData.username)
+  {
+    var message = GetString("PromptFTPUsernamePassword").replace(/%host%/, GetHost(publishData.publishUrl));
+    var savePWobj = {value:publishData.savePassword};
+    var userObj = {value:publishData.username};
+    var pwObj = {value:publishData.password};
+    if (!PromptUsernameAndPassword(GetString("Prompt"), message, savePWobj, userObj, pwObj))
+      return false; // User canceled out of dialog
+
+    // Reset data in URI objects
+    gPublishData.docURI.username = publishData.username;
+    gPublishData.docURI.password = publishData.password;
+
+    if (gPublishData.otherFilesURI)
+    {
+      gPublishData.otherFilesURI.username = publishData.username;
+      gPublishData.otherFilesURI.password = publishData.password;
+    }
+  }
+
+  try {
+    // We launch dialog as a dependent 
+    // Don't allow editing document!
+    SetDocumentEditable(false);
+
+    // Start progress monitoring
+    gProgressDialog =
+      window.openDialog("chrome://editor/content/EditorPublishProgress.xul", "_blank",
+                        "chrome,dependent,titlebar", gPublishData, gPersistObj);
+
+  } catch (e) {}
+
+  // Network transfer is often too quick for the progress dialog to be initialized
+  //  and we can completely miss messages for quickly-terminated bad URLs,
+  //  so we can't call OutputFileWithPersistAPI right away.
+  // StartPublishing() is called at the end of the dialog's onload method
+  return true;
+}
+
+function StartPublishing()
+{
+  var editor = GetCurrentEditor();
+  if (editor && gPublishData && gPublishData.docURI && gProgressDialog)
+  {
+    gRestoreDocumentSource = null;
+
+    // Save backup document since nsIWebBrowserPersist changes image src urls
+    // but we only need to do this if publishing images and other related files
+    if (gPublishData.otherFilesURI)
+    {
+      try {
+        gRestoreDocumentSource = 
+          editor.outputToString(editor.contentsMIMEType, kOutputEncodeW3CEntities);
+      } catch (e) {}
+    }
+
+    OutputFileWithPersistAPI(editor.document, 
+                             gPublishData.docURI, gPublishData.otherFilesURI, 
+                             editor.contentsMIMEType);
+    return gPersistObj;
+  }
+  return null;
+}
+
+function CancelPublishing()
+{
+  try {
+    gPersistObj.cancelSave();
+    gProgressDialog.SetProgressStatusCancel();
+  } catch (e) {}
+
+  // If canceling publishing do not do any commands after this    
+  gCommandAfterPublishing = null;
+
+  if (gProgressDialog)
+  {
+    // Close Progress dialog 
+    // (this will call FinishPublishing())
+    gProgressDialog.CloseDialog();
+  }
+  else
+    FinishPublishing();
+}
+
+function FinishPublishing()
+{
+  SetDocumentEditable(true);
+  gProgressDialog = null;
+  gPublishData = null;
+  gRestoreDocumentSource = null;
+
+  if (gCommandAfterPublishing)
+  {
+    // Be sure to null out the global now incase of trouble when executing command
+    var command = gCommandAfterPublishing;
+    gCommandAfterPublishing = null;
+    goDoCommand(command);
+  }
+}
+
+// Create a nsIURI object filled in with all required publishing info
+function CreateURIFromPublishData(publishData, doDocUri)
+{
+  if (!publishData || !publishData.publishUrl)
+    return null;
+
+  var URI;
+  try {
+    var spec = publishData.publishUrl;
+    if (doDocUri)
+      spec += FormatDirForPublishing(publishData.docDir) + publishData.filename; 
+    else
+      spec += FormatDirForPublishing(publishData.otherDir);
+
+    var ioService = GetIOService();
+    URI = ioService.newURI(spec, GetCurrentEditor().documentCharacterSet, null);
+
+    if (publishData.username)
+      URI.username = publishData.username;
+    if (publishData.password)
+      URI.password = publishData.password;
+  }
+  catch (e) {}
+
+  return URI;
+}
+
+// Resolve the correct "http:" document URL when publishing via ftp
+function GetDocUrlFromPublishData(publishData)
+{
+  if (!publishData || !publishData.filename || !publishData.publishUrl)
+    return "";
+
+  // If user was previously editing an "ftp" url, then keep that as the new scheme
+  var url;
+  var docScheme = GetScheme(GetDocumentUrl());
+
+  // Always use the "HTTP" address if available
+  // XXX Should we do some more validation here for bad urls???
+  // Let's at least check for a scheme!
+  if (!GetScheme(publishData.browseUrl))
+    url = publishData.publishUrl;
+  else
+    url = publishData.browseUrl;
+
+  url += FormatDirForPublishing(publishData.docDir) + publishData.filename;
+
+  if (GetScheme(url) == "ftp")
+    url = InsertUsernameIntoUrl(url, publishData.username);
+
+  return url;
+}
+
+function SetSaveAndPublishUI(urlstring)
+{
+  // Be sure enabled state of toolbar buttons are correct
+  goUpdateCommand("cmd_save");
+  goUpdateCommand("cmd_publish");
+}
+
+function SetDocumentEditable(isDocEditable)
+{
+  var editor = GetCurrentEditor();
+  if (editor && editor.document)
+  {
+    try {
+      var flags = editor.flags;
+      editor.flags = isDocEditable ?  
+            flags &= ~nsIPlaintextEditor.eEditorReadonlyMask :
+            flags | nsIPlaintextEditor.eEditorReadonlyMask;
+    } catch(e) {}
+
+    // update all commands
+    window.updateCommands("create");
+  }  
+}
+
+// ****** end of save / publish **********//
+
+//-----------------------------------------------------------------------------------
+var nsPublishSettingsCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    if (GetCurrentEditor())
+    {
+      // Launch Publish Settings dialog
+
+      window.ok = window.openDialog("chrome://editor/content/EditorPublishSettings.xul","_blank", "chrome,close,titlebar,modal", "");
+      window.content.focus();
+      return window.ok;
+    }
+    return false;
+  }
+}
+
+//-----------------------------------------------------------------------------------
+var nsRevertCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() &&
+            IsDocumentModified() &&
+            !IsUrlAboutBlank(GetDocumentUrl()));
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    // Confirm with the user to abandon current changes
+    var promptService = GetPromptService();
+    if (promptService)
+    {
+      // Put the page title in the message string
+      var title = GetDocumentTitle();
+      if (!title)
+        title = GetString("untitled");
+
+      var msg = GetString("AbandonChanges").replace(/%title%/,title);
+
+      var result = promptService.confirmEx(window, GetString("RevertCaption"), msg,
+  						      (promptService.BUTTON_TITLE_REVERT * promptService.BUTTON_POS_0) +
+  						      (promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_1),
+  						      null, null, null, null, {value:0});
+
+      // Reload page if first button (Revert) was pressed
+      if(result == 0)
+      {
+        CancelHTMLSource();
+        EditorLoadUrl(GetDocumentUrl());
+      }
+    }
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsCloseCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return GetCurrentEditor() != null;
+  },
+  
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    CloseWindow();
+  }
+};
+
+function CloseWindow()
+{
+  // Check to make sure document is saved. "true" means allow "Don't Save" button,
+  //   so user can choose to close without saving
+  if (CheckAndSaveDocument("cmd_close", true)) 
+  {
+    if (window.InsertCharWindow)
+      SwitchInsertCharToAnotherEditorOrClose();
+
+    try {
+      var basewin = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                      .getInterface(Components.interfaces.nsIWebNavigation)
+                      .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
+                      .treeOwner
+                      .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                      .getInterface(Components.interfaces.nsIBaseWindow);
+      basewin.destroy();
+    } catch (e) {}
+  }
+}
+
+//-----------------------------------------------------------------------------------
+var nsOpenRemoteCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return true;    // we can always do this
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    var params = { browser: null, action: null, url: "" };
+    openDialog( "chrome://communicator/content/openLocation.xul", "_blank", "chrome,modal,titlebar", params);
+    var win = getTopWin();
+    switch (params.action) {
+      case "0": // current window
+        win.focus();
+        win.loadURI(params.url, null,
+                    nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP);
+        break;
+      case "1": // new window
+        openDialog(getBrowserURL(), "_blank", "all,dialog=no", params.url, null,
+                   null, nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP);
+        break;
+      case "2": // edit
+        editPage(params.url);
+        break;
+      case "3": // new tab
+        win.focus();
+        var browser = win.getBrowser();
+        browser.selectedTab = browser.addTab(params.url, null, null, false,
+                nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP);
+        break;
+      default:
+        window.content.focus();
+        break;
+    }
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsPreviewCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && 
+            IsHTMLEditor() && 
+            (DocumentHasBeenSaved() || IsDocumentModified()));
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+	  // Don't continue if user canceled during prompt for saving
+    // DocumentHasBeenSaved will test if we have a URL and suppress "Don't Save" button if not
+    if (!CheckAndSaveDocument("cmd_preview", DocumentHasBeenSaved()))
+	    return;
+
+    // Check if we saved again just in case?
+	  if (DocumentHasBeenSaved())
+    {
+      var browser;
+      try {
+        // Find a browser with this URL
+        var windowManager = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService();
+        var windowManagerInterface = windowManager.QueryInterface(Components.interfaces.nsIWindowMediator);
+        var enumerator = windowManagerInterface.getEnumerator("navigator:browser");
+
+        var documentURI = GetDocumentUrl();
+        while ( enumerator.hasMoreElements() )
+        {
+          browser = enumerator.getNext().QueryInterface(Components.interfaces.nsIDOMWindowInternal);
+          if ( browser && (documentURI == browser.getBrowser().currentURI.spec))
+            break;
+
+          browser = null;
+        }
+      }
+      catch (ex) {}
+
+      // If none found, open a new browser
+      if (!browser)
+      {
+        browser = window.openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no", documentURI);
+      }
+      else
+      {
+        try {
+          browser.BrowserReloadSkipCache();
+          browser.focus();
+        } catch (ex) {}
+      }
+    }
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsSendPageCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() &&
+            (DocumentHasBeenSaved() || IsDocumentModified()));
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    // Don't continue if user canceled during prompt for saving
+    // DocumentHasBeenSaved will test if we have a URL and suppress "Don't Save" button if not
+    if (!CheckAndSaveDocument("cmd_editSendPage", DocumentHasBeenSaved()))
+	    return;
+
+    // Check if we saved again just in case?
+    if (DocumentHasBeenSaved())
+    {
+      // Launch Messenger Composer window with current page as contents
+      try
+      {
+        openComposeWindow(GetDocumentUrl(), GetDocumentTitle());        
+      } catch (ex) { dump("Cannot Send Page: " + ex + "\n"); }
+    }
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsPrintCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return true;    // we can always do this
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    // In editor.js
+    FinishHTMLSource();
+    try {
+      PrintUtils.print();
+    } catch (e) {}
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsPrintSetupCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return true;    // we can always do this
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    // In editor.js
+    FinishHTMLSource();
+    PrintUtils.showPageSetup();
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsQuitCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return true;    // we can always do this
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {}
+
+  /* The doCommand is not used, since cmd_quit's oncommand="goQuitApplication()" in platformCommunicatorOverlay.xul
+  doCommand: function(aCommand)
+  {
+    // In editor.js
+    FinishHTMLSource();
+    goQuitApplication();
+  }
+  */
+};
+
+//-----------------------------------------------------------------------------------
+var nsFindCommand =
+{
+  isCommandEnabled: function(aCommand, editorElement)
+  {
+    return editorElement.getEditor(editorElement.contentWindow) != null;
+  },
+
+  getCommandStateParams: function(aCommand, aParams, editorElement) {},
+  doCommandParams: function(aCommand, aParams, editorElement) {},
+
+  doCommand: function(aCommand, editorElement)
+  {
+    try {
+      window.openDialog("chrome://editor/content/EdReplace.xul", "_blank",
+                        "chrome,modal,titlebar", editorElement);
+    }
+    catch(ex) {
+      dump("*** Exception: couldn't open Replace Dialog\n");
+    }
+    //window.content.focus();
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsFindAgainCommand =
+{
+  isCommandEnabled: function(aCommand, editorElement)
+  {
+    // we can only do this if the search pattern is non-empty. Not sure how
+    // to get that from here
+    return editorElement.getEditor(editorElement.contentWindow) != null;
+  },
+
+  getCommandStateParams: function(aCommand, aParams, editorElement) {},
+  doCommandParams: function(aCommand, aParams, editorElement) {},
+
+  doCommand: function(aCommand, editorElement)
+  {
+    try {
+      var findPrev = aCommand == "cmd_findPrev";
+      var findInst = editorElement.webBrowserFind;
+      var findService = Components.classes["@mozilla.org/find/find_service;1"]
+                                  .getService(Components.interfaces.nsIFindService);
+      findInst.findBackwards = findService.findBackwards ^ findPrev;
+      findInst.findNext();
+      // reset to what it was in dialog, otherwise dialog setting can get reversed
+      findInst.findBackwards = findService.findBackwards; 
+    }
+    catch (ex) {}
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsRewrapCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && !IsInHTMLSourceMode() &&
+            GetCurrentEditor() instanceof Components.interfaces.nsIEditorMailSupport);
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    GetCurrentEditor().QueryInterface(Components.interfaces.nsIEditorMailSupport).rewrap(false);
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsSpellingCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && 
+            !IsInHTMLSourceMode() && IsSpellCheckerInstalled());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    window.cancelSendMessage = false;
+    try {
+      var skipBlockQuotes = (window.document.documentElement.getAttribute("windowtype") == "msgcompose");
+      window.openDialog("chrome://editor/content/EdSpellCheck.xul", "_blank",
+              "chrome,close,titlebar,modal", false, skipBlockQuotes, true);
+    }
+    catch(ex) {}
+    window.content.focus();
+  }
+};
+
+// Validate using http://validator.w3.org/file-upload.html
+var URL2Validate;
+var nsValidateCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return GetCurrentEditor() != null;
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    // If the document hasn't been modified,
+    // then just validate the current url.
+    if (IsDocumentModified() || IsHTMLSourceChanged())
+    {
+      if (!CheckAndSaveDocument("cmd_validate", false))
+        return;
+
+      // Check if we saved again just in case?
+      if (!DocumentHasBeenSaved())    // user hit cancel?
+        return;
+    }
+
+    URL2Validate = GetDocumentUrl();
+    // See if it's a file:
+    var ifile;
+    try {
+      var fileHandler = GetFileProtocolHandler();
+      ifile = fileHandler.getFileFromURLSpec(URL2Validate);
+      // nsIFile throws an exception if it's not a file url
+    } catch (e) { ifile = null; }
+    if (ifile)
+    {
+      URL2Validate = ifile.path;
+      var vwin = window.open("http://validator.w3.org/file-upload.html",
+                             "EditorValidate");
+      // Window loads asynchronously, so pass control to the load listener:
+      vwin.addEventListener("load", this.validateFilePageLoaded, false);
+    }
+    else
+    {
+      var vwin2 = window.open("http://validator.w3.org/check?uri="
+                              + URL2Validate
+                              + "&doctype=Inline",
+                              "EditorValidate");
+      // This does the validation, no need to wait for page loaded.
+    }
+  },
+  validateFilePageLoaded: function(event)
+  {
+    event.target.forms[0].uploaded_file.value = URL2Validate;
+  }
+};
+
+var nsCheckLinksCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    window.openDialog("chrome://editor/content/EdLinkChecker.xul","_blank", "chrome,close,titlebar,modal");
+    window.content.focus();
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsFormCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    window.openDialog("chrome://editor/content/EdFormProps.xul", "_blank", "chrome,close,titlebar,modal");
+    window.content.focus();
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsInputTagCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    window.openDialog("chrome://editor/content/EdInputProps.xul", "_blank", "chrome,close,titlebar,modal");
+    window.content.focus();
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsInputImageCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    window.openDialog("chrome://editor/content/EdInputImage.xul", "_blank", "chrome,close,titlebar,modal");
+    window.content.focus();
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsTextAreaCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    window.openDialog("chrome://editor/content/EdTextAreaProps.xul", "_blank", "chrome,close,titlebar,modal");
+    window.content.focus();
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsSelectCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    window.openDialog("chrome://editor/content/EdSelectProps.xul", "_blank", "chrome,close,titlebar,modal");
+    window.content.focus();
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsButtonCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    window.openDialog("chrome://editor/content/EdButtonProps.xul", "_blank", "chrome,close,titlebar,modal");
+    window.content.focus();
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsLabelCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    var tagName = "label";
+    try {
+      var editor = GetCurrentEditor();
+      // Find selected label or if start/end of selection is in label 
+      var labelElement = editor.getSelectedElement(tagName);
+      if (!labelElement)
+        labelElement = editor.getElementOrParentByTagName(tagName, editor.selection.anchorNode);
+      if (!labelElement)
+        labelElement = editor.getElementOrParentByTagName(tagName, editor.selection.focusNode);
+      if (labelElement) {
+        // We only open the dialog for an existing label
+        window.openDialog("chrome://editor/content/EdLabelProps.xul", "_blank", "chrome,close,titlebar,modal", labelElement);
+        window.content.focus();
+      } else {
+        EditorSetTextProperty(tagName, "", "");
+      }
+    } catch (e) {}
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsFieldSetCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    window.openDialog("chrome://editor/content/EdFieldSetProps.xul", "_blank", "chrome,close,titlebar,modal");
+    window.content.focus();
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsIsIndexCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    try {
+      var editor = GetCurrentEditor();
+      var isindexElement = editor.createElementWithDefaults("isindex");
+      isindexElement.setAttribute("prompt", editor.outputToString("text/plain", kOutputSelectionOnly));
+      editor.insertElementAtSelection(isindexElement, true);
+    } catch (e) {}
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsImageCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    window.openDialog("chrome://editor/content/EdImageProps.xul","_blank", "chrome,close,titlebar,modal");
+    window.content.focus();
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsHLineCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    // Inserting an HLine is different in that we don't use properties dialog
+    //  unless we are editing an existing line's attributes
+    //  We get the last-used attributes from the prefs and insert immediately
+
+    var tagName = "hr";
+    var editor = GetCurrentEditor();
+      
+    var hLine;
+    try {
+      hLine = editor.getSelectedElement(tagName);
+    } catch (e) {return;}
+
+    if (hLine)
+    {
+      // We only open the dialog for an existing HRule
+      window.openDialog("chrome://editor/content/EdHLineProps.xul", "_blank", "chrome,close,titlebar,modal");
+      window.content.focus();
+    } 
+    else
+    {
+      try {
+        hLine = editor.createElementWithDefaults(tagName);
+
+        // We change the default attributes to those saved in the user prefs
+        var prefs = GetPrefs();
+        var align = prefs.getIntPref("editor.hrule.align");
+        if (align == 0)
+          editor.setAttributeOrEquivalent(hLine, "align", "left", true);
+        else if (align == 2)
+          editor.setAttributeOrEquivalent(hLine, "align", "right", true);
+
+        //Note: Default is center (don't write attribute)
+  
+        var width = prefs.getIntPref("editor.hrule.width");
+        var percent = prefs.getBoolPref("editor.hrule.width_percent");
+        if (percent)
+          width = width +"%";
+
+        editor.setAttributeOrEquivalent(hLine, "width", width, true);
+
+        var height = prefs.getIntPref("editor.hrule.height");
+        editor.setAttributeOrEquivalent(hLine, "size", String(height), true);
+
+        var shading = prefs.getBoolPref("editor.hrule.shading");
+        if (shading)
+          hLine.removeAttribute("noshade");
+        else
+          hLine.setAttribute("noshade", "noshade");
+
+        editor.insertElementAtSelection(hLine, true);
+
+      } catch (e) {}
+    }
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsLinkCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    // If selected element is an image, launch that dialog instead 
+    // since last tab panel handles link around an image
+    var element = GetObjectForProperties();
+    if (element && element.nodeName.toLowerCase() == "img")
+      window.openDialog("chrome://editor/content/EdImageProps.xul","_blank", "chrome,close,titlebar,modal", null, true);
+    else
+      window.openDialog("chrome://editor/content/EdLinkProps.xul","_blank", "chrome,close,titlebar,modal");
+    window.content.focus();
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsAnchorCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    window.openDialog("chrome://editor/content/EdNamedAnchorProps.xul", "_blank", "chrome,close,titlebar,modal", "");
+    window.content.focus();
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsInsertHTMLWithDialogCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    window.openDialog("chrome://editor/content/EdInsSrc.xul","_blank", "chrome,close,titlebar,modal,resizable", "");
+    window.content.focus();
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsInsertCharsCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    EditorFindOrCreateInsertCharWindow();
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsInsertBreakCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    try {
+      GetCurrentEditor().insertHTML("<br>");
+    } catch (e) {}
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsInsertBreakAllCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    try {
+      GetCurrentEditor().insertHTML("<br clear='all'>");
+    } catch (e) {}
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsGridCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    window.openDialog("chrome://editor/content/EdSnapToGrid.xul","_blank", "chrome,close,titlebar,modal");
+    window.content.focus();
+  }
+};
+
+
+//-----------------------------------------------------------------------------------
+var nsListPropertiesCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    window.openDialog("chrome://editor/content/EdListProps.xul","_blank", "chrome,close,titlebar,modal");
+    window.content.focus();
+  }
+};
+
+
+//-----------------------------------------------------------------------------------
+var nsPagePropertiesCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    var oldTitle = GetDocumentTitle();
+    window.openDialog("chrome://editor/content/EdPageProps.xul","_blank", "chrome,close,titlebar,modal", "");
+
+    // Update main window title and 
+    // recent menu data in prefs if doc title changed
+    if (GetDocumentTitle() != oldTitle)
+      UpdateWindowTitle();
+
+    window.content.focus();
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsObjectPropertiesCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    var isEnabled = false;
+    if (IsDocumentEditable() && IsEditingRenderedHTML())
+    {
+      isEnabled = (GetObjectForProperties() != null ||
+                   GetCurrentEditor().getSelectedElement("href") != null);
+    }
+    return isEnabled;
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    // Launch Object properties for appropriate selected element 
+    var element = GetObjectForProperties();
+    if (element)
+    {
+      var name = element.nodeName.toLowerCase();
+      switch (name)
+      {
+        case 'img':
+          goDoCommand("cmd_image");
+          break;
+        case 'hr':
+          goDoCommand("cmd_hline");
+          break;
+        case 'form':
+          goDoCommand("cmd_form");
+          break;
+        case 'input':
+          var type = element.getAttribute("type");
+          if (type && type.toLowerCase() == "image")
+            goDoCommand("cmd_inputimage");
+          else
+            goDoCommand("cmd_inputtag");
+          break;
+        case 'textarea':
+          goDoCommand("cmd_textarea");
+          break;
+        case 'select':
+          goDoCommand("cmd_select");
+          break;
+        case 'button':
+          goDoCommand("cmd_button");
+          break;
+        case 'label':
+          goDoCommand("cmd_label");
+          break;
+        case 'fieldset':
+          goDoCommand("cmd_fieldset");
+          break;
+        case 'table':
+          EditorInsertOrEditTable(false);
+          break;
+        case 'td':
+        case 'th':
+          EditorTableCellProperties();
+          break;
+        case 'ol':
+        case 'ul':
+        case 'dl':
+        case 'li':
+          goDoCommand("cmd_listProperties");
+          break;
+        case 'a':
+          if (element.name)
+          {
+            goDoCommand("cmd_anchor");
+          }
+          else if(element.href)
+          {
+            goDoCommand("cmd_link");
+          }
+          break;
+        default:
+          doAdvancedProperties(element);
+          break;
+      }
+    } else {
+      // We get a partially-selected link if asked for specifically
+      try {
+        element = GetCurrentEditor().getSelectedElement("href");
+      } catch (e) {}
+      if (element)
+        goDoCommand("cmd_link");
+    }
+    window.content.focus();
+  }
+};
+
+
+//-----------------------------------------------------------------------------------
+var nsSetSmiley =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon)
+  {
+    var smileyCode = aParams.getStringValue("state_attribute");
+
+    var strSml;
+    switch(smileyCode)
+    {
+        case ":-)": strSml="s1";
+        break;
+        case ":-(": strSml="s2";
+        break;
+        case ";-)": strSml="s3";
+        break;
+        case ":-P":
+        case ":-p":
+        case ":-b": strSml="s4";
+        break;
+        case ":-D": strSml="s5";
+        break;
+        case ":-[": strSml="s6";
+        break;
+        case ":-/":
+        case ":/":
+        case ":-\\":
+        case ":\\": strSml="s7";
+        break;
+        case "=-O":
+        case "=-o": strSml="s8";
+        break;
+        case ":-*": strSml="s9";
+        break;
+        case ">:o":
+        case ">:-o": strSml="s10";
+        break;
+        case "8-)": strSml="s11";
+        break;
+        case ":-$": strSml="s12";
+        break;
+        case ":-!": strSml="s13";
+        break;
+        case "O:-)":
+        case "o:-)": strSml="s14";
+        break;
+        case ":'(": strSml="s15";
+        break;
+        case ":-X":
+        case ":-x": strSml="s16";
+        break;
+        default:	strSml="";
+        break;
+    }
+
+    try
+    {
+      var editor = GetCurrentEditor();
+      var selection = editor.selection;
+      var extElement = editor.createElementWithDefaults("span");
+      extElement.setAttribute("class", "moz-smiley-" + strSml);
+
+      var intElement = editor.createElementWithDefaults("span");
+      if (!intElement)
+        return;
+
+      //just for mailnews, because of the way it removes HTML
+      var smileButMenu = document.getElementById('smileButtonMenu');      
+      if (smileButMenu.getAttribute("padwithspace"))
+         smileyCode = " " + smileyCode + " ";
+
+      var txtElement =  editor.document.createTextNode(smileyCode);
+      if (!txtElement)
+        return;
+
+      intElement.appendChild (txtElement);
+      extElement.appendChild (intElement);
+
+
+      editor.insertElementAtSelection(extElement,true);
+      window.content.focus();		
+
+    } 
+    catch (e) 
+    {
+        dump("Exception occured in smiley InsertElementAtSelection\n");
+    }
+  },
+  // This is now deprecated in favor of "doCommandParams"
+  doCommand: function(aCommand) {}
+};
+
+
+function doAdvancedProperties(element)
+{
+  if (element)
+  {
+    window.openDialog("chrome://editor/content/EdAdvancedEdit.xul", "_blank", "chrome,close,titlebar,modal,resizable=yes", "", element);
+    window.content.focus();
+  }
+}
+
+var nsAdvancedPropertiesCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    // Launch AdvancedEdit dialog for the selected element
+    try {
+      var element = GetCurrentEditor().getSelectedElement("");
+      doAdvancedProperties(element);
+    } catch (e) {}
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsColorPropertiesCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    window.openDialog("chrome://editor/content/EdColorProps.xul","_blank", "chrome,close,titlebar,modal", ""); 
+    UpdateDefaultColors(); 
+    window.content.focus();
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsRemoveNamedAnchorsCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    // We could see if there's any link in selection, but it doesn't seem worth the work!
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    EditorRemoveTextProperty("name", "");
+    window.content.focus();
+  }
+};
+
+
+//-----------------------------------------------------------------------------------
+var nsEditLinkCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    // Not really used -- this command is only in context menu, and we do enabling there
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    try {
+      var element = GetCurrentEditor().getSelectedElement("href");
+      if (element)
+        editPage(element.href, window, false);
+    } catch (e) {}
+    window.content.focus();
+  }
+};
+
+
+//-----------------------------------------------------------------------------------
+var nsNormalModeCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return IsHTMLEditor() && IsDocumentEditable();
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    SetEditMode(kDisplayModeNormal);
+  }
+};
+
+var nsAllTagsModeCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsHTMLEditor());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    SetEditMode(kDisplayModeAllTags);
+  }
+};
+
+var nsHTMLSourceModeCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsHTMLEditor());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    SetEditMode(kDisplayModeSource);
+  }
+};
+
+var nsPreviewModeCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsHTMLEditor());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    SetEditMode(kDisplayModePreview);
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsInsertOrEditTableCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return (IsDocumentEditable() && IsEditingRenderedHTML());
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    if (IsInTableCell())
+      EditorTableCellProperties();
+    else
+      EditorInsertOrEditTable(true);
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsEditTableCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return IsInTable();
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    EditorInsertOrEditTable(false);
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsSelectTableCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return IsInTable();
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    try {
+      GetCurrentTableEditor().selectTable();
+    } catch(e) {}
+    window.content.focus();
+  }
+};
+
+var nsSelectTableRowCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return IsInTableCell();
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    try {
+      GetCurrentTableEditor().selectTableRow();
+    } catch(e) {}
+    window.content.focus();
+  }
+};
+
+var nsSelectTableColumnCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return IsInTableCell();
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    try {
+      GetCurrentTableEditor().selectTableColumn();
+    } catch(e) {}
+    window.content.focus();
+  }
+};
+
+var nsSelectTableCellCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return IsInTableCell();
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    try {
+      GetCurrentTableEditor().selectTableCell();
+    } catch(e) {}
+    window.content.focus();
+  }
+};
+
+var nsSelectAllTableCellsCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return IsInTable();
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    try {
+      GetCurrentTableEditor().selectAllTableCells();
+    } catch(e) {}
+    window.content.focus();
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsInsertTableCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return IsDocumentEditable() && IsEditingRenderedHTML();
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    EditorInsertTable();
+  }
+};
+
+var nsInsertTableRowAboveCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return IsInTableCell();
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    try {
+      GetCurrentTableEditor().insertTableRow(1, false);
+    } catch(e) {}
+    window.content.focus();
+  }
+};
+
+var nsInsertTableRowBelowCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return IsInTableCell();
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    try {
+      GetCurrentTableEditor().insertTableRow(1, true);
+    } catch(e) {}
+    window.content.focus();
+  }
+};
+
+var nsInsertTableColumnBeforeCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return IsInTableCell();
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    try {
+      GetCurrentTableEditor().insertTableColumn(1, false);
+    } catch(e) {}
+    window.content.focus();
+  }
+};
+
+var nsInsertTableColumnAfterCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return IsInTableCell();
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    try {
+      GetCurrentTableEditor().insertTableColumn(1, true);
+    } catch(e) {}
+    window.content.focus();
+  }
+};
+
+var nsInsertTableCellBeforeCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return IsInTableCell();
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    try {
+      GetCurrentTableEditor().insertTableCell(1, false);
+    } catch(e) {}
+    window.content.focus();
+  }
+};
+
+var nsInsertTableCellAfterCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return IsInTableCell();
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    try {
+      GetCurrentTableEditor().insertTableCell(1, true);
+    } catch(e) {}
+    window.content.focus();
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsDeleteTableCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return IsInTable();
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    try {
+      GetCurrentTableEditor().deleteTable();
+    } catch(e) {}
+    window.content.focus();
+  }
+};
+
+var nsDeleteTableRowCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return IsInTableCell();
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    var rows = GetNumberOfContiguousSelectedRows();
+    // Delete at least one row
+    if (rows == 0)
+      rows = 1;
+
+    try {
+      var editor = GetCurrentTableEditor();
+      editor.beginTransaction();
+
+      // Loop to delete all blocks of contiguous, selected rows
+      while (rows)
+      {
+        editor.deleteTableRow(rows);
+        rows = GetNumberOfContiguousSelectedRows();
+      }
+    } finally { editor.endTransaction(); }
+    window.content.focus();
+  }
+};
+
+var nsDeleteTableColumnCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return IsInTableCell();
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    var columns = GetNumberOfContiguousSelectedColumns();
+    // Delete at least one column
+    if (columns == 0)
+      columns = 1;
+
+    try {
+      var editor = GetCurrentTableEditor();
+      editor.beginTransaction();
+
+      // Loop to delete all blocks of contiguous, selected columns
+      while (columns)
+      {
+        editor.deleteTableColumn(columns);
+        columns = GetNumberOfContiguousSelectedColumns();
+      }
+    } finally { editor.endTransaction(); }
+    window.content.focus();
+  }
+};
+
+var nsDeleteTableCellCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return IsInTableCell();
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    try {
+      GetCurrentTableEditor().deleteTableCell(1);   
+    } catch (e) {}
+    window.content.focus();
+  }
+};
+
+var nsDeleteTableCellContentsCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return IsInTableCell();
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    try {
+      GetCurrentTableEditor().deleteTableCellContents();
+    } catch (e) {}
+    window.content.focus();
+  }
+};
+
+
+//-----------------------------------------------------------------------------------
+var nsNormalizeTableCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return IsInTable();
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    // Use nsnull to let editor find table enclosing current selection
+    try {
+      GetCurrentTableEditor().normalizeTable(null);   
+    } catch (e) {}
+    window.content.focus();
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsJoinTableCellsCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    if (IsDocumentEditable() && IsEditingRenderedHTML())
+    {
+      try {
+        var editor = GetCurrentTableEditor();
+        var tagNameObj = { value: "" };
+        var countObj = { value: 0 };
+        var cell = editor.getSelectedOrParentTableElement(tagNameObj, countObj);
+
+        // We need a cell and either > 1 selected cell or a cell to the right
+        //  (this cell may originate in a row spanned from above current row)
+        // Note that editor returns "td" for "th" also.
+        // (this is a pain! Editor and gecko use lowercase tagNames, JS uses uppercase!)
+        if( cell && (tagNameObj.value == "td"))
+        {
+          // Selected cells
+          if (countObj.value > 1) return true;
+
+          var colSpan = cell.getAttribute("colspan");
+
+          // getAttribute returns string, we need number
+          // no attribute means colspan = 1
+          if (!colSpan)
+            colSpan = Number(1);
+          else
+            colSpan = Number(colSpan);
+
+          var rowObj = { value: 0 };
+          var colObj = { value: 0 };
+          editor.getCellIndexes(cell, rowObj, colObj);
+
+          // Test if cell exists to the right of current cell
+          // (cells with 0 span should never have cells to the right
+          //  if there is, user can select the 2 cells to join them)
+          return (colSpan && editor.getCellAt(null, rowObj.value,
+                                              colObj.value + colSpan));
+        }
+      } catch (e) {}
+    }
+    return false;
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    // Param: Don't merge non-contiguous cells
+    try {
+      GetCurrentTableEditor().joinTableCells(false);
+    } catch (e) {}
+    window.content.focus();
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsSplitTableCellCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    if (IsDocumentEditable() && IsEditingRenderedHTML())
+    {
+      var tagNameObj = { value: "" };
+      var countObj = { value: 0 };
+      var cell;
+      try {
+        cell = GetCurrentTableEditor().getSelectedOrParentTableElement(tagNameObj, countObj);
+      } catch (e) {}
+
+      // We need a cell parent and there's just 1 selected cell 
+      // or selection is entirely inside 1 cell
+      if ( cell && (tagNameObj.value == "td") && 
+           countObj.value <= 1 &&
+           IsSelectionInOneCell() )
+      {
+        var colSpan = cell.getAttribute("colspan");
+        var rowSpan = cell.getAttribute("rowspan");
+        if (!colSpan) colSpan = 1;
+        if (!rowSpan) rowSpan = 1;
+        return (colSpan > 1  || rowSpan > 1 ||
+                colSpan == 0 || rowSpan == 0);
+      }
+    }
+    return false;
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    try {
+      GetCurrentTableEditor().splitTableCell();
+    } catch (e) {}
+    window.content.focus();
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsTableOrCellColorCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return IsInTable();
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    EditorSelectColor("TableOrCell");
+  }
+};
+
+//-----------------------------------------------------------------------------------
+var nsPreferencesCommand =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return true;
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    goPreferences('composer_pane');
+    window.content.focus();
+  }
+};
+
+
+var nsFinishHTMLSource =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return true;
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    // In editor.js
+    FinishHTMLSource();
+  }
+};
+
+var nsCancelHTMLSource =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    return true;
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    // In editor.js
+    CancelHTMLSource();
+  }
+};
+
+var nsConvertToTable =
+{
+  isCommandEnabled: function(aCommand, dummy)
+  {
+    if (IsDocumentEditable() && IsEditingRenderedHTML())
+    {
+      var selection;
+      try {
+        selection = GetCurrentEditor().selection;
+      } catch (e) {}
+
+      if (selection && !selection.isCollapsed)
+      {
+        // Don't allow if table or cell is the selection
+        var element;
+        try {
+          element = GetCurrentEditor().getSelectedElement("");
+        } catch (e) {}
+        if (element)
+        {
+          var name = element.nodeName.toLowerCase();
+          if (name == "td" ||
+              name == "th" ||
+              name == "caption" ||
+              name == "table")
+            return false;
+        }
+
+        // Selection start and end must be in the same cell
+        //   in same cell or both are NOT in a cell
+        if ( GetParentTableCell(selection.focusNode) !=
+             GetParentTableCell(selection.anchorNode) )
+          return false
+      
+        return true;
+      }
+    }
+    return false;
+  },
+
+  getCommandStateParams: function(aCommand, aParams, aRefCon) {},
+  doCommandParams: function(aCommand, aParams, aRefCon) {},
+
+  doCommand: function(aCommand)
+  {
+    if (this.isCommandEnabled())
+    {
+      window.openDialog("chrome://editor/content/EdConvertToTable.xul","_blank", "chrome,close,titlebar,modal")
+    }
+    window.content.focus();
+  }
+};
+
new file mode 100644
--- /dev/null
+++ b/editor/ui/composer/content/EditorAllTags.css
@@ -0,0 +1,835 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator client code, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* Styles to alter look of things in the Editor content window 
+ *  for the "All Tags Edit Mode" Every HTML tag shows up as an icon.
+*/
+
+/* For "userdefined" or "unknown" tags
+   (Note that "_" must be escaped)
+*/
+
+*:not(a):not(abbr):not(acronym):not(address):not(applet):not(area):not(b):not(base):not(basefont):not(bdo):not(bgsound):not(big):not(blink):not(blockquote):not(body):not(br):not(button):not(canvas):not(caption):not(center):not(cite):not(code):not(col):not(colgroup):not(dd):not(del):not(dfn):not(dir):not(div):not(dl):not(dt):not(em):not(embed):not(fieldset):not(font):not(form):not(frame):not(frameset):not(h1):not(h2):not(h3):not(h4):not(h5):not(h6):not(head):not(hr):not(html):not(i):not(iframe):not(image):not(img):not(input):not(ins):not(isindex):not(kbd):not(keygen):not(label):not(legend):not(li):not(link):not(listing):not(map):not(marquee):not(menu):not(meta):not(multicol):not(nobr):not(noembed):not(noframes):not(noscript):not(object):not(ol):not(optgroup):not(option):not(p):not(param):not(plaintext):not(pre):not(q):not(s):not(samp):not(script):not(select):not(server):not(small):not(sound):not(spacer):not(span):not(strike):not(strong):not(style):not(sub):not(sup):not(table):not(tbody):not(td):not(textarea):not(tfoot):not(th):not(thead):not(title):not(tr):not(tt):not(u):not(ul):not(var):not(wbr):not(xmp) {
+  display: inline;
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 16px; 
+  background-image: url(chrome://editor/content/images/tag-userdefined.gif);
+  background-repeat: no-repeat;
+  background-position: top left;
+}
+
+a:not([\_moz_anonclass]) {
+  min-height: 16px; margin-left: 2px; margin-top: 2px;
+  padding-left: 20px; 
+  background-image: url(chrome://editor/content/images/tag-a.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+abbr {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 35px; 
+  background-image: url(chrome://editor/content/images/tag-abr.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+
+acronym {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 57px; 
+  background-image: url(chrome://editor/content/images/tag-acr.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+address {
+  min-height: 44px; margin-top: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-adr.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+applet {
+  min-height: 35px; margin-top: 2px;
+  padding-left: 47px; 
+  background-image: url(chrome://editor/content/images/tag-app.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+area {
+  min-height: 35px; margin-top: 2px;
+  padding-left: 39px; 
+  background-image: url(chrome://editor/content/images/tag-ara.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+b {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 20px; 
+  background-image: url(chrome://editor/content/images/tag-b.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+basefont {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 57px; 
+  background-image: url(chrome://editor/content/images/tag-bsf.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+bdo {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 31px; 
+  background-image: url(chrome://editor/content/images/tag-bdo.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+big {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 31px; 
+  background-image: url(chrome://editor/content/images/tag-big.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+blockquote {
+  min-height: 44px; margin-left: 2px; margin-top: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-blq.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+body {
+  min-height: 36px; margin-left: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-body.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+br {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 23px; 
+  background-image: url(chrome://editor/content/images/tag-br.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+button {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 57px; 
+  background-image: url(chrome://editor/content/images/tag-btn.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+caption {
+  min-height: 35px; margin-top: 2px;
+  padding-left: 55px; 
+  background-image: url(chrome://editor/content/images/tag-cpt.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+center {
+  min-height: 44px; margin-top: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-ctr.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+cite {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 39px; 
+  background-image: url(chrome://editor/content/images/tag-cit.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+code {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 39px; 
+  background-image: url(chrome://editor/content/images/tag-cod.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+col {
+  min-height: 35px; margin-left: 2px;
+  padding-left: 31px; 
+  background-image: url(chrome://editor/content/images/tag-col.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+colgroup {
+  min-height: 35px; margin-left: 2px;
+  padding-left: 51px; 
+  background-image: url(chrome://editor/content/images/tag-clg.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+dd {
+  min-height: 35px; margin-top: 2px;
+  padding-left: 23px; 
+  background-image: url(chrome://editor/content/images/tag-dd.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+del {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 31px; 
+  background-image: url(chrome://editor/content/images/tag-del.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+dfn {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 31px; 
+  background-image: url(chrome://editor/content/images/tag-dfn.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+dir {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 31px; 
+  background-image: url(chrome://editor/content/images/tag-dir.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+div {
+  min-height: 24px; margin-top: 2px;
+  /* TEMPORARY TO COMPENSATE FOR BUG */
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-div.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+input div {
+  min-height: 0px; margin-left: 0px; margin-top: 0px;
+  padding-left: 0px; 
+  background-image: none;
+}
+
+dl {
+  min-height: 20px; margin-top: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-dl.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+dt {
+  min-height: 35px; margin-top: 2px;
+  padding-left: 23px; 
+  background-image: url(chrome://editor/content/images/tag-dt.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+em {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 23px; 
+  background-image: url(chrome://editor/content/images/tag-em.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+fieldset {
+  min-height: 44px; margin-top: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-fld.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+font {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 39px; 
+  background-image: url(chrome://editor/content/images/tag-fnt.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+form {
+  min-height: 36px; margin-top: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-for.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+frame {
+  min-height: 40px; margin-left: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-frm.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+frameset {
+  min-height: 44px; margin-left: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-fst.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+h1 {
+  min-height: 20px; margin-top: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-h1.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+h2 {
+  min-height: 20px; margin-top: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-h2.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+h3 {
+  min-height: 20px; margin-top: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-h3.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+h4 {
+  min-height: 20px; margin-top: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-h4.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+h5 {
+  min-height: 20px; margin-top: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-h5.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+h6 {
+  min-height: 20px; margin-top: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-h6.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+hr {
+  min-height: 20px; margin-top: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-hr.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+i {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 20px; 
+  background-image: url(chrome://editor/content/images/tag-i.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+iframe {
+  min-height: 35px; margin-left: 2px;
+  padding-left: 47px; 
+  background-image: url(chrome://editor/content/images/tag-ifr.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+img:not([\_moz_anonclass]) {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 31px; 
+  background-image: url(chrome://editor/content/images/tag-img.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+input {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 39px; 
+  background-image: url(chrome://editor/content/images/tag-inp.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+ins {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 31px; 
+  background-image: url(chrome://editor/content/images/tag-ins.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+isindex {
+  min-height: 40px; margin-left: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-isx.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+kbd {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 31px; 
+  background-image: url(chrome://editor/content/images/tag-kbd.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+label {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 39px; 
+  background-image: url(chrome://editor/content/images/tag-lbl.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+legend {
+  min-height: 35px; margin-top: 2px;
+  padding-left: 49px; 
+  background-image: url(chrome://editor/content/images/tag-lgn.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+li {
+  min-height: 35px; margin-top: 2px;
+  padding-left: 23px; 
+  background-image: url(chrome://editor/content/images/tag-li.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+listing {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 57px; 
+  background-image: url(chrome://editor/content/images/tag-lst.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+map {
+  min-height: 35px; margin-left: 2px;
+  padding-left: 31px; 
+  background-image: url(chrome://editor/content/images/tag-map.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+menu {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 39px; 
+  background-image: url(chrome://editor/content/images/tag-men.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+nobr {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 39px; 
+  background-image: url(chrome://editor/content/images/tag-nbr.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+noframes {
+  min-height: 44px; margin-left: 2px; margin-top: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-nfr.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+noscript {
+  min-height: 44px; margin-left: 2px; margin-top: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-nsc.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+object {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 49px; 
+  background-image: url(chrome://editor/content/images/tag-obj.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+ol {
+  min-height: 38px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-ol.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+optgroup {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 51px; 
+  background-image: url(chrome://editor/content/images/tag-opg.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+option {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 47px; 
+  background-image: url(chrome://editor/content/images/tag-opt.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+p {
+  min-height: 38px; margin-top: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-p.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+param {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 43px; 
+  background-image: url(chrome://editor/content/images/tag-prm.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+plaintext {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 57px; 
+  background-image: url(chrome://editor/content/images/tag-pln.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+pre {
+  min-height: 24px; margin-top: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-pre.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+q {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 20px; 
+  background-image: url(chrome://editor/content/images/tag-q.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+s {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 20px; 
+  background-image: url(chrome://editor/content/images/tag-s.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+samp {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 39px; 
+  background-image: url(chrome://editor/content/images/tag-smp.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+script {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 45px; 
+  background-image: url(chrome://editor/content/images/tag-scr.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+select {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 47px; 
+  background-image: url(chrome://editor/content/images/tag-slc.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+small {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 41px; 
+  background-image: url(chrome://editor/content/images/tag-sml.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+span:not([\_moz_anonclass]) {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  /* TEMPORARY TO COMPENSATE FOR BUG */
+  padding-left: 39px; 
+  background-image: url(chrome://editor/content/images/tag-spn.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+strike {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 45px; 
+  background-image: url(chrome://editor/content/images/tag-stk.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+strong {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 51px; 
+  background-image: url(chrome://editor/content/images/tag-stn.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+sub {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 31px; 
+  background-image: url(chrome://editor/content/images/tag-sub.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+sup {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 31px; 
+  background-image: url(chrome://editor/content/images/tag-sup.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+/* The background image technique is not working for 
+   some table elements. Trying the "before" strategy
+*/
+
+table {
+  min-height: 40px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-tbl.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+tbody {
+  min-height: 42px; margin-left: 2px; margin-top: 1px;
+  padding-left: 17px; 
+  content: url(chrome://editor/content/images/tag-tbd.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+td {
+  min-height: 22px; margin-left: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-td.gif);
+
+  background-repeat: no-repeat;
+  background-position: top left;
+}
+
+textarea {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 59px; 
+  background-image: url(chrome://editor/content/images/tag-txt.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+tfoot {
+  min-height: 42px; margin-left: 2px; margin-top: 1px;
+  padding-left: 17px; 
+  content: url(chrome://editor/content/images/tag-tft.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+th {
+  min-height: 22px; margin-left: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-th.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+thead {
+  min-height: 42px; margin-left: 2px; margin-top: 1px;
+  padding-left: 17px; 
+  content: url(chrome://editor/content/images/tag-thd.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+tr {
+  min-height: 22px; margin-left: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-tr.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+tt {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 23px; 
+  background-image: url(chrome://editor/content/images/tag-tt.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+u {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 23px; 
+  background-image: url(chrome://editor/content/images/tag-u.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+ul {
+  min-height: 20px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-ul.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+var {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 31px; 
+  background-image: url(chrome://editor/content/images/tag-var.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+xmp {
+  min-height: 35px; margin-left: 2px; margin-top: 2px;
+  padding-left: 31px; 
+  background-image: url(chrome://editor/content/images/tag-xmp.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+
+/* These are tags that we DON'T want to show icons for.
+   We have images for them in case we want to utilize them
+   for some other purpose than the "All Tags" editor mode
+
+html {
+  min-height: 36px; margin-left: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-html.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+head {
+  min-height: 36px; margin-left: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-hed.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+These are tags that are ONLY allowed as children of HEAD:
+
+title {
+  min-height: 40px; margin-left: 2px; margin-top: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-ttl.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+base {
+  min-height: 36px; margin-left: 2px; margin-top: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-bas.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+style {
+  min-height: 40px; margin-left: 2px; margin-top: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-stl.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+meta {
+  min-height: 36px; margin-left: 2px; margin-top: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-met.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+link {
+  min-height: 30px; margin-left: 2px; margin-top: 2px;
+  padding-left: 17px; 
+  background-image: url(chrome://editor/content/images/tag-lnk.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+*/
new file mode 100644
--- /dev/null
+++ b/editor/ui/composer/content/EditorContent.css
@@ -0,0 +1,96 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator client code, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Daniel Glazman <glazman@netscape.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* Styles to alter look of things in the Editor content window 
+ *  for the "Normal Edit Mode" These settings will be removed
+ *  when we display in completely WYSIWYG "Edit Preview" mode
+ *  Anything that should never change, like cursors, should be 
+ *  place in EditorOverride.css, instead of here.
+*/
+
+@import url(chrome://communicator/skin/smileys.css);
+
+a[name] {
+  min-height: 17px; margin-left: 2px; margin-top: 2px;
+  padding-left: 20px; 
+  background-image: url(chrome://editor/content/images/tag-anchor.gif);
+  background-repeat: no-repeat; 
+  background-position: top left;
+}
+
+/* Force border display for empty cells 
+   and tables with 0 border
+*/
+table {
+  empty-cells: show;
+}
+
+/* give a red dotted border to tables and cells with no border
+   otherwise they are invisible
+*/
+table[empty-cells],
+  table[border="0"],
+  /* next two selectors on line below for the case where tbody is omitted */
+  table[border="0"] > tr > td, table[border="0"] > tr > th,
+  table[border="0"] > thead > tr > td, table[border="0"] > tbody > tr > td, table[border="0"] > tfoot > tr > td,
+  table[border="0"] > thead > tr > th, table[border="0"] > tbody > tr > th, table[border="0"] > tfoot > tr > th,
+  table:not([border]),
+  /* next two selectors on line below for the case where tbody is omitted */
+  table:not([border]) > tr > td, table:not([border]) > tr >  th,
+  table:not([border]) > thead > tr > td, table:not([border]) > tbody > tr > td, table:not([border]) > tfoot > tr > td,
+  table:not([border]) > thead > tr > th, table:not([border]) > tbody > tr > th, table:not([border]) > tfoot > tr > th
+{
+  border: 1px dotted red;
+}
+
+/* give a green dashed border to forms otherwise they are invisible
+*/
+form
+{
+  border: 2px dashed green;
+}
+/* give a green dotted border to labels otherwise they are invisible
+*/
+label
+{
+  border: 1px dotted green;
+}
+
+img {
+  -moz-force-broken-image-icon: 1;
+}
new file mode 100644
--- /dev/null
+++ b/editor/ui/composer/content/EditorContextMenu.js
@@ -0,0 +1,172 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator client code, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Charles Manske (cmanske@netscape.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+function EditorFillContextMenu(event, contextMenuNode)
+{
+  if ( event.target != contextMenuNode )
+    return;
+
+  // Setup object property menuitem
+  var objectName = InitObjectPropertiesMenuitem("objectProperties_cm");
+  var isInLink = objectName == "href";
+
+  // Special case of an image inside a link
+  if (objectName == "img")
+  try {
+    isInLink = GetCurrentEditor().getElementOrParentByTagName("href", GetObjectForProperties());
+  } catch (e) {}
+
+  InitRemoveStylesMenuitems("removeStylesMenuitem_cm", "removeLinksMenuitem_cm", "removeNamedAnchorsMenuitem_cm");
+
+  var inCell = IsInTableCell();
+  // Set appropriate text for join cells command
+  InitJoinCellMenuitem("joinTableCells_cm");
+
+  // Update enable states for all table commands
+  goUpdateTableMenuItems(document.getElementById("composerTableMenuItems"));
+
+  // Loop through all children to hide disabled items
+  var children = contextMenuNode.childNodes;
+  if (children)
+  {
+    var count = children.length;
+    for (var i = 0; i < count; i++)
+      HideDisabledItem(children[i]);
+  }
+
+  // The above loop will always show all separators and the next two items
+  // Hide "Create Link" if in a link
+  ShowMenuItem("createLink_cm", !isInLink);
+
+  // Hide "Edit link in new Composer" unless in a link
+  ShowMenuItem("editLink_cm", isInLink);
+
+  // Remove separators if all items in immediate group above are hidden
+  // A bit complicated to account if multiple groups are completely hidden!
+  var haveUndo =
+    IsMenuItemShowing("menu_undo_cm") ||
+    IsMenuItemShowing("menu_redo_cm");
+
+  var haveEdit =
+    IsMenuItemShowing("menu_cut_cm")   ||
+    IsMenuItemShowing("menu_copy_cm")  ||
+    IsMenuItemShowing("menu_paste_cm") ||
+    IsMenuItemShowing("menu_pasteNoFormatting_cm") ||
+    IsMenuItemShowing("menu_delete_cm");
+
+  var haveStyle =
+    IsMenuItemShowing("removeStylesMenuitem_cm") ||
+    IsMenuItemShowing("createLink_cm") ||
+    IsMenuItemShowing("removeLinksMenuitem_cm") ||
+    IsMenuItemShowing("removeNamedAnchorsMenuitem_cm");
+
+  var haveProps =
+    IsMenuItemShowing("objectProperties_cm");
+
+  ShowMenuItem("undoredo-separator", haveUndo && haveEdit);
+
+  ShowMenuItem("edit-separator", haveEdit || haveUndo);
+
+  // Note: Item "menu_selectAll_cm" and
+  // following separator are ALWAYS enabled,
+  // so there will always be 1 separator here
+
+  var showStyleSep = haveStyle && (haveProps || inCell);
+  ShowMenuItem("styles-separator", showStyleSep);
+
+  var showPropSep = (haveProps && inCell);
+  ShowMenuItem("property-separator", showPropSep);
+
+  // Remove table submenus if not in table
+  ShowMenuItem("tableInsertMenu_cm",  inCell);
+  ShowMenuItem("tableSelectMenu_cm",  inCell);
+  ShowMenuItem("tableDeleteMenu_cm",  inCell);
+
+  // if we have a mispelled word, show spellchecker context
+  // menuitems as well as the usual context menu
+  InlineSpellCheckerUI.clearSuggestionsFromMenu();
+  InlineSpellCheckerUI.initFromEvent(document.popupRangeParent, document.popupRangeOffset);
+  var onMisspelling = InlineSpellCheckerUI.overMisspelling;
+  document.getElementById('spellCheckSuggestionsSeparator').hidden = !onMisspelling;
+  document.getElementById('spellCheckAddToDictionary').hidden = !onMisspelling;
+  document.getElementById('spellCheckIgnoreWord').hidden = !onMisspelling;
+  var separator = document.getElementById('spellCheckAddSep');
+  separator.hidden = !onMisspelling;
+  document.getElementById('spellCheckNoSuggestions').hidden = !onMisspelling ||
+      InlineSpellCheckerUI.addSuggestionsToMenu(contextMenuNode, separator, 5);
+}
+
+function IsItemOrCommandEnabled( item )
+{
+  var command = item.getAttribute("command");
+  if (command) {
+    // If possible, query the command controller directly
+    var controller = document.commandDispatcher.getControllerForCommand(command);
+    if (controller)
+      return controller.isCommandEnabled(command);
+  }
+
+  // Fall back on the inefficient observed disabled attribute
+  return item.getAttribute("disabled") != "true";
+}
+
+function HideDisabledItem( item )
+{
+  item.hidden = !IsItemOrCommandEnabled(item);
+}
+
+function ShowMenuItem(id, showItem)
+{
+  var item = document.getElementById(id);
+  if (item && !showItem)
+  {
+    item.hidden = true;
+  }
+  // else HideDisabledItem showed the item anyway
+}
+
+function IsMenuItemShowing(menuID)
+{
+  var item = document.getElementById(menuID);
+  if (item)
+    return !item.hidden;
+
+  return false;
+}
+
new file mode 100644
--- /dev/null
+++ b/editor/ui/composer/content/EditorContextMenuOverlay.xul
@@ -0,0 +1,164 @@
+<?xml version="1.0"?> 
+<!-- ***** BEGIN LICENSE BLOCK *****
+   - Version: MPL 1.1/GPL 2.0/LGPL 2.1
+   -
+   - The contents of this file are subject to the Mozilla Public License Version
+   - 1.1 (the "License"); you may not use this file except in compliance with
+   - the License. You may obtain a copy of the License at
+   - http://www.mozilla.org/MPL/
+   -
+   - Software distributed under the License is distributed on an "AS IS" basis,
+   - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+   - for the specific language governing rights and limitations under the
+   - License.
+   -
+   - The Original Code is Mozilla Communicator client code, released
+   - March 31, 1998.
+   -
+   - The Initial Developer of the Original Code is
+   - Netscape Communications Corporation.
+   - Portions created by the Initial Developer are Copyright (C) 2000
+   - the Initial Developer. All Rights Reserved.
+   -
+   - Contributor(s):
+   -   Daniel Glazman (glazman@netscape.com)
+   -   Charles Manske (cmanske@netscape.com)
+   -
+   - Alternatively, the contents of this file may be used under the terms of
+   - either of the GNU General Public License Version 2 or later (the "GPL"),
+   - or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+   - in which case the provisions of the GPL or the LGPL are applicable instead
+   - of those above. If you wish to allow use of your version of this file only
+   - under the terms of either the GPL or the LGPL, and not to allow others to
+   - use your version of this file under the terms of the MPL, indicate your
+   - decision by deleting the provisions above and replace them with the notice
+   - and other provisions required by the GPL or the LGPL. If you do not delete
+   - the provisions above, a recipient may use your version of this file under
+   - the terms of any one of the MPL, the GPL or the LGPL.
+   -
+   - ***** END LICENSE BLOCK ***** -->
+
+<!DOCTYPE overlay SYSTEM "chrome://editor/locale/editorOverlay.dtd">
+
+<overlay id="ComposerContextMenuOverlay"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<script type="application/x-javascript" src="chrome://editor/content/EditorContextMenu.js"/>
+<script type="application/x-javascript" src="chrome://editor/content/StructBarContextMenu.js"/>
+<script type="application/x-javascript" src="chrome://global/content/inlineSpellCheckUI.js"/>
+
+<popupset id="editorContentContextSet">
+ <popup id="editorContentContext"   
+   onpopupshowing="EditorFillContextMenu(event, this);"> 
+    <menuitem id="spellCheckNoSuggestions" label="&spellCheckNoSuggestions.label;" disabled="true"/>
+    <menuseparator id="spellCheckAddSep"/>
+    <menuitem id="spellCheckAddToDictionary"
+              label="&spellCheckAddToDictionary.label;"
+              accesskey="&spellCheckAddToDictionary.accesskey;"
+              oncommand="InlineSpellCheckerUI.addToDictionary();"/>
+    <menuitem id="spellCheckIgnoreWord"
+              label="&spellCheckIgnoreWord.label;"
+              accesskey="&spellCheckIgnoreWord.accesskey;"
+              oncommand="InlineSpellCheckerUI.ignoreWord();"/>
+    <menuseparator id="spellCheckSuggestionsSeparator"/>
+
+    <menuitem id="menu_undo_cm"   label="&undoCmd.label;" accesskey="&undo.accesskey;" command="cmd_undo"/>
+    <menuitem id="menu_redo_cm"   label="&redoCmd.label;" accesskey="&redo.accesskey;" command="cmd_redo"/>
+    <menuseparator id="undoredo-separator"/>
+    
+    <menuitem id="menu_cut_cm"    label="&cutCmd.label;"    accesskey="&cut.accesskey;" command="cmd_cut"/>
+    <menuitem id="menu_copy_cm"   label="&copyCmd.label;"   accesskey="&copy.accesskey;" command="cmd_copy"/>
+    <menuitem id="menu_paste_cm"  label="&pasteCmd.label;"  accesskey="&paste.accesskey;" command="cmd_paste"/>
+    <menuitem id="menu_pasteNoFormatting_cm" command="cmd_pasteNoFormatting"/>
+    <menuitem id="menu_delete_cm" label="&deleteCmd.label;" accesskey="&delete.accesskey;" command="cmd_delete"/>
+    <menuseparator id="edit-separator"/>
+    <menuitem id="menu_selectAll_cm" label="&selectAllCmd.label;" accesskey="&selectall.accesskey;" command="cmd_selectAll"/>
+    <menuseparator id="selectAll-separator"/>
+    
+    <!-- label and accesskey set at runtime from strings -->
+    <menuitem id="removeStylesMenuitem_cm"
+          observes="cmd_removeStyles"/>
+    <menuitem id="createLink_cm" label="&createLinkCmd.label;" accesskey="&createlink.accesskey;" command="cmd_link"/>
+    <!-- label and accesskey set at runtime from strings -->
+    <menuitem id="removeLinksMenuitem_cm" observes="cmd_removeLinks"/>
+    <menuitem id="removeNamedAnchorsMenuitem_cm"  label="&formatRemoveNamedAnchors.label;"   
+          accesskey="&formatRemoveNamedAnchors.accesskey;"
+          observes="cmd_removeNamedAnchors"/>
+    <menuseparator id="styles-separator"/>
+
+    <!-- label and accesskey are set in InitObjectProperties -->
+    <menuitem id="objectProperties_cm" observes="cmd_objectProperties"/>
+    <menuitem id="editLink_cm" label="&editLinkCmd.label;" accesskey="&editlink.accesskey;" command="cmd_editLink"/>
+    <menuseparator id="property-separator"/>
+
+    <!-- Can't get submenus to load from a shared overlay -->
+    <menu id="tableInsertMenu_cm" label="&tableInsertMenu2.label;" accesskey="&tableinsertmenu.accesskey;">
+      <menupopup>
+        <menuitem label="&insertTableCmd.label;"    accesskey="&tabletable.accesskey;"        observes="cmd_InsertTable"/>
+        <menuseparator />
+        <menuitem label="&tableRowAbove.label;"     accesskey="&tablerow.accesskey;"          observes="cmd_InsertRowAbove"/>
+        <menuitem label="&tableRowBelow.label;"     accesskey="&tablerowbelow.accesskey;"     observes="cmd_InsertRowBelow"/>
+        <menuseparator />
+        <menuitem label="&tableColumnBefore.label;" accesskey="&tablecolumn.accesskey;"       observes="cmd_InsertColumnBefore"/>
+        <menuitem label="&tableColumnAfter.label;"  accesskey="&tablecolumnafter.accesskey;"  observes="cmd_InsertColumnAfter"/>
+        <menuseparator />
+        <menuitem label="&tableCellBefore.label;"   accesskey="&tablecell.accesskey;"         observes="cmd_InsertCellBefore"/>
+        <menuitem label="&tableCellAfter.label;"    accesskey="&tablecellafter.accesskey;"    observes="cmd_InsertCellAfter"/>
+      </menupopup>
+    </menu>
+    <menu id="tableSelectMenu_cm" label="&tableSelectMenu2.label;" accesskey="&tableselectmenu.accesskey;">
+      <menupopup>
+        <menuitem id="menu_SelectTable_cm"    label="&tableTable.label;"    accesskey="&tabletable.accesskey;"    observes="cmd_SelectTable"    />
+        <menuitem id="menu_SelectRow_cm"      label="&tableRow.label;"      accesskey="&tablerow.accesskey;"      observes="cmd_SelectRow"      />
+        <menuitem id="menu_SelectColumn_cm"   label="&tableColumn.label;"   accesskey="&tablecolumn.accesskey;"   observes="cmd_SelectColumn"   />
+        <menuitem id="menu_SelectCell_cm"     label="&tableCell.label;"     accesskey="&tablecell.accesskey;"     observes="cmd_SelectCell"     />
+        <menuitem id="menu_SelectAllCells_cm" label="&tableAllCells.label;" accesskey="&tableallcells.accesskey;" observes="cmd_SelectAllCells" />
+      </menupopup>
+    </menu>
+    <menu id="tableDeleteMenu_cm" label="&tableDeleteMenu2.label;" accesskey="&tabledeletemenu.accesskey;">
+      <menupopup>
+        <menuitem id="menu_DeleteTable_cm"        label="&tableTable.label;"        accesskey="&tabletable.accesskey;"          observes="cmd_DeleteTable"/>
+        <menuitem id="menu_DeleteRow_cm"          label="&tableRows.label;"         accesskey="&tablerow.accesskey;"            observes="cmd_DeleteRow"/>
+        <menuitem id="menu_DeleteColumn_cm"       label="&tableColumns.label;"      accesskey="&tablecolumn.accesskey;"         observes="cmd_DeleteColumn"/>
+        <menuitem id="menu_DeleteCell_cm"         label="&tableCells.label;"        accesskey="&tablecell.accesskey;"           observes="cmd_DeleteCell"/>
+        <menuitem id="menu_DeleteCellContents_cm" label="&tableCellContents.label;" accesskey="&tablecellcontents.accesskey;"   observes="cmd_DeleteCellContents"/>
+      </menupopup>
+    </menu>
+    <!-- menu label is set in InitTableMenu -->
+    <menuitem id="joinTableCells_cm"   label="&tableJoinCells.label;" accesskey="&tablejoincells.accesskey;" observes="cmd_JoinTableCells"/>
+    <menuitem id="splitTableCell_cm"   label="&tableSplitCell.label;" accesskey="&tablesplitcell.accesskey;" observes="cmd_SplitTableCell"/>
+    <menuitem id="tableOrCellColor_cm" label="&tableOrCellColor.label;" accesskey="&tableOrCellColor.accesskey;" observes="cmd_TableOrCellColor"/>
+ </popup>
+
+ <popup id="editorSourceContext">
+   <menuitem label="&undoCmd.label;"      accesskey="&undo.accesskey;"      command="cmd_undo"/>
+   <menuitem label="&redoCmd.label;"      accesskey="&redo.accesskey;"      command="cmd_redo"/>
+   <menuseparator/>
+   <menuitem label="&cutCmd.label;"       accesskey="&cut.accesskey;"       command="cmd_cut"/>
+   <menuitem label="&copyCmd.label;"      accesskey="&copy.accesskey;"      command="cmd_copy"/>
+   <menuitem label="&pasteCmd.label;"     accesskey="&paste.accesskey;"     command="cmd_paste"/>
+   <menuitem label="&deleteCmd.label;"    accesskey="&delete.accesskey;"    command="cmd_delete"/>
+   <menuseparator/>
+   <menuitem label="&selectAllCmd.label;" accesskey="&selectall.accesskey;" command="cmd_selectAll"/>
+ </popup>
+
+ <popup id="structToolbarContext">
+   <menuitem id="structSelect"     label="&structSelect.label;"
+                                   accesskey="&structSelect.accesskey;"
+                                   oncommand="StructSelectTag()"/>
+   <menuseparator/>
+   <menuitem id="structRemoveTag"  label="&structRemoveTag.label;"
+                                   accesskey="&structRemoveTag.accesskey;"
+                                   oncommand="StructRemoveTag()"/>
+   <menuitem id="structChangeTag"  label="&structChangeTag.label;"
+                                   accesskey="&structChangeTag.accesskey;"
+                                   oncommand="StructChangeTag()"/>
+   <menuseparator/>
+   <menuitem id="advancedPropsTag" label="&advancedPropertiesCmd.label;"
+                                   accesskey="&advancedproperties.accesskey;"
+                                   oncommand="OpenAdvancedProperties()"/>
+ </popup>
+
+</popupset>
+
+</overlay>
new file mode 100644
--- /dev/null
+++ b/editor/ui/composer/content/EditorParagraphMarks.css
@@ -0,0 +1,49 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator client code, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* This adds the standard paragraph symbol 
+ *  after paragraphs (block container tags)
+*/
+
+p:after,br:after,
+h1:after,h2:after,h3:after,h4:after,h5:after,h6:after,
+address:after,blockquote:after,listing:after,
+plaintext:after, xmp:after, pre:after,
+li:after,dt:after,dd:after
+{
+  content: "\B6 ";
+}
new file mode 100644
--- /dev/null
+++ b/editor/ui/composer/content/StructBarContextMenu.js
@@ -0,0 +1,212 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *    Daniel Glazman (glazman@netscape.com), original author
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+var gContextMenuNode;
+var gContextMenuFiringDocumentElement;
+
+function InitStructBarContextMenu(button, docElement)
+{
+  gContextMenuFiringDocumentElement = docElement;
+  gContextMenuNode = button;
+
+  var tag = docElement.nodeName.toLowerCase();
+
+  var structRemoveTag = document.getElementById("structRemoveTag");
+  var enableRemove;
+
+  switch (tag) {
+    case "body":
+    case "tbody":
+    case "thead":
+    case "tfoot":
+    case "col":
+    case "colgroup":
+    case "tr":
+    case "th":
+    case "td":
+    case "caption":
+      enableRemove = false;
+      break;
+    default:
+      enableRemove = true;
+      break;
+  }
+  SetElementEnabled(structRemoveTag, enableRemove);
+
+  var structChangeTag = document.getElementById("structChangeTag");
+  SetElementEnabled(structChangeTag, (tag != "body"));
+}
+
+function TableCellFilter(node)
+{
+  switch (node.nodeName.toLowerCase())
+    {
+    case "td":
+    case "th":
+    case "caption":
+      return NodeFilter.FILTER_ACCEPT;
+      break;
+    default:
+      return NodeFilter.FILTER_SKIP;
+      break;
+    }
+  return NodeFilter.FILTER_SKIP;
+}
+
+function StructRemoveTag()
+{
+  var editor = GetCurrentEditor();
+  if (!editor) return;
+
+  var element = gContextMenuFiringDocumentElement;
+  var offset = 0;
+  var childNodes = element.parentNode.childNodes;
+
+  while (childNodes[offset] != element) {
+    ++offset;
+  }
+
+  editor.beginTransaction();
+
+  try {
+
+    var tag = element.nodeName.toLowerCase();
+    if (tag != "table") {
+      MoveChildNodesAfterElement(editor, element, element, offset);
+    }
+    else {
+
+      var nodeIterator = document.createTreeWalker(element,
+                                                   NodeFilter.SHOW_ELEMENT,
+                                                   TableCellFilter,
+                                                   true);
+      var node = nodeIterator.lastChild();
+      while (node) {
+        MoveChildNodesAfterElement(editor, node, element, offset);
+        node = nodeIterator.previousSibling();
+      }
+
+    }
+    editor.deleteNode(element);
+  }
+  catch (e) {};
+
+  editor.endTransaction();
+}
+
+function MoveChildNodesAfterElement(editor, element, targetElement, targetOffset)
+{
+  var childNodes = element.childNodes;
+  var childNodesLength = childNodes.length;
+  var i;
+  for (i = childNodesLength - 1; i >= 0; i--) {
+    var clone = childNodes.item(i).cloneNode(true);
+    editor.insertNode(clone, targetElement.parentNode, targetOffset + 1);
+  }
+}
+
+function StructChangeTag()
+{
+  var textbox = document.createElementNS(XUL_NS, "textbox");
+  textbox.setAttribute("value", gContextMenuNode.getAttribute("value"));
+  textbox.setAttribute("width", gContextMenuNode.boxObject.width);
+  textbox.className = "struct-textbox";
+
+  gContextMenuNode.parentNode.replaceChild(textbox, gContextMenuNode);
+
+  textbox.addEventListener("keypress", OnKeyPress, false);
+  textbox.addEventListener("blur", ResetStructToolbar, true);
+
+  textbox.select();
+}
+
+function StructSelectTag()
+{
+  SelectFocusNodeAncestor(gContextMenuFiringDocumentElement);
+}
+
+function OpenAdvancedProperties()
+{
+  doAdvancedProperties(gContextMenuFiringDocumentElement);
+}
+
+function OnKeyPress(event)
+{
+  var editor = GetCurrentEditor();
+
+  var keyCode = event.keyCode;
+  if (keyCode == 13) {
+    var newTag = event.target.value;
+
+    var element = gContextMenuFiringDocumentElement;
+
+    var offset = 0;
+    var childNodes = element.parentNode.childNodes;
+    while (childNodes.item(offset) != element) {
+      offset++;
+    }
+
+    editor.beginTransaction();
+
+    try {
+      var newElt = editor.document.createElement(newTag);
+      if (newElt) {
+        childNodes = element.childNodes;
+        var childNodesLength = childNodes.length;
+        var i;
+        for (i = 0; i < childNodesLength; i++) {
+          var clone = childNodes.item(i).cloneNode(true);
+          newElt.appendChild(clone);
+        }
+        editor.insertNode(newElt, element.parentNode, offset+1);
+        editor.deleteNode(element);
+        editor.selectElement(newElt);
+
+        window.content.focus();
+      }
+    }
+    catch (e) {}
+
+    editor.endTransaction();
+
+  }
+  else if (keyCode == 27) {
+    // if the user hits Escape, we discard the changes
+    window.content.focus();
+  }
+}
new file mode 100644
--- /dev/null
+++ b/editor/ui/composer/content/composerOverlay.xul
@@ -0,0 +1,48 @@
+<?xml version="1.0"?> 
+<!-- ***** BEGIN LICENSE BLOCK *****
+   - Version: MPL 1.1/GPL 2.0/LGPL 2.1
+   -
+   - The contents of this file are subject to the Mozilla Public License Version
+   - 1.1 (the "License"); you may not use this file except in compliance with
+   - the License. You may obtain a copy of the License at
+   - http://www.mozilla.org/MPL/
+   -
+   - Software distributed under the License is distributed on an "AS IS" basis,
+   - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+   - for the specific language governing rights and limitations under the
+   - License.
+   -
+   - The Original Code is Mozilla Communicator client code, released
+   - March 31, 1998.
+   -
+   - The Initial Developer of the Original Code is
+   - Netscape Communications Corporation.
+   - Portions created by the Initial Developer are Copyright (C) 1999
+   - the Initial Developer. All Rights Reserved.
+   -
+   - Contributor(s):
+   -   Charles Manske (cmanske@netscape.com)
+   -
+   - Alternatively, the contents of this file may be used under the terms of
+   - either of the GNU General Public License Version 2 or later (the "GPL"),
+   - or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+   - in which case the provisions of the GPL or the LGPL are applicable instead
+   - of those above. If you wish to allow use of your version of this file only
+   - under the terms of either the GPL or the LGPL, and not to allow others to
+   - use your version of this file under the terms of the MPL, indicate your
+   - decision by deleting the provisions above and replace them with the notice
+   - and other provisions required by the GPL or the LGPL. If you do not delete
+   - the provisions above, a recipient may use your version of this file under
+   - the terms of any one of the MPL, the GPL or the LGPL.
+   -
+   - ***** END LICENSE BLOCK ***** -->
+
+<overlay id="composerOverlay"
+         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+  <!-- Items in the Edit menu used only by Composer app -->
+  <menupopup id="edEditMenuPopup">
+    <menuitem id="menu_publishSettings" insertafter="menu_prefsSep"  observes="cmd_publishSettings"/>
+  </menupopup>
+
+</overlay>    
new file mode 100644
--- /dev/null
+++ b/editor/ui/composer/content/editor.js
@@ -0,0 +1,3468 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Sammy Ford (sford@swbell.net)
+ *   Dan Haddix (dan6992@hotmail.com)
+ *   John Ratke (jratke@owc.net)
+ *   Ryan Cassin (rcassin@supernova.org)
+ *   Daniel Glazman (glazman@netscape.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* Main Composer window UI control */
+
+var gComposerWindowControllerID = 0;
+var prefAuthorString = "";
+
+const kDisplayModeNormal = 0;
+const kDisplayModeAllTags = 1;
+const kDisplayModeSource = 2;
+const kDisplayModePreview = 3;
+const kDisplayModeMenuIDs = ["viewNormalMode", "viewAllTagsMode", "viewSourceMode", "viewPreviewMode"];
+const kDisplayModeTabIDS = ["NormalModeButton", "TagModeButton", "SourceModeButton", "PreviewModeButton"];
+const kNormalStyleSheet = "chrome://editor/content/EditorContent.css";
+const kAllTagsStyleSheet = "chrome://editor/content/EditorAllTags.css";
+const kParagraphMarksStyleSheet = "chrome://editor/content/EditorParagraphMarks.css";
+const kContentEditableStyleSheet = "resource://gre/res/contenteditable.css";
+
+const kTextMimeType = "text/plain";
+const kHTMLMimeType = "text/html";
+
+const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
+
+var gPreviousNonSourceDisplayMode = 1;
+var gEditorDisplayMode = -1;
+var gDocWasModified = false;  // Check if clean document, if clean then unload when user "Opens"
+var gContentWindow = 0;
+var gSourceContentWindow = 0;
+var gSourceTextEditor = null;
+var gContentWindowDeck;
+var gFormatToolbar;
+var gFormatToolbarHidden = false;
+var gViewFormatToolbar;
+var gColorObj = { LastTextColor:"", LastBackgroundColor:"", LastHighlightColor:"",
+                  Type:"", SelectedType:"", NoDefault:false, Cancel:false,
+                  HighlightColor:"", BackgroundColor:"", PageColor:"",
+                  TextColor:"", TableColor:"", CellColor:""
+                };
+var gDefaultTextColor = "";
+var gDefaultBackgroundColor = "";
+var gCSSPrefListener;
+var gEditorToolbarPrefListener;
+var gReturnInParagraphPrefListener;
+var gPrefs;
+var gLocalFonts = null;
+
+var gLastFocusNode = null;
+var gLastFocusNodeWasSelected = false;
+
+// These must be kept in synch with the XUL <options> lists
+var gFontSizeNames = ["xx-small","x-small","small","medium","large","x-large","xx-large"];
+
+const nsIFilePicker = Components.interfaces.nsIFilePicker;
+
+const kEditorToolbarPrefs = "editor.toolbars.showbutton.";
+const kUseCssPref         = "editor.use_css";
+const kCRInParagraphsPref = "editor.CR_creates_new_p";
+
+function ShowHideToolbarSeparators(toolbar) {
+  var childNodes = toolbar.childNodes;
+  var separator = null;
+  var hideSeparator = true;
+  for (var i = 0; childNodes[i].localName != "spacer"; i++) {
+    if (childNodes[i].localName == "toolbarseparator") {
+      if (separator)
+        separator.hidden = true;
+      separator = childNodes[i];
+    } else if (!childNodes[i].hidden) {
+      if (separator)
+        separator.hidden = hideSeparator;
+      separator = null;
+      hideSeparator = false;
+    }
+  }
+}
+
+function ShowHideToolbarButtons()
+{
+  var array = gPrefs.getChildList(kEditorToolbarPrefs, {});
+  for (var i in array) {
+    var prefName = array[i];
+    var id = prefName.substr(kEditorToolbarPrefs.length) + "Button";
+    var button = document.getElementById(id);
+    if (button)
+      button.hidden = !gPrefs.getBoolPref(prefName);
+  }
+  ShowHideToolbarSeparators(document.getElementById("EditToolbar"));
+  ShowHideToolbarSeparators(document.getElementById("FormatToolbar"));
+}
+  
+function nsPrefListener(prefName)
+{
+  this.startup(prefName);
+}
+
+// implements nsIObserver
+nsPrefListener.prototype =
+{
+  domain: "",
+  startup: function(prefName)
+  {
+    this.domain = prefName;
+    try {
+      var pbi = pref.QueryInterface(Components.interfaces.nsIPrefBranch2);
+      pbi.addObserver(this.domain, this, false);
+    } catch(ex) {
+      dump("Failed to observe prefs: " + ex + "\n");
+    }
+  },
+  shutdown: function()
+  {
+    try {
+      var pbi = pref.QueryInterface(Components.interfaces.nsIPrefBranch2);
+      pbi.removeObserver(this.domain, this);
+    } catch(ex) {
+      dump("Failed to remove pref observers: " + ex + "\n");
+    }
+  },
+  observe: function(subject, topic, prefName)
+  {
+    if (!IsHTMLEditor())
+      return;
+    // verify that we're changing a button pref
+    if (topic != "nsPref:changed") return;
+    
+    var editor = GetCurrentEditor();
+    if (prefName == kUseCssPref)
+    {
+      var cmd = document.getElementById("cmd_highlight");
+      if (cmd) {
+        var useCSS = gPrefs.getBoolPref(prefName);
+
+        if (useCSS && editor) {
+          var mixedObj = {};
+          var state = editor.getHighlightColorState(mixedObj);
+          cmd.setAttribute("state", state);
+          cmd.collapsed = false;
+        }      
+        else {
+          cmd.setAttribute("state", "transparent");
+          cmd.collapsed = true;
+        }
+
+        if (editor)
+          editor.isCSSEnabled = useCSS;
+      }
+    }
+    else if (prefName.substr(0, kEditorToolbarPrefs.length) == kEditorToolbarPrefs)
+    {
+      var id = prefName.substr(kEditorToolbarPrefs.length) + "Button";
+      var button = document.getElementById(id);
+      if (button) {
+        button.hidden = !gPrefs.getBoolPref(prefName);
+        ShowHideToolbarSeparators(button.parentNode);
+      }
+    }
+    else if (editor && (prefName == kCRInParagraphsPref))
+      editor.returnInParagraphCreatesNewParagraph = gPrefs.getBoolPref(prefName);
+  }
+}
+
+function AfterHighlightColorChange()
+{
+  if (!IsHTMLEditor())
+    return;
+
+  var button = document.getElementById("cmd_highlight");
+  if (button) {
+    var mixedObj = {};
+    try {
+      var state = GetCurrentEditor().getHighlightColorState(mixedObj);
+      button.setAttribute("state", state);
+      onHighlightColorChange();
+    } catch (e) {}
+  }      
+}
+
+function EditorOnLoad()
+{
+    // See if argument was passed.
+    if ( window.arguments && window.arguments[0] ) {
+        // Opened via window.openDialog with URL as argument.
+        // Put argument where EditorStartup expects it.
+        document.getElementById( "args" ).setAttribute( "value", window.arguments[0] );
+    }
+
+    // get default character set if provided
+    if ("arguments" in window && window.arguments.length > 1 && window.arguments[1]) {
+      if (window.arguments[1].indexOf("charset=") != -1) {
+        var arrayArgComponents = window.arguments[1].split("=");
+        if (arrayArgComponents) {
+          // Put argument where EditorStartup expects it.
+          document.getElementById( "args" ).setAttribute("charset", arrayArgComponents[1]);
+        }
+      }
+    }
+
+    window.tryToClose = EditorCanClose;
+
+    // Continue with normal startup.
+    EditorStartup();
+
+    // Initialize our source text <editor>
+    try {
+      gSourceContentWindow = document.getElementById("content-source");
+      gSourceContentWindow.makeEditable("text", false);
+      gSourceTextEditor = gSourceContentWindow.getEditor(gSourceContentWindow.contentWindow);
+      gSourceTextEditor.QueryInterface(Components.interfaces.nsIPlaintextEditor);
+      gSourceTextEditor.enableUndo(false);
+      gSourceTextEditor.rootElement.style.fontFamily = "-moz-fixed";
+      gSourceTextEditor.rootElement.style.whiteSpace = "pre";
+      gSourceTextEditor.rootElement.style.margin = 0;
+      var controller = Components.classes["@mozilla.org/embedcomp/base-command-controller;1"]
+                                 .createInstance(Components.interfaces.nsIControllerContext);
+      controller.init(null);
+      controller.setCommandContext(gSourceContentWindow);
+      gSourceContentWindow.contentWindow.controllers.insertControllerAt(0, controller);
+      var commandTable = controller.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                                   .getInterface(Components.interfaces.nsIControllerCommandTable);
+      commandTable.registerCommand("cmd_find",        nsFindCommand);
+      commandTable.registerCommand("cmd_findNext",    nsFindAgainCommand);
+      commandTable.registerCommand("cmd_findPrev",    nsFindAgainCommand);
+    } catch (e) { dump("makeEditable failed: "+e+"\n"); }
+}
+
+const gSourceTextListener =
+{
+  NotifyDocumentCreated: function NotifyDocumentCreated() {},
+  NotifyDocumentWillBeDestroyed: function NotifyDocumentWillBeDestroyed() {},
+  NotifyDocumentStateChanged: function NotifyDocumentStateChanged(isChanged)
+  {
+    window.updateCommands("save");
+  }
+};
+
+const gSourceTextObserver =
+{
+  observe: function observe(aSubject, aTopic, aData)
+  {
+    // we currently only use this to update undo
+    window.updateCommands("undo");
+  }
+};
+
+function TextEditorOnLoad()
+{
+    // See if argument was passed.
+    if ( window.arguments && window.arguments[0] ) {
+        // Opened via window.openDialog with URL as argument.
+        // Put argument where EditorStartup expects it.
+        document.getElementById( "args" ).setAttribute( "value", window.arguments[0] );
+    }
+    // Continue with normal startup.
+    EditorStartup();
+}
+
+// This should be called by all editor users when they close their window
+//  or other similar "done with editor" actions, like recycling a Mail Composer window.
+function EditorCleanup()
+{
+  SwitchInsertCharToAnotherEditorOrClose();
+}
+
+var DocumentReloadListener =
+{
+  NotifyDocumentCreated: function() {},
+  NotifyDocumentWillBeDestroyed: function() {},
+
+  NotifyDocumentStateChanged:function( isNowDirty )
+  {
+    var editor = GetCurrentEditor();
+    try {
+      // unregister the listener to prevent multiple callbacks
+      editor.removeDocumentStateListener( DocumentReloadListener );
+
+      var charset = editor.documentCharacterSet;
+
+      // update the META charset with the current presentation charset
+      editor.documentCharacterSet = charset;
+
+    } catch (e) {}
+  }
+};
+
+function addEditorClickEventListener()
+{
+  try {
+    var bodyelement = GetBodyElement();
+    if (bodyelement)
+      bodyelement.addEventListener("click", EditorClick, false);
+  } catch (e) {}
+}
+
+// implements nsIObserver
+var gEditorDocumentObserver =
+{ 
+  observe: function(aSubject, aTopic, aData)
+  {
+    // Should we allow this even if NOT the focused editor?
+    var commandManager = GetCurrentCommandManager();
+    if (commandManager != aSubject)
+      return;
+
+    var editor = GetCurrentEditor();
+    switch(aTopic)
+    {
+      case "obs_documentCreated":
+        // Just for convenience
+        gContentWindow = window.content;
+
+        // Get state to see if document creation succeeded
+        var params = newCommandParams();
+        if (!params)
+          return;
+
+        try {
+          commandManager.getCommandState(aTopic, gContentWindow, params);
+          var errorStringId = 0;
+          var editorStatus = params.getLongValue("state_data");
+          if (!editor && editorStatus == nsIEditingSession.eEditorOK)
+          {
+            dump("\n ****** NO EDITOR BUT NO EDITOR ERROR REPORTED ******* \n\n");
+            editorStatus = nsIEditingSession.eEditorErrorUnknown;
+          }
+
+          switch (editorStatus)
+          {
+            case nsIEditingSession.eEditorErrorCantEditFramesets:
+              errorStringId = "CantEditFramesetMsg";
+              break;
+            case nsIEditingSession.eEditorErrorCantEditMimeType:
+              errorStringId = "CantEditMimeTypeMsg";
+              break;
+            case nsIEditingSession.eEditorErrorUnknown:
+              errorStringId = "CantEditDocumentMsg";
+              break;
+            // Note that for "eEditorErrorFileNotFound, 
+            // network code popped up an alert dialog, so we don't need to
+          }
+          if (errorStringId)
+            AlertWithTitle("", GetString(errorStringId));
+        } catch(e) { dump("EXCEPTION GETTING obs_documentCreated state "+e+"\n"); }
+
+        // We have a bad editor -- nsIEditingSession will rebuild an editor
+        //   with a blank page, so simply abort here
+        if (editorStatus)
+          return; 
+
+        if (!("InsertCharWindow" in window))
+          window.InsertCharWindow = null;
+
+        try {
+          editor.QueryInterface(nsIEditorStyleSheets);
+
+          //  and extra styles for showing anchors, table borders, smileys, etc
+          editor.addOverrideStyleSheet(kNormalStyleSheet);
+
+          // remove contenteditable stylesheets if they were applied by the
+          // editingSession
+          editor.removeOverrideStyleSheet(kContentEditableStyleSheet);
+        } catch (e) {}
+
+        // Things for just the Web Composer application
+        if (IsWebComposer())
+        {
+          InlineSpellCheckerUI.init(editor);
+          document.getElementById('menu_inlinespellcheck').setAttribute('disabled', !InlineSpellCheckerUI.canSpellCheck);
+
+          editor.returnInParagraphCreatesNewParagraph = gPrefs.getBoolPref(kCRInParagraphsPref);
+
+          // Set focus to content window if not a mail composer
+          // Race conditions prevent us from setting focus here
+          //   when loading a url into blank window
+          setTimeout(SetFocusOnStartup, 0);
+
+          // Call EditorSetDefaultPrefsAndDoctype first so it gets the default author before initing toolbars
+          EditorSetDefaultPrefsAndDoctype();
+
+          // We may load a text document into an html editor,
+          //   so be sure editortype is set correctly
+          // XXX We really should use the "real" plaintext editor for this!
+          if (editor.contentsMIMEType == "text/plain")
+          {
+            try {
+              GetCurrentEditorElement().editortype = "text";
+            } catch (e) { dump (e)+"\n"; }
+
+            // Hide or disable UI not used for plaintext editing
+            HideItem("FormatToolbar");
+            HideItem("EditModeToolbar");
+            HideItem("formatMenu");
+            HideItem("tableMenu");
+            HideItem("menu_validate");
+            HideItem("sep_validate");
+            HideItem("previewButton");
+            HideItem("imageButton");
+            HideItem("linkButton");
+            HideItem("namedAnchorButton");
+            HideItem("hlineButton");
+            HideItem("tableButton");
+
+            HideItem("fileExportToText");
+            HideItem("previewInBrowser");
+
+/* XXX When paste actually converts formatted rich text to pretty formatted plain text
+       and pasteNoFormatting is fixed to paste the text without formatting (what paste
+       currently does), then this item shouldn't be hidden: */
+            HideItem("menu_pasteNoFormatting"); 
+
+            HideItem("cmd_viewFormatToolbar");
+            HideItem("cmd_viewEditModeToolbar");
+
+            HideItem("viewSep1");
+            HideItem("viewNormalMode");
+            HideItem("viewAllTagsMode");
+            HideItem("viewSourceMode");
+            HideItem("viewPreviewMode");
+
+            HideItem("structSpacer");
+
+            // Hide everything in "Insert" except for "Symbols"
+            var menuPopup = document.getElementById("insertMenuPopup");
+            if (menuPopup)
+            {
+              var children = menuPopup.childNodes;
+              for (var i=0; i < children.length; i++) 
+              {
+                var item = children.item(i);
+                if (item.id != "insertChars")
+                  item.hidden = true;
+              }
+            }
+          }
+    
+          // Set window title
+          UpdateWindowTitle();
+
+          // We must wait until document is created to get proper Url
+          // (Windows may load with local file paths)
+          SetSaveAndPublishUI(GetDocumentUrl());
+
+          // Start in "Normal" edit mode
+          SetDisplayMode(kDisplayModeNormal);
+        }
+
+        // Add mouse click watcher if right type of editor
+        if (IsHTMLEditor())
+        {
+          addEditorClickEventListener();
+
+          // Force color widgets to update
+          onFontColorChange();
+          onBackgroundColorChange();
+        }
+        break;
+
+      case "cmd_setDocumentModified":
+        window.updateCommands("save");
+        break;
+
+      case "obs_documentWillBeDestroyed":
+        dump("obs_documentWillBeDestroyed notification\n");
+        break;
+
+      case "obs_documentLocationChanged":
+        // Ignore this when editor doesn't exist,
+        //   which happens once when page load starts
+        if (editor)
+          try {
+            editor.updateBaseURL();
+          } catch(e) { dump (e); }
+        break;
+
+      case "cmd_bold":
+        // Update all style items
+        // cmd_bold is a proxy; see EditorSharedStartup (above) for details
+        window.updateCommands("style");
+        window.updateCommands("undo");
+        break;
+    }
+  }
+}
+
+function SetFocusOnStartup()
+{
+  gContentWindow.focus();
+}
+
+function EditorStartup()
+{
+  var ds = GetCurrentEditorElement().docShell;
+  ds.useErrorPages = false;
+  var root = ds.QueryInterface(Components.interfaces.nsIDocShellTreeItem).
+    rootTreeItem.QueryInterface(Components.interfaces.nsIDocShell);
+
+  root.QueryInterface(Components.interfaces.nsIDocShell).appType =
+    Components.interfaces.nsIDocShell.APP_TYPE_EDITOR;
+
+  var is_HTMLEditor = IsHTMLEditor();
+  if (is_HTMLEditor)
+  {
+    // XUL elements we use when switching from normal editor to edit source
+    gContentWindowDeck = document.getElementById("ContentWindowDeck");
+    gFormatToolbar = document.getElementById("FormatToolbar");
+    gViewFormatToolbar = document.getElementById("viewFormatToolbar");
+  }
+
+  // set up our global prefs object
+  GetPrefsService();
+
+  // Startup also used by other editor users, such as Message Composer
+  EditorSharedStartup();
+
+  // Commands specific to the Composer Application window,
+  //  (i.e., not embedded editors)
+  //  such as file-related commands, HTML Source editing, Edit Modes...
+  SetupComposerWindowCommands();
+
+  ShowHideToolbarButtons();
+  gEditorToolbarPrefListener = new nsPrefListener(kEditorToolbarPrefs);
+
+  gCSSPrefListener = new nsPrefListener(kUseCssPref);
+  gReturnInParagraphPrefListener = new nsPrefListener(kCRInParagraphsPref);
+
+  // hide Highlight button if we are in an HTML editor with CSS mode off
+  // and tell the editor if a CR in a paragraph creates a new paragraph
+  var cmd = document.getElementById("cmd_highlight");
+  if (cmd) {
+    var useCSS = gPrefs.getBoolPref(kUseCssPref);
+    if (!useCSS && is_HTMLEditor) {
+      cmd.collapsed = true;
+    }
+  }
+
+  // Get url for editor content and load it.
+  // the editor gets instantiated by the edittingSession when the URL has finished loading.
+  var url = document.getElementById("args").getAttribute("value");
+  try {
+    var charset = document.getElementById("args").getAttribute("charset");
+    var contentViewer = GetCurrentEditorElement().docShell.contentViewer;
+    contentViewer.QueryInterface(Components.interfaces.nsIMarkupDocumentViewer);
+    contentViewer.defaultCharacterSet = charset;
+    contentViewer.forceCharacterSet = charset;
+  } catch (e) {}
+  EditorLoadUrl(url);
+}
+
+function EditorLoadUrl(url)
+{
+  try {
+    if (url)
+      GetCurrentEditorElement().webNavigation.loadURI(url, // uri string
+             nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE,     // load flags
+             null,                                         // referrer
+             null,                                         // post-data stream
+             null);
+  } catch (e) { dump(" EditorLoadUrl failed: "+e+"\n"); }
+}
+
+// This should be called by all Composer types
+function EditorSharedStartup()
+{
+  // Just for convenience
+  gContentWindow = window.content;
+
+  // Set up the mime type and register the commands.
+  if (IsHTMLEditor())
+    SetupHTMLEditorCommands();
+  else
+    SetupTextEditorCommands();
+
+  // add observer to be called when document is really done loading 
+  // and is modified
+  // Note: We're really screwed if we fail to install this observer!
+  try {
+    var commandManager = GetCurrentCommandManager();
+    commandManager.addCommandObserver(gEditorDocumentObserver, "obs_documentCreated");
+    commandManager.addCommandObserver(gEditorDocumentObserver, "cmd_setDocumentModified");
+    commandManager.addCommandObserver(gEditorDocumentObserver, "obs_documentWillBeDestroyed");
+    commandManager.addCommandObserver(gEditorDocumentObserver, "obs_documentLocationChanged");
+
+    // Until nsIControllerCommandGroup-based code is implemented,
+    //  we will observe just the bold command to trigger update of
+    //  all toolbar style items
+    commandManager.addCommandObserver(gEditorDocumentObserver, "cmd_bold");
+  } catch (e) { dump(e); }
+
+  var isMac = (GetOS() == gMac);
+
+  // Set platform-specific hints for how to select cells
+  // Mac uses "Cmd", all others use "Ctrl"
+  var tableKey = GetString(isMac ? "XulKeyMac" : "TableSelectKey");
+  var dragStr = tableKey+GetString("Drag");
+  var clickStr = tableKey+GetString("Click");
+
+  var delStr = GetString(isMac ? "Clear" : "Del");
+
+  SafeSetAttribute("menu_SelectCell", "acceltext", clickStr);
+  SafeSetAttribute("menu_SelectRow", "acceltext", dragStr);
+  SafeSetAttribute("menu_SelectColumn", "acceltext", dragStr);
+  SafeSetAttribute("menu_SelectAllCells", "acceltext", dragStr);
+  // And add "Del" or "Clear"
+  SafeSetAttribute("menu_DeleteCellContents", "acceltext", delStr);
+
+  // Set text for indent, outdent keybinding
+
+  // hide UI that we don't have components for
+  RemoveInapplicableUIElements();
+
+  gPrefs = GetPrefs();
+
+  // Use browser colors as initial values for editor's default colors
+  var BrowserColors = GetDefaultBrowserColors();
+  if (BrowserColors)
+  {
+    gDefaultTextColor = BrowserColors.TextColor;
+    gDefaultBackgroundColor = BrowserColors.BackgroundColor;
+  }
+
+  // For new window, no default last-picked colors
+  gColorObj.LastTextColor = "";
+  gColorObj.LastBackgroundColor = "";
+  gColorObj.LastHighlightColor = "";
+}
+
+// This method is only called by Message composer when recycling a compose window
+function EditorResetFontAndColorAttributes()
+{
+  try {  
+    var editor = GetCurrentEditor();
+    editor.rebuildDocumentFromSource("");
+    // Because the selection is now collapsed, the following line
+    // clears the typing state to discontinue all inline styles
+    editor.removeAllInlineProperties();
+    document.getElementById("cmd_fontFace").setAttribute("state", "");
+    gColorObj.LastTextColor = "";
+    gColorObj.LastBackgroundColor = "";
+    gColorObj.LastHighlightColor = "";
+    document.getElementById("cmd_fontColor").setAttribute("state", "");
+    document.getElementById("cmd_backgroundColor").setAttribute("state", "");
+    UpdateDefaultColors();
+  } catch (e) {}
+}
+
+function EditorShutdown()
+{
+  gEditorToolbarPrefListener.shutdown();
+  gCSSPrefListener.shutdown();
+  gReturnInParagraphPrefListener.shutdown();
+
+  try {
+    var commandManager = GetCurrentCommandManager();
+    commandManager.removeCommandObserver(gEditorDocumentObserver, "obs_documentCreated");
+    commandManager.removeCommandObserver(gEditorDocumentObserver, "obs_documentWillBeDestroyed");
+    commandManager.removeCommandObserver(gEditorDocumentObserver, "obs_documentLocationChanged");
+  } catch (e) { dump (e); }   
+}
+
+function SafeSetAttribute(nodeID, attributeName, attributeValue)
+{
+    var theNode = document.getElementById(nodeID);
+    if (theNode)
+        theNode.setAttribute(attributeName, attributeValue);
+}
+
+function DocumentHasBeenSaved()
+{
+  var fileurl = "";
+  try {
+    fileurl = GetDocumentUrl();
+  } catch (e) {
+    return false;
+  }
+
+  if (!fileurl || IsUrlAboutBlank(fileurl))
+    return false;
+
+  // We have a file URL already
+  return true;
+}
+
+function CheckAndSaveDocument(command, allowDontSave)
+{
+  var document;
+  try {
+    // if we don't have an editor or an document, bail
+    var editor = GetCurrentEditor();
+    document = editor.document;
+    if (!document)
+      return true;
+  } catch (e) { return true; }
+
+  if (!IsDocumentModified() && !IsHTMLSourceChanged())
+    return true;
+
+  // call window.focus, since we need to pop up a dialog
+  // and therefore need to be visible (to prevent user confusion)
+  top.document.commandDispatcher.focusedWindow.focus();  
+
+  var scheme = GetScheme(GetDocumentUrl());
+  var doPublish = (scheme && scheme != "file");
+
+  var strID;
+  switch (command)
+  {
+    case "cmd_close":
+      strID = "BeforeClosing";
+      break;
+    case "cmd_preview":
+      strID = "BeforePreview";
+      break;
+    case "cmd_editSendPage":
+      strID = "SendPageReason";
+      break;
+    case "cmd_validate":
+      strID = "BeforeValidate";
+      break;
+  }
+    
+  var reasonToSave = strID ? GetString(strID) : "";
+
+  var title = document.title;
+  if (!title)
+    title = GetString("untitled");
+
+  var dialogTitle = GetString(doPublish ? "PublishPage" : "SaveDocument");
+  var dialogMsg = GetString(doPublish ? "PublishPrompt" : "SaveFilePrompt");
+  dialogMsg = (dialogMsg.replace(/%title%/,title)).replace(/%reason%/,reasonToSave);
+
+  var promptService = GetPromptService();
+  if (!promptService)
+    return false;
+
+  var result = {value:0};
+  var promptFlags = promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_1;
+  var button1Title = null;
+  var button3Title = null;
+
+  if (doPublish)
+  {
+    promptFlags += promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0;
+    button1Title = GetString("Publish");
+    button3Title = GetString("DontPublish");    
+  }
+  else
+  {
+    promptFlags += promptService.BUTTON_TITLE_SAVE * promptService.BUTTON_POS_0;
+  }
+
+  // If allowing "Don't..." button, add that
+  if (allowDontSave)
+    promptFlags += doPublish ?
+        (promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_2)
+        : (promptService.BUTTON_TITLE_DONT_SAVE * promptService.BUTTON_POS_2);
+  
+  result = promptService.confirmEx(window, dialogTitle, dialogMsg, promptFlags,
+                          button1Title, null, button3Title, null, {value:0});
+
+  if (result == 0)
+  {
+    // Save, but first finish HTML source mode
+    if (IsHTMLSourceChanged()) {
+      try {
+        FinishHTMLSource();
+      } catch (e) { return false;}
+    }
+
+    if (doPublish)
+    {
+      // We save the command the user wanted to do in a global
+      // and return as if user canceled because publishing is asynchronous
+      // This command will be fired when publishing finishes
+      gCommandAfterPublishing = command;
+      goDoCommand("cmd_publish");
+      return false;
+    }
+
+    // Save to local disk
+    var contentsMIMEType;
+    if (IsHTMLEditor())
+      contentsMIMEType = kHTMLMimeType;
+    else
+      contentsMIMEType = kTextMimeType;
+    var success = SaveDocument(false, false, contentsMIMEType);
+    return success;
+  }
+
+  if (result == 2) // "Don't Save"
+    return true;
+
+  // Default or result == 1 (Cancel)
+  return false;
+}
+
+// --------------------------- File menu ---------------------------
+
+// Check for changes to document and allow saving before closing
+// This is hooked up to the OS's window close widget (e.g., "X" for Windows)
+function EditorCanClose()
+{
+  // Returns FALSE only if user cancels save action
+
+  // "true" means allow "Don't Save" button
+  var canClose = CheckAndSaveDocument("cmd_close", true);
+
+  // This is our only hook into closing via the "X" in the caption
+  //   or "Quit" (or other paths?)
+  //   so we must shift association to another
+  //   editor or close any non-modal windows now
+  if (canClose && "InsertCharWindow" in window && window.InsertCharWindow)
+    SwitchInsertCharToAnotherEditorOrClose();
+
+  return canClose;
+}
+
+// --------------------------- View menu ---------------------------
+
+function EditorSetDocumentCharacterSet(aCharset)
+{
+  try {
+    var editor = GetCurrentEditor();
+    editor.documentCharacterSet = aCharset;
+    var docUrl = GetDocumentUrl();
+    if( !IsUrlAboutBlank(docUrl))
+    {
+      // reloading the document will reverse any changes to the META charset, 
+      // we need to put them back in, which is achieved by a dedicated listener
+      editor.addDocumentStateListener( DocumentReloadListener );
+      EditorLoadUrl(docUrl);
+    }
+  } catch (e) {}
+}
+
+// ------------------------------------------------------------------
+function updateCharsetPopupMenu(menuPopup)
+{
+  if (IsDocumentModified() && !IsDocumentEmpty())
+  {
+    for (var i = 0; i < menuPopup.childNodes.length; i++)
+    {
+      var menuItem = menuPopup.childNodes[i];
+      menuItem.setAttribute('disabled', 'true');
+    }
+  }
+}
+
+// --------------------------- Text style ---------------------------
+
+function onParagraphFormatChange(paraMenuList, commandID)
+{
+  if (!paraMenuList)
+    return;
+
+  var commandNode = document.getElementById(commandID);
+  var state = commandNode.getAttribute("state");
+
+  // force match with "normal"
+  if (state == "body")
+    state = "";
+
+  if (state == "mixed")
+  {
+    //Selection is the "mixed" ( > 1 style) state
+    paraMenuList.selectedItem = null;
+    paraMenuList.setAttribute("label",GetString('Mixed'));
+  }
+  else
+  {
+    var menuPopup = document.getElementById("ParagraphPopup");
+    var menuItems = menuPopup.childNodes;
+    for (var i=0; i < menuItems.length; i++)
+    {
+      var menuItem = menuItems.item(i);
+      if ("value" in menuItem && menuItem.value == state)
+      {
+        paraMenuList.selectedItem = menuItem;
+        break;
+      }
+    }
+  }
+}
+
+function onFontFaceChange(fontFaceMenuList, commandID)
+{
+  var commandNode = document.getElementById(commandID);
+  var state = commandNode.getAttribute("state");
+
+  if (state == "mixed")
+  {
+    //Selection is the "mixed" ( > 1 style) state
+    fontFaceMenuList.selectedItem = null;
+    fontFaceMenuList.setAttribute("label",GetString('Mixed'));
+  }
+  else
+  {
+    var menuPopup = document.getElementById("FontFacePopup");
+    var menuItems = menuPopup.childNodes;
+    for (var i=0; i < menuItems.length; i++)
+    {
+      var menuItem = menuItems.item(i);
+      if (menuItem.getAttribute("label") && ("value" in menuItem && menuItem.value.toLowerCase() == state.toLowerCase()))
+      {
+        fontFaceMenuList.selectedItem = menuItem;
+        break;
+      }
+    }
+  }
+}
+
+function EditorSelectFontSize()
+{
+  var select = document.getElementById("FontSizeSelect");
+  if (select)
+  {
+    if (select.selectedIndex == -1)
+      return;
+
+    EditorSetFontSize(gFontSizeNames[select.selectedIndex]);
+  }
+}
+
+function onFontSizeChange(fontSizeMenulist, commandID)
+{
+  // If we don't match anything, set to "0 (normal)"
+  var newIndex = 2;
+  var size = fontSizeMenulist.getAttribute("size");
+  if ( size == "mixed")
+  {
+    // No single type selected
+    newIndex = -1;
+  }
+  else
+  {
+    for (var i = 0; i < gFontSizeNames.length; i++)
+    {
+      if( gFontSizeNames[i] == size )
+      {
+        newIndex = i;
+        break;
+      }
+    }
+  }
+  if (fontSizeMenulist.selectedIndex != newIndex)
+    fontSizeMenulist.selectedIndex = newIndex;
+}
+
+function EditorSetFontSize(size)
+{
+  if( size == "0" || size == "normal" ||
+      size == "medium" )
+  {
+    EditorRemoveTextProperty("font", "size");
+    // Also remove big and small,
+    //  else it will seem like size isn't changing correctly
+    EditorRemoveTextProperty("small", "");
+    EditorRemoveTextProperty("big", "");
+  } else {
+    // Temp: convert from new CSS size strings to old HTML size strings
+    switch (size)
+    {
+      case "xx-small":
+      case "x-small":
+        size = "-2";
+        break;
+      case "small":
+        size = "-1";
+        break;
+      case "large":
+        size = "+1";
+        break;
+      case "x-large":
+        size = "+2";
+        break;
+      case "xx-large":
+        size = "+3";
+        break;
+    }
+    EditorSetTextProperty("font", "size", size);
+  }
+  gContentWindow.focus();
+}
+
+function initFontFaceMenu(menuPopup)
+{
+  initLocalFontFaceMenu(menuPopup);
+
+  if (menuPopup)
+  {
+    var children = menuPopup.childNodes;
+    if (!children) return;
+
+    var firstHas = { value: false };
+    var anyHas = { value: false };
+    var allHas = { value: false };
+
+    // we need to set or clear the checkmark for each menu item since the selection
+    // may be in a new location from where it was when the menu was previously opened
+
+    // Fixed width (second menu item) is special case: old TT ("teletype") attribute
+    EditorGetTextProperty("tt", "", "", firstHas, anyHas, allHas);
+    children[1].setAttribute("checked", allHas.value);
+
+    if (!anyHas.value)
+      EditorGetTextProperty("font", "face", "", firstHas, anyHas, allHas);
+
+    children[0].setAttribute("checked", !anyHas.value);
+
+    // Skip over default, TT, and separator
+    for (var i = 3; i < children.length; i++)
+    {
+      var menuItem = children[i];
+      var faceType = menuItem.getAttribute("value");
+
+      if (faceType)
+      {
+        EditorGetTextProperty("font", "face", faceType, firstHas, anyHas, allHas);
+
+        // Check the menuitem only if all of selection has the face
+        if (allHas.value)
+        {
+          menuItem.setAttribute("checked", "true");
+          break;
+        }
+
+        // in case none match, make sure we've cleared the checkmark
+        menuItem.removeAttribute("checked");
+      }
+    }
+  }
+}
+
+const kFixedFontFaceMenuItems = 7; // number of fixed font face menuitems
+
+function initLocalFontFaceMenu(menuPopup)
+{
+  if (!gLocalFonts)
+  {
+    // Build list of all local fonts once per editor
+    try 
+    {
+      var enumerator = Components.classes["@mozilla.org/gfx/fontenumerator;1"]
+                                 .getService(Components.interfaces.nsIFontEnumerator);
+      var localFontCount = { value: 0 }
+      gLocalFonts = enumerator.EnumerateAllFonts(localFontCount);
+    }
+    catch(e) { }
+  }
+  
+  var useRadioMenuitems = (menuPopup.parentNode.localName == "menu"); // don't do this for menulists  
+  if (menuPopup.childNodes.length == kFixedFontFaceMenuItems) 
+  {
+    if (gLocalFonts.length == 0) {
+      menuPopup.childNodes[kFixedFontFaceMenuItems - 1].hidden = true;
+    }
+    for (var i = 0; i < gLocalFonts.length; ++i)
+    {
+      if (gLocalFonts[i] != "")
+      {
+        var itemNode = document.createElementNS(XUL_NS, "menuitem");
+        itemNode.setAttribute("label", gLocalFonts[i]);
+        itemNode.setAttribute("value", gLocalFonts[i]);
+        if (useRadioMenuitems) {
+          itemNode.setAttribute("type", "radio");
+          itemNode.setAttribute("name", "2");
+          itemNode.setAttribute("observes", "cmd_renderedHTMLEnabler");
+        }
+        menuPopup.appendChild(itemNode);
+      }
+    }
+  }
+}
+ 
+
+function initFontSizeMenu(menuPopup)
+{
+  if (menuPopup)
+  {
+    var children = menuPopup.childNodes;
+    if (!children) return;
+
+    var firstHas = { value: false };
+    var anyHas = { value: false };
+    var allHas = { value: false };
+
+    var sizeWasFound = false;
+
+    // we need to set or clear the checkmark for each menu item since the selection
+    // may be in a new location from where it was when the menu was previously opened
+
+    // First 2 items add <small> and <big> tags
+    // While it would be better to show the number of levels,
+    //  at least this tells user if either of them are set
+    var menuItem = children[0];
+    if (menuItem)
+    {
+      EditorGetTextProperty("small", "", "", firstHas, anyHas, allHas);
+      menuItem.setAttribute("checked", allHas.value);
+      sizeWasFound = anyHas.value;
+    }
+
+    menuItem = children[1];
+    if (menuItem)
+    {
+      EditorGetTextProperty("big", "", "", firstHas, anyHas, allHas);
+      menuItem.setAttribute("checked", allHas.value);
+      sizeWasFound |= anyHas.value;
+    }
+
+    // Fixed size items start after menu separator
+    var menuIndex = 3;
+    // Index of the medium (default) item
+    var mediumIndex = 5;
+
+    // Scan through all supported "font size" attribute values
+    for (var i = -2; i <= 3; i++)
+    {
+      menuItem = children[menuIndex];
+
+      // Skip over medium since it'll be set below.
+      // If font size=0 is actually set, we'll toggle it off below if
+      // we enter this loop in this case.
+      if (menuItem && (i != 0))
+      {
+        var sizeString = (i <= 0) ? String(i) : ("+" + String(i));
+        EditorGetTextProperty("font", "size", sizeString, firstHas, anyHas, allHas);
+        // Check the item only if all of selection has the size...
+        menuItem.setAttribute("checked", allHas.value);
+        // ...but remember if ANY of of selection had size set
+        sizeWasFound |= anyHas.value;
+      }
+      menuIndex++;
+    }
+
+    // if no size was found, then check default (medium)
+    // note that no item is checked in the case of "mixed" selection
+    children[mediumIndex].setAttribute("checked", !sizeWasFound);
+  }
+}
+
+function onHighlightColorChange()
+{
+  var commandNode = document.getElementById("cmd_highlight");
+  if (commandNode)
+  {
+    var color = commandNode.getAttribute("state");
+    var button = document.getElementById("HighlightColorButton");
+    if (button)
+    {
+      // No color set - get color set on page or other defaults
+      if (!color)
+        color = "transparent" ;
+
+      button.setAttribute("style", "background-color:"+color+" !important");
+    }
+  }
+}
+
+function onFontColorChange()
+{
+  var commandNode = document.getElementById("cmd_fontColor");
+  if (commandNode)
+  {
+    var color = commandNode.getAttribute("state");
+    var button = document.getElementById("TextColorButton");
+    if (button)
+    {
+      // No color set - get color set on page or other defaults
+      if (!color)
+        color = gDefaultTextColor;
+      button.setAttribute("style", "background-color:"+color);
+    }
+  }
+}
+
+function onBackgroundColorChange()
+{
+  var commandNode = document.getElementById("cmd_backgroundColor");
+  if (commandNode)
+  {
+    var color = commandNode.getAttribute("state");
+    var button = document.getElementById("BackgroundColorButton");
+    if (button)
+    {
+      if (!color)
+        color = gDefaultBackgroundColor;
+
+      button.setAttribute("style", "background-color:"+color);
+    }
+  }
+}
+
+// Call this when user changes text and/or background colors of the page
+function UpdateDefaultColors()
+{
+  var BrowserColors = GetDefaultBrowserColors();
+  var bodyelement = GetBodyElement();
+  var defTextColor = gDefaultTextColor;
+  var defBackColor = gDefaultBackgroundColor;
+
+  if (bodyelement)
+  {
+    var color = bodyelement.getAttribute("text");
+    if (color)
+      gDefaultTextColor = color;
+    else if (BrowserColors)
+      gDefaultTextColor = BrowserColors.TextColor;
+
+    color = bodyelement.getAttribute("bgcolor");
+    if (color)
+      gDefaultBackgroundColor = color;
+    else if (BrowserColors)
+      gDefaultBackgroundColor = BrowserColors.BackgroundColor;
+  }
+
+  // Trigger update on toolbar
+  if (defTextColor != gDefaultTextColor)
+  {
+    goUpdateCommandState("cmd_fontColor");
+    onFontColorChange();
+  }
+  if (defBackColor != gDefaultBackgroundColor)
+  {
+    goUpdateCommandState("cmd_backgroundColor");
+    onBackgroundColorChange();
+  }
+}
+
+function GetBackgroundElementWithColor()
+{
+  var editor = GetCurrentTableEditor();
+  if (!editor)
+    return null;
+
+  gColorObj.Type = "";
+  gColorObj.PageColor = "";
+  gColorObj.TableColor = "";
+  gColorObj.CellColor = "";
+  gColorObj.BackgroundColor = "";
+  gColorObj.SelectedType = "";
+
+  var tagNameObj = { value: "" };
+  var element;
+  try {
+    element = editor.getSelectedOrParentTableElement(tagNameObj, {value:0});
+  }
+  catch(e) {}
+
+  if (element && tagNameObj && tagNameObj.value)
+  {
+    gColorObj.BackgroundColor = GetHTMLOrCSSStyleValue(element, "bgcolor", "background-color");
+    gColorObj.BackgroundColor = ConvertRGBColorIntoHEXColor(gColorObj.BackgroundColor);
+    if (tagNameObj.value.toLowerCase() == "td")
+    {
+      gColorObj.Type = "Cell";
+      gColorObj.CellColor = gColorObj.BackgroundColor;
+
+      // Get any color that might be on parent table
+      var table = GetParentTable(element);
+      gColorObj.TableColor = GetHTMLOrCSSStyleValue(table, "bgcolor", "background-color");
+      gColorObj.TableColor = ConvertRGBColorIntoHEXColor(gColorObj.TableColor);
+    }
+    else
+    {
+      gColorObj.Type = "Table";
+      gColorObj.TableColor = gColorObj.BackgroundColor;
+    }
+    gColorObj.SelectedType = gColorObj.Type;
+  }
+  else
+  {
+    var IsCSSPrefChecked = gPrefs.getBoolPref(kUseCssPref);
+    if (IsCSSPrefChecked && IsHTMLEditor())
+    {
+      var selection = editor.selection;
+      if (selection)
+      {
+        element = selection.focusNode;
+        while (!editor.nodeIsBlock(element))
+          element = element.parentNode;
+      }
+      else
+      {
+        element = GetBodyElement();
+      }
+    }
+    else
+    {
+      element = GetBodyElement();
+    }
+    if (element)
+    {
+      gColorObj.Type = "Page";
+      gColorObj.BackgroundColor = GetHTMLOrCSSStyleValue(element, "bgcolor", "background-color");
+      if (gColorObj.BackgroundColor == "")
+      {
+        gColorObj.BackgroundColor = "transparent";
+      }
+      else
+      {
+        gColorObj.BackgroundColor = ConvertRGBColorIntoHEXColor(gColorObj.BackgroundColor);
+      }
+      gColorObj.PageColor = gColorObj.BackgroundColor;
+    }
+  }
+  return element;
+}
+
+function SetSmiley(smileyText)
+{
+  try {
+    GetCurrentEditor().insertText(smileyText);
+    gContentWindow.focus();
+  }
+  catch(e) {}
+}
+
+function EditorSelectColor(colorType, mouseEvent)
+{
+  var editor = GetCurrentEditor();
+  if (!editor || !gColorObj)
+    return;
+
+  // Shift + mouse click automatically applies last color, if available
+  var useLastColor = mouseEvent ? ( mouseEvent.button == 0 && mouseEvent.shiftKey ) : false;
+  var element;
+  var table;
+  var currentColor = "";
+  var commandNode;
+
+  if (!colorType)
+    colorType = "";
+
+  if (colorType == "Text")
+  {
+    gColorObj.Type = colorType;
+
+    // Get color from command node state
+    commandNode = document.getElementById("cmd_fontColor");
+    currentColor = commandNode.getAttribute("state");
+    currentColor = ConvertRGBColorIntoHEXColor(currentColor);
+    gColorObj.TextColor = currentColor;
+
+    if (useLastColor && gColorObj.LastTextColor )
+      gColorObj.TextColor = gColorObj.LastTextColor;
+    else
+      useLastColor = false;
+  }
+  else if (colorType == "Highlight")
+  {
+    gColorObj.Type = colorType;
+
+    // Get color from command node state
+    commandNode = document.getElementById("cmd_highlight");
+    currentColor = commandNode.getAttribute("state");
+    currentColor = ConvertRGBColorIntoHEXColor(currentColor);
+    gColorObj.HighlightColor = currentColor;
+
+    if (useLastColor && gColorObj.LastHighlightColor )
+      gColorObj.HighlightColor = gColorObj.LastHighlightColor;
+    else
+      useLastColor = false;
+  }
+  else
+  {
+    element = GetBackgroundElementWithColor();
+    if (!element)
+      return;
+
+    // Get the table if we found a cell
+    if (gColorObj.Type == "Table")
+      table = element;
+    else if (gColorObj.Type == "Cell")
+      table = GetParentTable(element);
+
+    // Save to avoid resetting if not necessary
+    currentColor = gColorObj.BackgroundColor;
+
+    if (colorType == "TableOrCell" || colorType == "Cell")
+    {
+      if (gColorObj.Type == "Cell")
+        gColorObj.Type = colorType;
+      else if (gColorObj.Type != "Table")
+        return;
+    }
+    else if (colorType == "Table" && gColorObj.Type == "Page")
+      return;
+
+    if (colorType == "" && gColorObj.Type == "Cell")
+    {
+      // Using empty string for requested type means
+      //  we can let user select cell or table
+      gColorObj.Type = "TableOrCell";
+    }
+
+    if (useLastColor && gColorObj.LastBackgroundColor )
+      gColorObj.BackgroundColor = gColorObj.LastBackgroundColor;
+    else
+      useLastColor = false;
+  }
+  // Save the type we are really requesting
+  colorType = gColorObj.Type;
+
+  if (!useLastColor)
+  {
+    // Avoid the JS warning
+    gColorObj.NoDefault = false;
+
+    // Launch the ColorPicker dialog
+    // TODO: Figure out how to position this under the color buttons on the toolbar
+    window.openDialog("chrome://editor/content/EdColorPicker.xul", "_blank", "chrome,close,titlebar,modal", "", gColorObj);
+
+    // User canceled the dialog
+    if (gColorObj.Cancel)
+      return;
+  }
+
+  if (gColorObj.Type == "Text")
+  {
+    if (currentColor != gColorObj.TextColor)
+    {
+      if (gColorObj.TextColor)
+        EditorSetTextProperty("font", "color", gColorObj.TextColor);
+      else
+        EditorRemoveTextProperty("font", "color");
+    }
+    // Update the command state (this will trigger color button update)
+    goUpdateCommandState("cmd_fontColor");
+  }
+  else if (gColorObj.Type == "Highlight")
+  {
+    if (currentColor != gColorObj.HighlightColor)
+    {
+      if (gColorObj.HighlightColor)
+        EditorSetTextProperty("font", "bgcolor", gColorObj.HighlightColor);
+      else
+        EditorRemoveTextProperty("font", "bgcolor");
+    }
+    // Update the command state (this will trigger color button update)
+    goUpdateCommandState("cmd_highlight");
+  }
+  else if (element)
+  {
+    if (gColorObj.Type == "Table")
+    {
+      // Set background on a table
+      // Note that we shouldn't trust "currentColor" because of "TableOrCell" behavior
+      if (table)
+      {
+        var bgcolor = table.getAttribute("bgcolor");
+        if (bgcolor != gColorObj.BackgroundColor)
+        try {
+          if (gColorObj.BackgroundColor)
+            editor.setAttributeOrEquivalent(table, "bgcolor", gColorObj.BackgroundColor, false);
+          else
+            editor.removeAttributeOrEquivalent(table, "bgcolor", false);
+        } catch (e) {}
+      }
+    }
+    else if (currentColor != gColorObj.BackgroundColor && IsHTMLEditor())
+    {
+      editor.beginTransaction();
+      try
+      {
+        editor.setBackgroundColor(gColorObj.BackgroundColor);
+
+        if (gColorObj.Type == "Page" && gColorObj.BackgroundColor)
+        {
+          // Set all page colors not explicitly set,
+          //  else you can end up with unreadable pages
+          //  because viewer's default colors may not be same as page author's
+          var bodyelement = GetBodyElement();
+          if (bodyelement)
+          {
+            var defColors = GetDefaultBrowserColors();
+            if (defColors)
+            {
+              if (!bodyelement.getAttribute("text"))
+                editor.setAttributeOrEquivalent(bodyelement, "text", defColors.TextColor, false);
+
+              // The following attributes have no individual CSS declaration counterparts
+              // Getting rid of them in favor of CSS implies CSS rules management
+              if (!bodyelement.getAttribute("link"))
+                editor.setAttribute(bodyelement, "link", defColors.LinkColor);
+
+              if (!bodyelement.getAttribute("alink"))
+                editor.setAttribute(bodyelement, "alink", defColors.ActiveLinkColor);
+
+              if (!bodyelement.getAttribute("vlink"))
+                editor.setAttribute(bodyelement, "vlink", defColors.VisitedLinkColor);
+            }
+          }
+        }
+      }
+      catch(e) {}
+
+      editor.endTransaction();
+    }
+
+    goUpdateCommandState("cmd_backgroundColor");
+  }
+  gContentWindow.focus();
+}
+
+function GetParentTable(element)
+{
+  var node = element;
+  while (node)
+  {
+    if (node.nodeName.toLowerCase() == "table")
+      return node;
+
+    node = node.parentNode;
+  }
+  return node;
+}
+
+function GetParentTableCell(element)
+{
+  var node = element;
+  while (node)
+  {
+    if (node.nodeName.toLowerCase() == "td" || node.nodeName.toLowerCase() == "th")
+      return node;
+
+    node = node.parentNode;
+  }
+  return node;
+}
+
+function EditorDblClick(event)
+{
+  // We check event.explicitOriginalTarget here because .target will never
+  // be a textnode (bug 193689)
+  if (event.explicitOriginalTarget)
+  {
+    // Only bring up properties if clicked on an element or selected link
+    var element;
+    try {
+      element = event.explicitOriginalTarget.QueryInterface(
+                    Components.interfaces.nsIDOMElement);
+    } catch (e) {}
+
+     //  We use "href" instead of "a" to not be fooled by named anchor
+    if (!element)
+      try {
+        element = GetCurrentEditor().getSelectedElement("href");
+      } catch (e) {}
+
+    if (element)
+    {
+      goDoCommand("cmd_objectProperties");  
+      event.preventDefault();
+    }
+  }
+}
+
+function EditorClick(event)
+{
+  if (!event)
+    return;
+
+  if (event.detail == 2)
+  {
+    EditorDblClick(event);
+    return;
+  }
+
+  // For Web Composer: In Show All Tags Mode,
+  // single click selects entire element,
+  //  except for body and table elements
+  if (IsWebComposer() && event.explicitOriginalTarget && IsHTMLEditor() &&
+      gEditorDisplayMode == kDisplayModeAllTags)
+  {
+    try
+    {
+      // We check event.explicitOriginalTarget here because .target will never
+      // be a textnode (bug 193689)
+      var element = event.explicitOriginalTarget.QueryInterface(
+                        Components.interfaces.nsIDOMElement);
+      var name = element.localName.toLowerCase();
+      if (name != "body" && name != "table" &&
+          name != "td" && name != "th" && name != "caption" && name != "tr")
+      {          
+        GetCurrentEditor().selectElement(event.explicitOriginalTarget);
+        event.preventDefault();
+      }
+    } catch (e) {}
+  }
+}
+
+/*TODO: We need an oncreate hook to do enabling/disabling for the
+        Format menu. There should be code like this for the
+        object-specific "Properties" item
+*/
+// For property dialogs, we want the selected element,
+//  but will accept a parent link, list, or table cell if inside one
+function GetObjectForProperties()
+{
+  var editor = GetCurrentEditor();
+  if (!editor || !IsHTMLEditor())
+    return null;
+
+  var element;
+  try {
+    element = editor.getSelectedElement("");
+  } catch (e) {}
+  if (element)
+    return element;
+
+  // Find nearest parent of selection anchor node
+  //   that is a link, list, table cell, or table
+
+  var anchorNode
+  var node;
+  try {
+    anchorNode = editor.selection.anchorNode;
+    if (anchorNode.firstChild)
+    {
+      // Start at actual selected node
+      var offset = editor.selection.anchorOffset;
+      // Note: If collapsed, offset points to element AFTER caret,
+      //  thus node may be null
+      node = anchorNode.childNodes.item(offset);
+    }
+    if (!node)
+      node = anchorNode;
+  } catch (e) {}
+
+  while (node)
+  {
+    if (node.nodeName)
+    {
+      var nodeName = node.nodeName.toLowerCase();
+
+      // Done when we hit the body
+      if (nodeName == "body") break;
+
+      if ((nodeName == "a" && node.href) ||
+          nodeName == "ol" || nodeName == "ul" || nodeName == "dl" ||
+          nodeName == "td" || nodeName == "th" ||
+          nodeName == "table")
+      {
+        return node;
+      }
+    }
+    node = node.parentNode;
+  }
+  return null;
+}
+
+function SetEditMode(mode)
+{
+  if (!IsHTMLEditor())
+    return;
+
+  var bodyElement = GetBodyElement();
+  if (!bodyElement)
+  {
+    dump("SetEditMode: We don't have a body node!\n");
+    return;
+  }
+
+  // must have editor if here!
+  var editor = GetCurrentEditor();
+  var inlineSpellCheckItem = document.getElementById('menu_inlinespellcheck');
+
+  // Switch the UI mode before inserting contents
+  //   so user can't type in source window while new window is being filled
+  var previousMode = gEditorDisplayMode;
+  if (!SetDisplayMode(mode))
+    return;
+
+  if (mode == kDisplayModeSource)
+  {
+    // Display the DOCTYPE as a non-editable string above edit area
+    var domdoc;
+    try { domdoc = editor.document; } catch (e) { dump( e + "\n");}
+    if (domdoc)
+    {
+      var doctypeNode = document.getElementById("doctype-text");
+      var dt = domdoc.doctype;
+      if (doctypeNode)
+      {
+        if (dt)
+        {
+          doctypeNode.collapsed = false;
+          var doctypeText = "<!DOCTYPE " + domdoc.doctype.name;
+          if (dt.publicId)
+            doctypeText += " PUBLIC \"" + domdoc.doctype.publicId;
+          if (dt.systemId)
+            doctypeText += " "+"\"" + dt.systemId;
+          doctypeText += "\">"
+          doctypeNode.setAttribute("value", doctypeText);
+        }
+        else
+          doctypeNode.collapsed = true;
+      }
+    }
+    // Get the entire document's source string
+
+    var flags = (editor.documentCharacterSet == "ISO-8859-1")
+      ? kOutputEncodeLatin1Entities
+      : kOutputEncodeBasicEntities;
+    try { 
+      var encodeEntity = gPrefs.getCharPref("editor.encode_entity");
+      switch (encodeEntity) {
+        case "basic"  : flags = kOutputEncodeBasicEntities; break;
+        case "latin1" : flags = kOutputEncodeLatin1Entities; break;
+        case "html"   : flags = kOutputEncodeHTMLEntities; break;
+        case "none"   : flags = 0;     break;
+      }
+    } catch (e) { }
+
+    try { 
+      var prettyPrint = gPrefs.getBoolPref("editor.prettyprint");
+      if (prettyPrint)
+        flags |= kOutputFormatted;
+
+    } catch (e) {}
+
+    flags |= kOutputLFLineBreak;
+    var source = editor.outputToString(kHTMLMimeType, flags);
+    var start = source.search(/<html/i);
+    if (start == -1) start = 0;
+    gSourceTextEditor.insertText(source.slice(start));
+    gSourceTextEditor.resetModificationCount();
+    gSourceTextEditor.addDocumentStateListener(gSourceTextListener);
+    gSourceTextEditor.enableUndo(true);
+    gSourceContentWindow.commandManager.addCommandObserver(gSourceTextObserver, "cmd_undo");
+    gSourceContentWindow.contentWindow.focus();
+    goDoCommand("cmd_moveTop");
+  }
+  else if (previousMode == kDisplayModeSource)
+  {
+    // Only rebuild document if a change was made in source window
+    if (IsHTMLSourceChanged())
+    {
+      // Disable spell checking when rebuilding source
+      InlineSpellCheckerUI.enabled = false;
+      inlineSpellCheckItem.removeAttribute('checked');
+
+      // Reduce the undo count so we don't use too much memory
+      //   during multiple uses of source window 
+      //   (reinserting entire doc caches all nodes)
+      try {
+        editor.transactionManager.maxTransactionCount = 1;
+      } catch (e) {}
+
+      editor.beginTransaction();
+      try {
+        // We are coming from edit source mode,
+        //   so transfer that back into the document
+        source = gSourceTextEditor.outputToString(kTextMimeType, kOutputLFLineBreak);
+        editor.rebuildDocumentFromSou