home *** CD-ROM | disk | FTP | other *** search
/ ftp.swcp.com / ftp.swcp.com.zip / ftp.swcp.com / mac / mozilla-macos9-1.3.1.sea.bin / Mozilla1.3.1 / Chrome / comm.jar / content / editor / editor.js < prev    next >
Text File  |  2003-06-08  |  96KB  |  3,277 lines

  1. /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: NPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Netscape Public License
  6.  * Version 1.1 (the "License"); you may not use this file except in
  7.  * compliance with the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/NPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is mozilla.org code.
  16.  *
  17.  * The Initial Developer of the Original Code is 
  18.  * Netscape Communications Corporation.
  19.  * Portions created by the Initial Developer are Copyright (C) 1998-1999
  20.  * the Initial Developer. All Rights Reserved.
  21.  *
  22.  * Contributor(s):
  23.  *    Sammy Ford (sford@swbell.net)
  24.  *    Dan Haddix (dan6992@hotmail.com)
  25.  *    John Ratke (jratke@owc.net)
  26.  *    Ryan Cassin (rcassin@supernova.org)
  27.  *    Daniel Glazman (glazman@netscape.com)
  28.  *
  29.  * Alternatively, the contents of this file may be used under the terms of
  30.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  31.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  32.  * in which case the provisions of the GPL or the LGPL are applicable instead
  33.  * of those above. If you wish to allow use of your version of this file only
  34.  * under the terms of either the GPL or the LGPL, and not to allow others to
  35.  * use your version of this file under the terms of the NPL, indicate your
  36.  * decision by deleting the provisions above and replace them with the notice
  37.  * and other provisions required by the GPL or the LGPL. If you do not delete
  38.  * the provisions above, a recipient may use your version of this file under
  39.  * the terms of any one of the NPL, the GPL or the LGPL.
  40.  *
  41.  * ***** END LICENSE BLOCK ***** */
  42.  
  43. /* Main Composer window UI control */
  44.  
  45. var gComposerWindowControllerID = 0;
  46. var prefAuthorString = "";
  47.  
  48. const kDisplayModeNormal = 0;
  49. const kDisplayModeAllTags = 1;
  50. const kDisplayModeSource = 2;
  51. const kDisplayModePreview = 3;
  52. const kDisplayModeMenuIDs = ["viewNormalMode", "viewAllTagsMode", "viewSourceMode", "viewPreviewMode"];
  53. const kDisplayModeTabIDS = ["NormalModeButton", "TagModeButton", "SourceModeButton", "PreviewModeButton"];
  54. const kBaseEditorStyleSheet = "chrome://editor/content/EditorOverride.css";
  55. const kNormalStyleSheet = "chrome://editor/content/EditorContent.css";
  56. const kAllTagsStyleSheet = "chrome://editor/content/EditorAllTags.css";
  57. const kParagraphMarksStyleSheet = "chrome://editor/content/EditorParagraphMarks.css";
  58.  
  59. const kTextMimeType = "text/plain";
  60. const kHTMLMimeType = "text/html";
  61.  
  62. const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
  63.  
  64. var gPreviousNonSourceDisplayMode = 1;
  65. var gEditorDisplayMode = -1;
  66. var gDocWasModified = false;  // Check if clean document, if clean then unload when user "Opens"
  67. var gContentWindow = 0;
  68. var gSourceContentWindow = 0;
  69. var gHTMLSourceChanged = false;
  70. var gContentWindowDeck;
  71. var gFormatToolbar;
  72. var gFormatToolbarHidden = false;
  73. var gViewFormatToolbar;
  74. var gColorObj = { LastTextColor:"", LastBackgroundColor:"", LastHighlightColor:"",
  75.                   Type:"", SelectedType:"", NoDefault:false, Cancel:false,
  76.                   HighlightColor:"", BackgroundColor:"", PageColor:"",
  77.                   TextColor:"", TableColor:"", CellColor:""
  78.                 };
  79. var gDefaultTextColor = "";
  80. var gDefaultBackgroundColor = "";
  81. var gCSSPrefListener;
  82. var gPrefs;
  83.  
  84. var gLastFocusNode = null;
  85. var gLastFocusNodeWasSelected = false;
  86.  
  87. // These must be kept in synch with the XUL <options> lists
  88. var gFontSizeNames = ["xx-small","x-small","small","medium","large","x-large","xx-large"];
  89.  
  90. const nsIFilePicker = Components.interfaces.nsIFilePicker;
  91.  
  92. function nsButtonPrefListener()
  93. {
  94.   try {
  95.     var pbi = pref.QueryInterface(Components.interfaces.nsIPrefBranchInternal);
  96.     pbi.addObserver(this.domain, this, false);
  97.   } catch(ex) {
  98.     dump("Failed to observe prefs: " + ex + "\n");
  99.   }
  100. }
  101.  
  102. // implements nsIObserver
  103. nsButtonPrefListener.prototype =
  104. {
  105.   domain: "editor.use_css",
  106.   observe: function(subject, topic, prefName)
  107.   {
  108.     if (!IsHTMLEditor())
  109.       return;
  110.     // verify that we're changing a button pref
  111.     if (topic != "nsPref:changed") return;
  112.     if (prefName.substr(0, this.domain.length) != this.domain) return;
  113.  
  114.     var cmd = document.getElementById("cmd_highlight");
  115.     if (cmd) {
  116.       var prefs = GetPrefs();
  117.       var useCSS = prefs.getBoolPref(prefName);
  118.       var editor = GetCurrentEditor();
  119.       if (useCSS && editor) {
  120.         var mixedObj = {};
  121.         var state = editor.getHighlightColorState(mixedObj);
  122.         cmd.setAttribute("state", state);
  123.         cmd.collapsed = false;
  124.       }      
  125.       else {
  126.         cmd.setAttribute("state", "transparent");
  127.         cmd.collapsed = true;
  128.       }
  129.  
  130.       if (editor)
  131.         editor.isCSSEnabled = useCSS;
  132.     }
  133.   }
  134. }
  135.  
  136. function AfterHighlightColorChange()
  137. {
  138.   if (!IsHTMLEditor())
  139.     return;
  140.  
  141.   var button = document.getElementById("cmd_highlight");
  142.   if (button) {
  143.     var mixedObj = {};
  144.     try {
  145.       var state = GetCurrentEditor().getHighlightColorState(mixedObj);
  146.       button.setAttribute("state", state);
  147.       onHighlightColorChange();
  148.     } catch (e) {}
  149.   }      
  150. }
  151.  
  152. function EditorOnLoad()
  153. {
  154.     // See if argument was passed.
  155.     if ( window.arguments && window.arguments[0] ) {
  156.         // Opened via window.openDialog with URL as argument.
  157.         // Put argument where EditorStartup expects it.
  158.         document.getElementById( "args" ).setAttribute( "value", window.arguments[0] );
  159.     }
  160.  
  161.     // get default character set if provided
  162.     if ("arguments" in window && window.arguments.length > 1 && window.arguments[1]) {
  163.       if (window.arguments[1].indexOf("charset=") != -1) {
  164.         var arrayArgComponents = window.arguments[1].split("=");
  165.         if (arrayArgComponents) {
  166.           // Put argument where EditorStartup expects it.
  167.           document.getElementById( "args" ).setAttribute("charset", arrayArgComponents[1]);
  168.         }
  169.       }
  170.     }
  171.  
  172.     window.tryToClose = EditorCanClose;
  173.  
  174.     // Continue with normal startup.
  175.     EditorStartup();
  176. }
  177.  
  178. function TextEditorOnLoad()
  179. {
  180.     // See if argument was passed.
  181.     if ( window.arguments && window.arguments[0] ) {
  182.         // Opened via window.openDialog with URL as argument.
  183.         // Put argument where EditorStartup expects it.
  184.         document.getElementById( "args" ).setAttribute( "value", window.arguments[0] );
  185.     }
  186.     // Continue with normal startup.
  187.     EditorStartup();
  188. }
  189.  
  190. // This should be called by all editor users when they close their window
  191. //  or other similar "done with editor" actions, like recycling a Mail Composer window.
  192. function EditorCleanup()
  193. {
  194.   SwitchInsertCharToAnotherEditorOrClose();
  195. }
  196.  
  197. var DocumentReloadListener =
  198. {
  199.   NotifyDocumentCreated: function() {},
  200.   NotifyDocumentWillBeDestroyed: function() {},
  201.  
  202.   NotifyDocumentStateChanged:function( isNowDirty )
  203.   {
  204.     var editor = GetCurrentEditor();
  205.     try {
  206.       // unregister the listener to prevent multiple callbacks
  207.       editor.removeDocumentStateListener( DocumentReloadListener );
  208.  
  209.       var charset = editor.documentCharacterSet;
  210.  
  211.       // update the META charset with the current presentation charset
  212.       editor.documentCharacterSet = charset;
  213.  
  214.     } catch (e) {}
  215.   }
  216. };
  217.  
  218. function addEditorClickEventListener()
  219. {
  220.   try {
  221.     var bodyelement = GetBodyElement();
  222.     if (bodyelement)
  223.       bodyelement.addEventListener("click", EditorClick, false);
  224.   } catch (e) {}
  225. }
  226.  
  227. // implements nsIObserver
  228. var gEditorDocumentObserver =
  229.   observe: function(aSubject, aTopic, aData)
  230.   {
  231.     // Should we allow this even if NOT the focused editor?
  232.     var commandManager = GetCurrentCommandManager();
  233.     if (commandManager != aSubject)
  234.       return;
  235.  
  236.     var editor = GetCurrentEditor();
  237.     switch(aTopic)
  238.     {
  239.       case "obs_documentCreated":
  240.         // Just for convenience
  241.         gContentWindow = window.content;
  242.  
  243.         // Get state to see if document creation succeeded
  244.         var params = newCommandParams();
  245.         if (!params)
  246.           return;
  247.  
  248.         try {
  249.           commandManager.getCommandState(aTopic, gContentWindow, params);
  250.           var errorStringId = 0;
  251.           var editorStatus = params.getLongValue("state_data");
  252.           if (!editor && editorStatus == nsIEditingSession.eEditorOK)
  253.           {
  254.             dump("\n ****** NO EDITOR BUT NO EDITOR ERROR REPORTED ******* \n\n");
  255.             editorStatus = nsIEditingSession.eEditorErrorUnkown;
  256.           }
  257.  
  258.           switch (editorStatus)
  259.           {
  260.             case nsIEditingSession.eEditorErrorCantEditFramesets:
  261.               errorStringId = "CantEditFramesetMsg";
  262.               break;
  263.             case nsIEditingSession.eEditorErrorCantEditMimeType:
  264.               errorStringId = "CantEditMimeTypeMsg";
  265.               break;
  266.             case nsIEditingSession.eEditorErrorUnkown:
  267.               errorStringId = "CantEditDocumentMsg";
  268.               break;
  269.             // Note that for "eEditorErrorFileNotFound, 
  270.             // network code popped up an alert dialog, so we don't need to
  271.           }
  272.           if (errorStringId)
  273.             AlertWithTitle("", GetString(errorStringId));
  274.         } catch(e) { dump("EXCEPTION GETTING obs_documentCreated state "+e+"\n"); }
  275.  
  276.         // We have a bad editor -- nsIEditingSession will rebuild an editor
  277.         //   with a blank page, so simply abort here
  278.         if (editorStatus)
  279.           return; 
  280.  
  281.         var editorType = GetCurrentEditorType();
  282.         if (editorType != "htmlmail"
  283.             && editorType != "textmail")
  284.         {
  285.           // Set focus to content window if not a mail composer
  286.           // Race conditions prevent us from setting focus here
  287.           //   when loading a url into blank window
  288.           setTimeout("SetFocusOnStartup()", 0);
  289.         }
  290.  
  291.         if (!("InsertCharWindow" in window))
  292.           window.InsertCharWindow = null;
  293.  
  294.         try {
  295.           editor.QueryInterface(nsIEditorStyleSheets);
  296.  
  297.           // Add the base sheets for editor cursor etc.
  298.           editor.addOverrideStyleSheet(kBaseEditorStyleSheet);
  299.  
  300.           //  and extra styles for showing anchors, table borders, smileys, etc
  301.           editor.addOverrideStyleSheet(kNormalStyleSheet);
  302.         } catch (e) {}
  303.  
  304.         // Things for just the Web Composer application
  305.         if (IsWebComposer())
  306.         {
  307.           // Get the window command controller created in SetupComposerWindowCommands()
  308.           if (gComposerWindowControllerID)
  309.           {
  310.             try { 
  311.               var controller = window.controllers.getControllerById(gComposerWindowControllerID);
  312.               controller.SetCommandRefCon(editor.QueryInterface(Components.interfaces.nsISupports));
  313.             } catch (e) {}
  314.           }
  315.           // Call EditorSetDefaultPrefsAndDoctype first so it gets the default author before initing toolbars
  316.           EditorSetDefaultPrefsAndDoctype();
  317.  
  318.           // We may load a text document into an html editor,
  319.           //   so be sure editortype is set correctly
  320.           // XXX We really should use the "real" plaintext editor for this!
  321.           if (editor.contentsMIMEType == "text/plain")
  322.           {
  323.             try {
  324.               GetCurrentEditorElement().editortype = "text";
  325.             } catch (e) { dump (e)+"\n"; }
  326.  
  327.             // Hide or disable UI not used for plaintext editing
  328.             HideItem("FormatToolbar");
  329.             HideItem("EditModeToolbar");
  330.             HideItem("formatMenu");
  331.             HideItem("tableMenu");
  332.             HideItem("menu_validate");
  333.             HideItem("sep_validate");
  334.             HideItem("previewButton");
  335.             HideItem("imageButton");
  336.             HideItem("linkButton");
  337.             HideItem("namedAnchorButton");
  338.             HideItem("hlineButton");
  339.             HideItem("tableButton");
  340.  
  341.             SetElementEnabledById("cmd_viewFormatToolbar", false);
  342.             SetElementEnabledById("cmd_viewEditModeToolbar", false);
  343.  
  344.             // Hide everything in "Insert" except for "Symbols"
  345.             var menuPopup = document.getElementById("insertMenuPopup");
  346.             if (menuPopup)
  347.             {
  348.               var children = menuPopup.childNodes;
  349.               for (var i=0; i < children.length; i++) 
  350.               {
  351.                 var item = children.item(i);
  352.                 if (item.id != "insertChars")
  353.                   item.hidden = true;
  354.               }
  355.             }
  356.           }
  357.     
  358.           // Set window title
  359.           UpdateWindowTitle();
  360.  
  361.           // We must wait until document is created to get proper Url
  362.           // (Windows may load with local file paths)
  363.           SetSaveAndPublishUI(GetDocumentUrl());
  364.  
  365.           // Start in "Normal" edit mode
  366.           SetDisplayMode(kDisplayModeNormal);
  367.         }
  368.  
  369.         // Add mouse click watcher if right type of editor
  370.         if (IsHTMLEditor())
  371.         {
  372.           addEditorClickEventListener();
  373.  
  374.           // Force color widgets to update
  375.           onFontColorChange();
  376.           onBackgroundColorChange();
  377.         }
  378.  
  379.         break;
  380.  
  381.       case "cmd_setDocumentModified":
  382.         window.updateCommands("save");
  383.         break;
  384.  
  385.       case "obs_documentWillBeDestroyed":
  386.         dump("obs_documentWillBeDestroyed notification\n");
  387.         break;
  388.  
  389.       case "obs_documentLocationChanged":
  390.         // Ignore this when editor doesn't exist,
  391.         //   which happens once when page load starts
  392.         if (editor)
  393.           try {
  394.             editor.updateBaseURL();
  395.           } catch(e) { dump (e); }
  396.         break;
  397.  
  398.       case "cmd_bold":
  399.         // Update all style items
  400.         // cmd_bold is a proxy; see EditorSharedStartup (above) for details
  401.         window.updateCommands("style");
  402.         window.updateCommands("undo");
  403.         break;
  404.     }
  405.   }
  406. }
  407.  
  408. function SetFocusOnStartup()
  409. {
  410.   gContentWindow.focus();
  411. }
  412.  
  413. function EditorStartup()
  414. {
  415.   var is_HTMLEditor = IsHTMLEditor();
  416.   if (is_HTMLEditor)
  417.   {
  418.     gSourceContentWindow = document.getElementById("content-source");
  419.  
  420.     // XUL elements we use when switching from normal editor to edit source
  421.     gContentWindowDeck = document.getElementById("ContentWindowDeck");
  422.     gFormatToolbar = document.getElementById("FormatToolbar");
  423.     gViewFormatToolbar = document.getElementById("viewFormatToolbar");
  424.   }
  425.  
  426.   // set up our global prefs object
  427.   GetPrefsService();
  428.  
  429.   // Startup also used by other editor users, such as Message Composer
  430.   EditorSharedStartup();
  431.  
  432.   // Commands specific to the Composer Application window,
  433.   //  (i.e., not embedded editors)
  434.   //  such as file-related commands, HTML Source editing, Edit Modes...
  435.   SetupComposerWindowCommands();
  436.  
  437.   gCSSPrefListener = new nsButtonPrefListener();
  438.  
  439.   // hide Highlight button if we are in an HTML editor with CSS mode off
  440.   var cmd = document.getElementById("cmd_highlight");
  441.   if (cmd) {
  442.     var prefs = GetPrefs();
  443.     var useCSS = prefs.getBoolPref("editor.use_css");
  444.     if (!useCSS && is_HTMLEditor) {
  445.       cmd.collapsed = true;
  446.     }
  447.   }
  448.  
  449.   // Get url for editor content and load it.
  450.   // the editor gets instantiated by the edittingSession when the URL has finished loading.
  451.   var url = document.getElementById("args").getAttribute("value");
  452.   try {
  453.     var charset = document.getElementById("args").getAttribute("charset");
  454.     var contentViewer = GetCurrentEditorElement().docShell.contentViewer;
  455.     contentViewer.QueryInterface(Components.interfaces.nsIMarkupDocumentViewer);
  456.     contentViewer.defaultCharacterSet = charset;
  457.     contentViewer.forceCharacterSet = charset;
  458.   } catch (e) {}
  459.   EditorLoadUrl(url);
  460. }
  461.  
  462. function EditorLoadUrl(url)
  463. {
  464.   try {
  465.     if (url)
  466.       GetCurrentEditorElement().webNavigation.loadURI(url, // uri string
  467.              nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE,     // load flags
  468.              null,                                         // referrer
  469.              null,                                         // post-data stream
  470.              null);
  471.   } catch (e) { dump(" EditorLoadUrl failed: "+e+"\n"); }
  472. }
  473.  
  474. // This should be called by all Composer types
  475. function EditorSharedStartup()
  476. {
  477.   // Just for convenience
  478.   gContentWindow = window.content;
  479.  
  480.   // Set up the mime type and register the commands.
  481.   if (IsHTMLEditor)
  482.     SetupHTMLEditorCommands();
  483.   else
  484.     SetupTextEditorCommands();
  485.  
  486.   // add observer to be called when document is really done loading 
  487.   // and is modified
  488.   // Note: We're really screwed if we fail to install this observer!
  489.   try {
  490.     var commandManager = GetCurrentCommandManager();
  491.     commandManager.addCommandObserver(gEditorDocumentObserver, "obs_documentCreated");
  492.     commandManager.addCommandObserver(gEditorDocumentObserver, "cmd_setDocumentModified");
  493.     commandManager.addCommandObserver(gEditorDocumentObserver, "obs_documentWillBeDestroyed");
  494.     commandManager.addCommandObserver(gEditorDocumentObserver, "obs_documentLocationChanged");
  495.  
  496.     // Until nsIControllerCommandGroup-based code is implemented,
  497.     //  we will observe just the bold command to trigger update of
  498.     //  all toolbar style items
  499.     commandManager.addCommandObserver(gEditorDocumentObserver, "cmd_bold");
  500.   } catch (e) { dump(e); }
  501.  
  502.   var isMac = (GetOS() == gMac);
  503.  
  504.   // Set platform-specific hints for how to select cells
  505.   // Mac uses "Cmd", all others use "Ctrl"
  506.   var tableKey = GetString(isMac ? "XulKeyMac" : "TableSelectKey");
  507.   var dragStr = tableKey+GetString("Drag");
  508.   var clickStr = tableKey+GetString("Click");
  509.  
  510.   var delStr = GetString(isMac ? "Clear" : "Del");
  511.  
  512.   SafeSetAttribute("menu_SelectCell", "acceltext", clickStr);
  513.   SafeSetAttribute("menu_SelectRow", "acceltext", dragStr);
  514.   SafeSetAttribute("menu_SelectColumn", "acceltext", dragStr);
  515.   SafeSetAttribute("menu_SelectAllCells", "acceltext", dragStr);
  516.   // And add "Del" or "Clear"
  517.   SafeSetAttribute("menu_DeleteCellContents", "acceltext", delStr);
  518.  
  519.   // Set text for indent, outdent keybinding
  520.  
  521.   // hide UI that we don't have components for
  522.   RemoveInapplicableUIElements();
  523.  
  524.   gPrefs = GetPrefs();
  525.  
  526.   // Use browser colors as initial values for editor's default colors
  527.   var BrowserColors = GetDefaultBrowserColors();
  528.   if (BrowserColors)
  529.   {
  530.     gDefaultTextColor = BrowserColors.TextColor;
  531.     gDefaultBackgroundColor = BrowserColors.BackgroundColor;
  532.   }
  533.  
  534.   // For new window, no default last-picked colors
  535.   gColorObj.LastTextColor = "";
  536.   gColorObj.LastBackgroundColor = "";
  537.   gColorObj.LastHighlightColor = "";
  538. }
  539.  
  540. // This method is only called by Message composer when recycling a compose window
  541. function EditorResetFontAndColorAttributes()
  542. {
  543.   try {  
  544.     document.getElementById("cmd_fontFace").setAttribute("state", "");
  545.     EditorRemoveTextProperty("font", "color");
  546.     EditorRemoveTextProperty("font", "bgcolor");
  547.     EditorRemoveTextProperty("font", "size");
  548.     EditorRemoveTextProperty("small", "");
  549.     EditorRemoveTextProperty("big", "");
  550.     var bodyelement = GetBodyElement();
  551.     if (bodyelement)
  552.     {
  553.       var editor = GetCurrentEditor();
  554.       editor.removeAttributeOrEquivalent(bodyelement, "text", true);
  555.       editor.removeAttributeOrEquivalent(bodyelement, "bgcolor", true);
  556.       bodyelement.removeAttribute("link");
  557.       bodyelement.removeAttribute("alink");
  558.       bodyelement.removeAttribute("vlink");
  559.       editor.removeAttributeOrEquivalent(bodyelement, "background", true);
  560.     }
  561.     gColorObj.LastTextColor = "";
  562.     gColorObj.LastBackgroundColor = "";
  563.     gColorObj.LastHighlightColor = "";
  564.     document.getElementById("cmd_fontColor").setAttribute("state", "");
  565.     document.getElementById("cmd_backgroundColor").setAttribute("state", "");
  566.     UpdateDefaultColors();
  567.   } catch (e) {}
  568. }
  569.  
  570. function EditorShutdown()
  571. {
  572.   try {
  573.     var commandManager = GetCurrentCommandManager();
  574.     commandManager.removeCommandObserver(gEditorDocumentObserver, "obs_documentCreated");
  575.     commandManager.removeCommandObserver(gEditorDocumentObserver, "obs_documentWillBeDestroyed");
  576.     commandManager.removeCommandObserver(gEditorDocumentObserver, "obs_documentLocationChanged");
  577.   } catch (e) { dump (e); }   
  578. }
  579.  
  580. function SafeSetAttribute(nodeID, attributeName, attributeValue)
  581. {
  582.     var theNode = document.getElementById(nodeID);
  583.     if (theNode)
  584.         theNode.setAttribute(attributeName, attributeValue);
  585. }
  586.  
  587. function DocumentHasBeenSaved()
  588. {
  589.   var fileurl = "";
  590.   try {
  591.     fileurl = GetDocumentUrl();
  592.   } catch (e) {
  593.     return false;
  594.   }
  595.  
  596.   if (!fileurl || IsUrlAboutBlank(fileurl))
  597.     return false;
  598.  
  599.   // We have a file URL already
  600.   return true;
  601. }
  602.  
  603. function CheckAndSaveDocument(command, allowDontSave)
  604. {
  605.   var document;
  606.   try {
  607.     // if we don't have an editor or an document, bail
  608.     var editor = GetCurrentEditor();
  609.     document = editor.document;
  610.     if (!document)
  611.       return true;
  612.   } catch (e) { return true; }
  613.  
  614.   if (!IsDocumentModified() && !gHTMLSourceChanged)
  615.     return true;
  616.  
  617.   // call window.focus, since we need to pop up a dialog
  618.   // and therefore need to be visible (to prevent user confusion)
  619.   window.focus();  
  620.  
  621.   var scheme = GetScheme(GetDocumentUrl());
  622.   var doPublish = (scheme && scheme != "file");
  623.  
  624.   var strID;
  625.   switch (command)
  626.   {
  627.     case "cmd_close":
  628.       strID = "BeforeClosing";
  629.       break;
  630.     case "cmd_preview":
  631.       strID = "BeforePreview";
  632.       break;
  633.     case "cmd_editSendPage":
  634.       strID = "SendPageReason";
  635.       break;
  636.     case "cmd_validate":
  637.       strID = "BeforeValidate";
  638.       break;
  639.   }
  640.     
  641.   var reasonToSave = strID ? GetString(strID) : "";
  642.  
  643.   var title = document.title;
  644.   if (!title)
  645.     title = GetString("untitled");
  646.  
  647.   var dialogTitle = GetString(doPublish ? "PublishPage" : "SaveDocument");
  648.   var dialogMsg = GetString(doPublish ? "PublishPrompt" : "SaveFilePrompt");
  649.   dialogMsg = (dialogMsg.replace(/%title%/,title)).replace(/%reason%/,reasonToSave);
  650.  
  651.   var promptService = GetPromptService();
  652.   if (!promptService)
  653.     return false;
  654.  
  655.   var result = {value:0};
  656.   var promptFlags = promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_1;
  657.   var button1Title = null;
  658.   var button3Title = null;
  659.  
  660.   if (doPublish)
  661.   {
  662.     promptFlags += promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0;
  663.     button1Title = GetString("Publish");
  664.     button3Title = GetString("DontPublish");    
  665.   }
  666.   else
  667.   {
  668.     promptFlags += promptService.BUTTON_TITLE_SAVE * promptService.BUTTON_POS_0;
  669.   }
  670.  
  671.   // If allowing "Don't..." button, add that
  672.   if (allowDontSave)
  673.     promptFlags += doPublish ?
  674.         (promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_2)
  675.         : (promptService.BUTTON_TITLE_DONT_SAVE * promptService.BUTTON_POS_2);
  676.   
  677.   result = promptService.confirmEx(window, dialogTitle, dialogMsg, promptFlags,
  678.                           button1Title, null, button3Title, null, {value:0});
  679.  
  680.   if (result == 0)
  681.   {
  682.     // Save, but first finish HTML source mode
  683.     if (gHTMLSourceChanged) {
  684.       try {
  685.         FinishHTMLSource();
  686.       } catch (e) { return false;}
  687.     }
  688.  
  689.     if (doPublish)
  690.     {
  691.       // We save the command the user wanted to do in a global
  692.       // and return as if user canceled because publishing is asynchronous
  693.       // This command will be fired when publishing finishes
  694.       gCommandAfterPublishing = command;
  695.       goDoCommand("cmd_publish");
  696.       return false;
  697.     }
  698.  
  699.     // Save to local disk
  700.     var contentsMIMEType;
  701.     if (IsHTMLEditor())
  702.       contentsMIMEType = kHTMLMimeType;
  703.     else
  704.       contentsMIMEType = kTextMimeType;
  705.     var success = SaveDocument(false, false, contentsMIMEType);
  706.     return success;
  707.   }
  708.  
  709.   if (result == 2) // "Don't Save"
  710.     return true;
  711.  
  712.   // Default or result == 1 (Cancel)
  713.   return false;
  714. }
  715.  
  716. // --------------------------- File menu ---------------------------
  717.  
  718.  
  719. // used by openLocation. see openLocation.js for additional notes.
  720. function delayedOpenWindow(chrome, flags, url)
  721. {
  722.   dump("setting timeout\n");
  723.   setTimeout("window.openDialog('"+chrome+"','_blank','"+flags+"','"+url+"')", 10);
  724. }
  725.  
  726. function EditorNewPlaintext()
  727. {
  728.   window.openDialog( "chrome://editor/content/TextEditorAppShell.xul",
  729.                      "_blank",
  730.                      "chrome,dialog=no,all",
  731.                      "about:blank");
  732. }
  733.  
  734. // Check for changes to document and allow saving before closing
  735. // This is hooked up to the OS's window close widget (e.g., "X" for Windows)
  736. function EditorCanClose()
  737. {
  738.   // Returns FALSE only if user cancels save action
  739.  
  740.   // "true" means allow "Don't Save" button
  741.   var canClose = CheckAndSaveDocument("cmd_close", true);
  742.  
  743.   // This is our only hook into closing via the "X" in the caption
  744.   //   or "Quit" (or other paths?)
  745.   //   so we must shift association to another
  746.   //   editor or close any non-modal windows now
  747.   if (canClose && "InsertCharWindow" in window && window.InsertCharWindow)
  748.     SwitchInsertCharToAnotherEditorOrClose();
  749.  
  750.   return canClose;
  751. }
  752.  
  753. // --------------------------- View menu ---------------------------
  754.  
  755. function EditorSetDocumentCharacterSet(aCharset)
  756. {
  757.   try {
  758.     var editor = GetCurrentEditor();
  759.     editor.documentCharacterSet = aCharset;
  760.     var docUrl = GetDocumentUrl();
  761.     if( !IsUrlAboutBlank(docUrl))
  762.     {
  763.       // reloading the document will reverse any changes to the META charset, 
  764.       // we need to put them back in, which is achieved by a dedicated listener
  765.       editor.addDocumentStateListener( DocumentReloadListener );
  766.       EditorLoadUrl(docUrl);
  767.     }
  768.   } catch (e) {}
  769. }
  770.  
  771. // ------------------------------------------------------------------
  772. function updateCharsetPopupMenu(menuPopup)
  773. {
  774.   if (IsDocumentModified() && !IsDocumentEmpty())
  775.   {
  776.     for (var i = 0; i < menuPopup.childNodes.length; i++)
  777.     {
  778.       var menuItem = menuPopup.childNodes[i];
  779.       menuItem.setAttribute('disabled', 'true');
  780.     }
  781.   }
  782. }
  783.  
  784. // --------------------------- Text style ---------------------------
  785.  
  786. function onParagraphFormatChange(paraMenuList, commandID)
  787. {
  788.   if (!paraMenuList)
  789.     return;
  790.  
  791.   var commandNode = document.getElementById(commandID);
  792.   var state = commandNode.getAttribute("state");
  793.  
  794.   // force match with "normal"
  795.   if (state == "body")
  796.     state = "";
  797.  
  798.   if (state == "mixed")
  799.   {
  800.     //Selection is the "mixed" ( > 1 style) state
  801.     paraMenuList.selectedItem = null;
  802.     paraMenuList.setAttribute("label",GetString('Mixed'));
  803.   }
  804.   else
  805.   {
  806.     var menuPopup = document.getElementById("ParagraphPopup");
  807.     var menuItems = menuPopup.childNodes;
  808.     for (var i=0; i < menuItems.length; i++)
  809.     {
  810.       var menuItem = menuItems.item(i);
  811.       if ("value" in menuItem && menuItem.value == state)
  812.       {
  813.         paraMenuList.selectedItem = menuItem;
  814.         break;
  815.       }
  816.     }
  817.   }
  818. }
  819.  
  820. function onFontFaceChange(fontFaceMenuList, commandID)
  821. {
  822.   var commandNode = document.getElementById(commandID);
  823.   var state = commandNode.getAttribute("state");
  824.  
  825.   if (state == "mixed")
  826.   {
  827.     //Selection is the "mixed" ( > 1 style) state
  828.     fontFaceMenuList.selectedItem = null;
  829.     fontFaceMenuList.setAttribute("label",GetString('Mixed'));
  830.   }
  831.   else
  832.   {
  833.     var menuPopup = document.getElementById("FontFacePopup");
  834.     var menuItems = menuPopup.childNodes;
  835.     for (var i=0; i < menuItems.length; i++)
  836.     {
  837.       var menuItem = menuItems.item(i);
  838.       if (menuItem.getAttribute("label") && ("value" in menuItem && menuItem.value.toLowerCase() == state.toLowerCase()))
  839.       {
  840.         fontFaceMenuList.selectedItem = menuItem;
  841.         break;
  842.       }
  843.     }
  844.   }
  845. }
  846.  
  847. function EditorSelectFontSize()
  848. {
  849.   var select = document.getElementById("FontSizeSelect");
  850.   if (select)
  851.   {
  852.     if (select.selectedIndex == -1)
  853.       return;
  854.  
  855.     EditorSetFontSize(gFontSizeNames[select.selectedIndex]);
  856.   }
  857. }
  858.  
  859. function onFontSizeChange(fontSizeMenulist, commandID)
  860. {
  861.   // If we don't match anything, set to "0 (normal)"
  862.   var newIndex = 2;
  863.   var size = fontSizeMenulist.getAttribute("size");
  864.   if ( size == "mixed")
  865.   {
  866.     // No single type selected
  867.     newIndex = -1;
  868.   }
  869.   else
  870.   {
  871.     for (var i = 0; i < gFontSizeNames.length; i++)
  872.     {
  873.       if( gFontSizeNames[i] == size )
  874.       {
  875.         newIndex = i;
  876.         break;
  877.       }
  878.     }
  879.   }
  880.   if (fontSizeMenulist.selectedIndex != newIndex)
  881.     fontSizeMenulist.selectedIndex = newIndex;
  882. }
  883.  
  884. function EditorSetFontSize(size)
  885. {
  886.   if( size == "0" || size == "normal" ||
  887.       size == "medium" )
  888.   {
  889.     EditorRemoveTextProperty("font", "size");
  890.     // Also remove big and small,
  891.     //  else it will seem like size isn't changing correctly
  892.     EditorRemoveTextProperty("small", "");
  893.     EditorRemoveTextProperty("big", "");
  894.   } else {
  895.     // Temp: convert from new CSS size strings to old HTML size strings
  896.     switch (size)
  897.     {
  898.       case "xx-small":
  899.       case "x-small":
  900.         size = "-2";
  901.         break;
  902.       case "small":
  903.         size = "-1";
  904.         break;
  905.       case "large":
  906.         size = "+1";
  907.         break;
  908.       case "x-large":
  909.         size = "+2";
  910.         break;
  911.       case "xx-large":
  912.         size = "+3";
  913.         break;
  914.     }
  915.     EditorSetTextProperty("font", "size", size);
  916.   }
  917.   gContentWindow.focus();
  918. }
  919.  
  920. function initFontFaceMenu(menuPopup)
  921. {
  922.   if (menuPopup)
  923.   {
  924.     var children = menuPopup.childNodes;
  925.     if (!children) return;
  926.  
  927.     var firstHas = { value: false };
  928.     var anyHas = { value: false };
  929.     var allHas = { value: false };
  930.  
  931.     // we need to set or clear the checkmark for each menu item since the selection
  932.     // may be in a new location from where it was when the menu was previously opened
  933.  
  934.     // Fixed width (second menu item) is special case: old TT ("teletype") attribute
  935.     EditorGetTextProperty("tt", "", "", firstHas, anyHas, allHas);
  936.     children[1].setAttribute("checked", allHas.value);
  937.     var fontWasFound = anyHas.value;
  938.  
  939.     // Skip over default, TT, and separator
  940.     for (var i = 3; i < children.length; i++)
  941.     {
  942.       var menuItem = children[i];
  943.       var faceType = menuItem.getAttribute("value");
  944.  
  945.       if (faceType)
  946.       {
  947.         EditorGetTextProperty("font", "face", faceType, firstHas, anyHas, allHas);
  948.  
  949.         // Check the item only if all of selection has the face...
  950.         menuItem.setAttribute("checked", allHas.value);
  951.         // ...but remember if ANY part of the selection has it
  952.         fontWasFound |= anyHas.value;
  953.       }
  954.     }
  955.     // Check the default item if no other item was checked
  956.     // note that no item is checked in the case of "mixed" selection
  957.     children[0].setAttribute("checked", !fontWasFound);
  958.   }
  959. }
  960.  
  961. function initFontSizeMenu(menuPopup)
  962. {
  963.   if (menuPopup)
  964.   {
  965.     var children = menuPopup.childNodes;
  966.     if (!children) return;
  967.  
  968.     var firstHas = { value: false };
  969.     var anyHas = { value: false };
  970.     var allHas = { value: false };
  971.  
  972.     var sizeWasFound = false;
  973.  
  974.     // we need to set or clear the checkmark for each menu item since the selection
  975.     // may be in a new location from where it was when the menu was previously opened
  976.  
  977.     // First 2 items add <small> and <big> tags
  978.     // While it would be better to show the number of levels,
  979.     //  at least this tells user if either of them are set
  980.     var menuItem = children[0];
  981.     if (menuItem)
  982.     {
  983.       EditorGetTextProperty("small", "", "", firstHas, anyHas, allHas);
  984.       menuItem.setAttribute("checked", allHas.value);
  985.       sizeWasFound = anyHas.value;
  986.     }
  987.  
  988.     menuItem = children[1];
  989.     if (menuItem)
  990.     {
  991.       EditorGetTextProperty("big", "", "", firstHas, anyHas, allHas);
  992.       menuItem.setAttribute("checked", allHas.value);
  993.       sizeWasFound |= anyHas.value;
  994.     }
  995.  
  996.     // Fixed size items start after menu separator
  997.     var menuIndex = 3;
  998.     // Index of the medium (default) item
  999.     var mediumIndex = 5;
  1000.  
  1001.     // Scan through all supported "font size" attribute values
  1002.     for (var i = -2; i <= 3; i++)
  1003.     {
  1004.       menuItem = children[menuIndex];
  1005.  
  1006.       // Skip over medium since it'll be set below.
  1007.       // If font size=0 is actually set, we'll toggle it off below if
  1008.       // we enter this loop in this case.
  1009.       if (menuItem && (i != 0))
  1010.       {
  1011.         var sizeString = (i <= 0) ? String(i) : ("+" + String(i));
  1012.         EditorGetTextProperty("font", "size", sizeString, firstHas, anyHas, allHas);
  1013.         // Check the item only if all of selection has the size...
  1014.         menuItem.setAttribute("checked", allHas.value);
  1015.         // ...but remember if ANY of of selection had size set
  1016.         sizeWasFound |= anyHas.value;
  1017.       }
  1018.       menuIndex++;
  1019.     }
  1020.  
  1021.     // if no size was found, then check default (medium)
  1022.     // note that no item is checked in the case of "mixed" selection
  1023.     children[mediumIndex].setAttribute("checked", !sizeWasFound);
  1024.   }
  1025.  }
  1026.  
  1027. function onHighlightColorChange()
  1028. {
  1029.   var commandNode = document.getElementById("cmd_highlight");
  1030.   if (commandNode)
  1031.   {
  1032.     var color = commandNode.getAttribute("state");
  1033.     var button = document.getElementById("HighlightColorButton");
  1034.     if (button)
  1035.     {
  1036.       // No color set - get color set on page or other defaults
  1037.       if (!color)
  1038.         color = "transparent" ;
  1039.  
  1040.       button.setAttribute("style", "background-color:"+color+" !important");
  1041.     }
  1042.   }
  1043. }
  1044.  
  1045. function onFontColorChange()
  1046. {
  1047.   var commandNode = document.getElementById("cmd_fontColor");
  1048.   if (commandNode)
  1049.   {
  1050.     var color = commandNode.getAttribute("state");
  1051.     var button = document.getElementById("TextColorButton");
  1052.     if (button)
  1053.     {
  1054.       // No color set - get color set on page or other defaults
  1055.       if (!color)
  1056.         color = gDefaultTextColor;
  1057.       button.setAttribute("style", "background-color:"+color);
  1058.     }
  1059.   }
  1060. }
  1061.  
  1062. function onBackgroundColorChange()
  1063. {
  1064.   var commandNode = document.getElementById("cmd_backgroundColor");
  1065.   if (commandNode)
  1066.   {
  1067.     var color = commandNode.getAttribute("state");
  1068.     var button = document.getElementById("BackgroundColorButton");
  1069.     if (button)
  1070.     {
  1071.       if (!color)
  1072.         color = gDefaultBackgroundColor;
  1073.  
  1074.       button.setAttribute("style", "background-color:"+color);
  1075.     }
  1076.   }
  1077. }
  1078.  
  1079. // Call this when user changes text and/or background colors of the page
  1080. function UpdateDefaultColors()
  1081. {
  1082.   var BrowserColors = GetDefaultBrowserColors();
  1083.   var bodyelement = GetBodyElement();
  1084.   var defTextColor = gDefaultTextColor;
  1085.   var defBackColor = gDefaultBackgroundColor;
  1086.  
  1087.   if (bodyelement)
  1088.   {
  1089.     var color = bodyelement.getAttribute("text");
  1090.     if (color)
  1091.       gDefaultTextColor = color;
  1092.     else if (BrowserColors)
  1093.       gDefaultTextColor = BrowserColors.TextColor;
  1094.  
  1095.     color = bodyelement.getAttribute("bgcolor");
  1096.     if (color)
  1097.       gDefaultBackgroundColor = color;
  1098.     else if (BrowserColors)
  1099.       gDefaultBackgroundColor = BrowserColors.BackgroundColor;
  1100.   }
  1101.  
  1102.   // Trigger update on toolbar
  1103.   if (defTextColor != gDefaultTextColor)
  1104.   {
  1105.     goUpdateCommandState("cmd_fontColor");
  1106.     onFontColorChange();
  1107.   }
  1108.   if (defBackColor != gDefaultBackgroundColor)
  1109.   {
  1110.     goUpdateCommandState("cmd_backgroundColor");
  1111.     onBackgroundColorChange();
  1112.   }
  1113. }
  1114.  
  1115. function GetBackgroundElementWithColor()
  1116. {
  1117.   var editor = GetCurrentTableEditor();
  1118.   if (!editor)
  1119.     return null;
  1120.  
  1121.   gColorObj.Type = "";
  1122.   gColorObj.PageColor = "";
  1123.   gColorObj.TableColor = "";
  1124.   gColorObj.CellColor = "";
  1125.   gColorObj.BackgroundColor = "";
  1126.   gColorObj.SelectedType = "";
  1127.  
  1128.   var tagNameObj = { value: "" };
  1129.   var element;
  1130.   try {
  1131.     element = editor.getSelectedOrParentTableElement(tagNameObj, {value:0});
  1132.   }
  1133.   catch(e) {}
  1134.  
  1135.   if (element && tagNameObj && tagNameObj.value)
  1136.   {
  1137.     gColorObj.BackgroundColor = GetHTMLOrCSSStyleValue(element, "bgcolor", "background-color");
  1138.     gColorObj.BackgroundColor = ConvertRGBColorIntoHEXColor(gColorObj.BackgroundColor);
  1139.     if (tagNameObj.value.toLowerCase() == "td")
  1140.     {
  1141.       gColorObj.Type = "Cell";
  1142.       gColorObj.CellColor = gColorObj.BackgroundColor;
  1143.  
  1144.       // Get any color that might be on parent table
  1145.       var table = GetParentTable(element);
  1146.       gColorObj.TableColor = GetHTMLOrCSSStyleValue(table, "bgcolor", "background-color");
  1147.       gColorObj.TableColor = ConvertRGBColorIntoHEXColor(gColorObj.TableColor);
  1148.     }
  1149.     else
  1150.     {
  1151.       gColorObj.Type = "Table";
  1152.       gColorObj.TableColor = gColorObj.BackgroundColor;
  1153.     }
  1154.     gColorObj.SelectedType = gColorObj.Type;
  1155.   }
  1156.   else
  1157.   {
  1158.     var prefs = GetPrefs();
  1159.     var IsCSSPrefChecked = prefs.getBoolPref("editor.use_css");
  1160.     if (IsCSSPrefChecked && IsHTMLEditor())
  1161.     {
  1162.       var selection = editor.selection;
  1163.       if (selection)
  1164.       {
  1165.         element = selection.focusNode;
  1166.         while (!editor.nodeIsBlock(element))
  1167.           element = element.parentNode;
  1168.       }
  1169.       else
  1170.       {
  1171.         element = GetBodyElement();
  1172.       }
  1173.     }
  1174.     else
  1175.     {
  1176.       element = GetBodyElement();
  1177.     }
  1178.     if (element)
  1179.     {
  1180.       gColorObj.Type = "Page";
  1181.       gColorObj.BackgroundColor = GetHTMLOrCSSStyleValue(element, "bgcolor", "background-color");
  1182.       if (gColorObj.BackgroundColor == "")
  1183.       {
  1184.         gColorObj.BackgroundColor = "transparent";
  1185.       }
  1186.       else
  1187.       {
  1188.         gColorObj.BackgroundColor = ConvertRGBColorIntoHEXColor(gColorObj.BackgroundColor);
  1189.       }
  1190.       gColorObj.PageColor = gColorObj.BackgroundColor;
  1191.     }
  1192.   }
  1193.   return element;
  1194. }
  1195.  
  1196. function SetSmiley(smileyText)
  1197. {
  1198.   try {
  1199.     GetCurrentEditor().insertText(smileyText);
  1200.     gContentWindow.focus();
  1201.   }
  1202.   catch(e) {}
  1203. }
  1204.  
  1205. function EditorSelectColor(colorType, mouseEvent)
  1206. {
  1207.   var editor = GetCurrentEditor();
  1208.   if (!editor || !gColorObj)
  1209.     return;
  1210.  
  1211.   // Shift + mouse click automatically applies last color, if available
  1212.   var useLastColor = mouseEvent ? ( mouseEvent.button == 0 && mouseEvent.shiftKey ) : false;
  1213.   var element;
  1214.   var table;
  1215.   var currentColor = "";
  1216.   var commandNode;
  1217.  
  1218.   if (!colorType)
  1219.     colorType = "";
  1220.  
  1221.   if (colorType == "Text")
  1222.   {
  1223.     gColorObj.Type = colorType;
  1224.  
  1225.     // Get color from command node state
  1226.     commandNode = document.getElementById("cmd_fontColor");
  1227.     currentColor = commandNode.getAttribute("state");
  1228.     gColorObj.TextColor = currentColor;
  1229.  
  1230.     if (useLastColor && gColorObj.LastTextColor )
  1231.       gColorObj.TextColor = gColorObj.LastTextColor;
  1232.     else
  1233.       useLastColor = false;
  1234.   }
  1235.   else if (colorType == "Highlight")
  1236.   {
  1237.     gColorObj.Type = colorType;
  1238.  
  1239.     // Get color from command node state
  1240.     commandNode = document.getElementById("cmd_highlight");
  1241.     currentColor = commandNode.getAttribute("state");
  1242.     gColorObj.HighlightColor = currentColor;
  1243.  
  1244.     if (useLastColor && gColorObj.LastHighlightColor )
  1245.       gColorObj.HighlightColor = gColorObj.LastHighlightColor;
  1246.     else
  1247.       useLastColor = false;
  1248.   }
  1249.   else
  1250.   {
  1251.     element = GetBackgroundElementWithColor();
  1252.     if (!element)
  1253.       return;
  1254.  
  1255.     // Get the table if we found a cell
  1256.     if (gColorObj.Type == "Table")
  1257.       table = element;
  1258.     else if (gColorObj.Type == "Cell")
  1259.       table = GetParentTable(element);
  1260.  
  1261.     // Save to avoid resetting if not necessary
  1262.     currentColor = gColorObj.BackgroundColor;
  1263.  
  1264.     if (colorType == "TableOrCell" || colorType == "Cell")
  1265.     {
  1266.       if (gColorObj.Type == "Cell")
  1267.         gColorObj.Type = colorType;
  1268.       else if (gColorObj.Type != "Table")
  1269.         return;
  1270.     }
  1271.     else if (colorType == "Table" && gColorObj.Type == "Page")
  1272.       return;
  1273.  
  1274.     if (colorType == "" && gColorObj.Type == "Cell")
  1275.     {
  1276.       // Using empty string for requested type means
  1277.       //  we can let user select cell or table
  1278.       gColorObj.Type = "TableOrCell";
  1279.     }
  1280.  
  1281.     if (useLastColor && gColorObj.LastBackgroundColor )
  1282.       gColorObj.BackgroundColor = gColorObj.LastBackgroundColor;
  1283.     else
  1284.       useLastColor = false;
  1285.   }
  1286.   // Save the type we are really requesting
  1287.   colorType = gColorObj.Type;
  1288.  
  1289.   if (!useLastColor)
  1290.   {
  1291.     // Avoid the JS warning
  1292.     gColorObj.NoDefault = false;
  1293.  
  1294.     // Launch the ColorPicker dialog
  1295.     // TODO: Figure out how to position this under the color buttons on the toolbar
  1296.     window.openDialog("chrome://editor/content/EdColorPicker.xul", "_blank", "chrome,close,titlebar,modal", "", gColorObj);
  1297.  
  1298.     // User canceled the dialog
  1299.     if (gColorObj.Cancel)
  1300.       return;
  1301.   }
  1302.  
  1303.   if (gColorObj.Type == "Text")
  1304.   {
  1305.     if (currentColor != gColorObj.TextColor)
  1306.     {
  1307.       if (gColorObj.TextColor)
  1308.         EditorSetTextProperty("font", "color", gColorObj.TextColor);
  1309.       else
  1310.         EditorRemoveTextProperty("font", "color");
  1311.     }
  1312.     // Update the command state (this will trigger color button update)
  1313.     goUpdateCommandState("cmd_fontColor");
  1314.   }
  1315.   else if (gColorObj.Type == "Highlight")
  1316.   {
  1317.     if (currentColor != gColorObj.HighlightColor)
  1318.     {
  1319.       if (gColorObj.HighlightColor)
  1320.         EditorSetTextProperty("font", "bgcolor", gColorObj.HighlightColor);
  1321.       else
  1322.         EditorRemoveTextProperty("font", "bgcolor");
  1323.     }
  1324.     // Update the command state (this will trigger color button update)
  1325.     goUpdateCommandState("cmd_highlight");
  1326.   }
  1327.   else if (element)
  1328.   {
  1329.     if (gColorObj.Type == "Table")
  1330.     {
  1331.       // Set background on a table
  1332.       // Note that we shouldn't trust "currentColor" because of "TableOrCell" behavior
  1333.       if (table)
  1334.       {
  1335.         var bgcolor = table.getAttribute("bgcolor");
  1336.         if (bgcolor != gColorObj.BackgroundColor)
  1337.         try {
  1338.           if (gColorObj.BackgroundColor)
  1339.             editor.setAttributeOrEquivalent(table, "bgcolor", gColorObj.BackgroundColor, false);
  1340.           else
  1341.             editor.removeAttributeOrEquivalent(table, "bgcolor", false);
  1342.         } catch (e) {}
  1343.       }
  1344.     }
  1345.     else if (currentColor != gColorObj.BackgroundColor && IsHTMLEditor())
  1346.     {
  1347.       editor.beginTransaction();
  1348.       try
  1349.       {
  1350.         editor.setBackgroundColor(gColorObj.BackgroundColor);
  1351.  
  1352.         if (gColorObj.Type == "Page" && gColorObj.BackgroundColor)
  1353.         {
  1354.           // Set all page colors not explicitly set,
  1355.           //  else you can end up with unreadable pages
  1356.           //  because viewer's default colors may not be same as page author's
  1357.           var bodyelement = GetBodyElement();
  1358.           if (bodyelement)
  1359.           {
  1360.             var defColors = GetDefaultBrowserColors();
  1361.             if (defColors)
  1362.             {
  1363.               if (!bodyelement.getAttribute("text"))
  1364.                 editor.setAttributeOrEquivalent(bodyelement, "text", defColors.TextColor, false);
  1365.  
  1366.               // The following attributes have no individual CSS declaration counterparts
  1367.               // Getting rid of them in favor of CSS implies CSS rules management
  1368.               if (!bodyelement.getAttribute("link"))
  1369.                 editor.setAttribute(bodyelement, "link", defColors.LinkColor);
  1370.  
  1371.               if (!bodyelement.getAttribute("alink"))
  1372.                 editor.setAttribute(bodyelement, "alink", defColors.LinkColor);
  1373.  
  1374.               if (!bodyelement.getAttribute("vlink"))
  1375.                 editor.setAttribute(bodyelement, "vlink", defColors.VisitedLinkColor);
  1376.             }
  1377.           }
  1378.         }
  1379.       }
  1380.       catch(e) {}
  1381.  
  1382.       editor.endTransaction();
  1383.     }
  1384.  
  1385.     goUpdateCommandState("cmd_backgroundColor");
  1386.   }
  1387.   gContentWindow.focus();
  1388. }
  1389.  
  1390. function GetParentTable(element)
  1391. {
  1392.   var node = element;
  1393.   while (node)
  1394.   {
  1395.     if (node.nodeName.toLowerCase() == "table")
  1396.       return node;
  1397.  
  1398.     node = node.parentNode;
  1399.   }
  1400.   return node;
  1401. }
  1402.  
  1403. function GetParentTableCell(element)
  1404. {
  1405.   var node = element;
  1406.   while (node)
  1407.   {
  1408.     if (node.nodeName.toLowerCase() == "td" || node.nodeName.toLowerCase() == "th")
  1409.       return node;
  1410.  
  1411.     node = node.parentNode;
  1412.   }
  1413.   return node;
  1414. }
  1415.  
  1416. function EditorDblClick(event)
  1417. {
  1418.   // We check event.explicitOriginalTarget here because .target will never
  1419.   // be a textnode (bug 193689)
  1420.   if (event.explicitOriginalTarget)
  1421.   {
  1422.     // Only bring up properties if clicked on an element or selected link
  1423.     var element;
  1424.     try {
  1425.       element = event.explicitOriginalTarget.QueryInterface(
  1426.                     Components.interfaces.nsIDOMElement);
  1427.     } catch (e) {}
  1428.  
  1429.      //  We use "href" instead of "a" to not be fooled by named anchor
  1430.     if (!element)
  1431.       try {
  1432.         element = GetCurrentEditor().getSelectedElement("href");
  1433.       } catch (e) {}
  1434.  
  1435.     if (element)
  1436.     {
  1437.       goDoCommand("cmd_objectProperties");  
  1438.       event.preventDefault();
  1439.     }
  1440.   }
  1441. }
  1442.  
  1443. function EditorClick(event)
  1444. {
  1445.   if (!event)
  1446.     return;
  1447.  
  1448.   if (event.detail == 2)
  1449.   {
  1450.     EditorDblClick(event);
  1451.     return;
  1452.   }
  1453.  
  1454.   // For Web Composer: In Show All Tags Mode,
  1455.   // single click selects entire element,
  1456.   //  except for body and table elements
  1457.   if (IsWebComposer() && event.explicitOriginalTarget && IsHTMLEditor() &&
  1458.       gEditorDisplayMode == kDisplayModeAllTags)
  1459.   {
  1460.     try
  1461.     {
  1462.       // We check event.explicitOriginalTarget here because .target will never
  1463.       // be a textnode (bug 193689)
  1464.       var element = event.explicitOriginalTarget.QueryInterface(
  1465.                         Components.interfaces.nsIDOMElement);
  1466.       var name = element.localName.toLowerCase();
  1467.       if (name != "body" && name != "table" &&
  1468.           name != "td" && name != "th" && name != "caption" && name != "tr")
  1469.       {          
  1470.         GetCurrentEditor().selectElement(event.explicitOriginalTarget);
  1471.         event.preventDefault();
  1472.       }
  1473.     } catch (e) {}
  1474.   }
  1475. }
  1476.  
  1477. /*TODO: We need an oncreate hook to do enabling/disabling for the
  1478.         Format menu. There should be code like this for the
  1479.         object-specific "Properties" item
  1480. */
  1481. // For property dialogs, we want the selected element,
  1482. //  but will accept a parent link, list, or table cell if inside one
  1483. function GetObjectForProperties()
  1484. {
  1485.   var editor = GetCurrentEditor();
  1486.   if (!editor || !IsHTMLEditor())
  1487.     return null;
  1488.  
  1489.   var element;
  1490.   try {
  1491.     element = editor.getSelectedElement("");
  1492.   } catch (e) {}
  1493.   if (element)
  1494.     return element;
  1495.  
  1496.   // Find nearest parent of selection anchor node
  1497.   //   that is a link, list, table cell, or table
  1498.  
  1499.   var anchorNode
  1500.   var node;
  1501.   try {
  1502.     anchorNode = editor.selection.anchorNode;
  1503.     if (anchorNode.firstChild)
  1504.     {
  1505.       // Start at actual selected node
  1506.       var offset = editor.selection.anchorOffset;
  1507.       // Note: If collapsed, offset points to element AFTER caret,
  1508.       //  thus node may be null
  1509.       node = anchorNode.childNodes.item(offset);
  1510.     }
  1511.     if (!node)
  1512.       node = anchorNode;
  1513.   } catch (e) {}
  1514.  
  1515.   while (node)
  1516.   {
  1517.     if (node.nodeName)
  1518.     {
  1519.       var nodeName = node.nodeName.toLowerCase();
  1520.  
  1521.       // Done when we hit the body
  1522.       if (nodeName == "body") break;
  1523.  
  1524.       if ((nodeName == "a" && node.href) ||
  1525.           nodeName == "ol" || nodeName == "ul" || nodeName == "dl" ||
  1526.           nodeName == "td" || nodeName == "th" ||
  1527.           nodeName == "table")
  1528.       {
  1529.         return node;
  1530.       }
  1531.     }
  1532.     node = node.parentNode;
  1533.   }
  1534.   return null;
  1535. }
  1536.  
  1537. function SetEditMode(mode)
  1538. {
  1539.   if (!IsHTMLEditor())
  1540.     return;
  1541.  
  1542.   var bodyElement = GetBodyElement();
  1543.   if (!bodyElement)
  1544.   {
  1545.     dump("SetEditMode: We don't have a body node!\n");
  1546.     return;
  1547.   }
  1548.  
  1549.   // must have editor if here!
  1550.   var editor = GetCurrentEditor();
  1551.  
  1552.   // Switch the UI mode before inserting contents
  1553.   //   so user can't type in source window while new window is being filled
  1554.   var previousMode = gEditorDisplayMode;
  1555.   if (!SetDisplayMode(mode))
  1556.     return;
  1557.  
  1558.   if (mode == kDisplayModeSource)
  1559.   {
  1560.     // Display the DOCTYPE as a non-editable string above edit area
  1561.     var domdoc;
  1562.     try { domdoc = editor.document; } catch (e) { dump( e + "\n");}
  1563.     if (domdoc)
  1564.     {
  1565.       var doctypeNode = document.getElementById("doctype-text");
  1566.       var dt = domdoc.doctype;
  1567.       if (doctypeNode)
  1568.       {
  1569.         if (dt)
  1570.         {
  1571.           doctypeNode.collapsed = false;
  1572.           var doctypeText = "<!DOCTYPE " + domdoc.doctype.name;
  1573.           if (dt.publicId)
  1574.             doctypeText += " PUBLIC \"" + domdoc.doctype.publicId;
  1575.           if (dt.systemId)
  1576.             doctypeText += " "+"\"" + dt.systemId;
  1577.           doctypeText += "\">"
  1578.           doctypeNode.setAttribute("value", doctypeText);
  1579.         }
  1580.         else
  1581.           doctypeNode.collapsed = true;
  1582.       }
  1583.     }
  1584.     // Get the entire document's source string
  1585.  
  1586.     var flags = (editor.documentCharacterSet == "ISO-8859-1")
  1587.       ? 32768  // OutputEncodeLatin1Entities
  1588.       : 16384; // OutputEncodeBasicEntities
  1589.     try { 
  1590.       var encodeEntity = gPrefs.getCharPref("editor.encode_entity");
  1591.       switch (encodeEntity) {
  1592.         case "basic"  : flags = 16384; break; // OutputEncodeBasicEntities
  1593.         case "latin1" : flags = 32768; break; // OutputEncodeLatin1Entities
  1594.         case "html"   : flags = 65536; break; // OutputEncodeHTMLEntities
  1595.         case "none"   : flags = 0;     break;
  1596.       }
  1597.     } catch (e) { }
  1598.  
  1599.     try { 
  1600.       var prettyPrint = gPrefs.getBoolPref("editor.prettyprint");
  1601.       if (prettyPrint)
  1602.         flags |= 2; // OutputFormatted
  1603.  
  1604.     } catch (e) {}
  1605.  
  1606.     var source = editor.outputToString(kHTMLMimeType, flags);
  1607.     var start = source.search(/<html/i);
  1608.     if (start == -1) start = 0;
  1609.     gSourceContentWindow.value = source.slice(start);
  1610.     gSourceContentWindow.focus();
  1611.  
  1612.     // Add input handler so we know if user made any changes
  1613.     gSourceContentWindow.addEventListener("input", oninputHTMLSource, false);
  1614.     gHTMLSourceChanged = false;
  1615.   }
  1616.   else if (previousMode == kDisplayModeSource)
  1617.   {
  1618.     // Only rebuild document if a change was made in source window
  1619.     if (gHTMLSourceChanged)
  1620.     {
  1621.       // Reduce the undo count so we don't use too much memory
  1622.       //   during multiple uses of source window 
  1623.       //   (reinserting entire doc caches all nodes)
  1624.       try {
  1625.         editor.transactionManager.maxTransactionCount = 1;
  1626.       } catch (e) {}
  1627.  
  1628.       editor.beginTransaction();
  1629.       try {
  1630.         // We are comming from edit source mode,
  1631.         //   so transfer that back into the document
  1632.         source = gSourceContentWindow.value;
  1633.         editor.rebuildDocumentFromSource(source);
  1634.  
  1635.         // Get the text for the <title> from the newly-parsed document
  1636.         // (must do this for proper conversion of "escaped" characters)
  1637.         var title = "";
  1638.         var titlenodelist = editor.document.getElementsByTagName("title");
  1639.         if (titlenodelist)
  1640.         {
  1641.           var titleNode = titlenodelist.item(0);
  1642.           if (titleNode && titleNode.firstChild && titleNode.firstChild.data)
  1643.             title = titleNode.firstChild.data;
  1644.         }
  1645.         if (editor.document.title != title)
  1646.           SetDocumentTitle(title);
  1647.  
  1648.       } catch (ex) {
  1649.         dump(ex);
  1650.       }
  1651.       editor.endTransaction();
  1652.  
  1653.       // Restore unlimited undo count
  1654.       try {
  1655.         editor.transactionManager.maxTransactionCount = -1;
  1656.       } catch (e) {}
  1657.     } else {
  1658.       // We don't need to call this again, so remove handler
  1659.       gSourceContentWindow.removeEventListener("input", oninputHTMLSource, false);
  1660.     }
  1661.     gHTMLSourceChanged = false;
  1662.  
  1663.     // Clear out the string buffers
  1664.     gSourceContentWindow.value = null;
  1665.  
  1666.     gContentWindow.focus();
  1667.   }
  1668. }
  1669.  
  1670. function oninputHTMLSource()
  1671. {
  1672.   gHTMLSourceChanged = true;
  1673.  
  1674.   // Trigger update of "Save" and "Publish" buttons
  1675.   goUpdateCommand("cmd_save");
  1676.   goUpdateCommand("cmd_publish");
  1677.  
  1678.   // We don't need to call this again, so remove handler
  1679.   gSourceContentWindow.removeEventListener("input", oninputHTMLSource, false);
  1680. }
  1681.  
  1682. function CancelHTMLSource()
  1683. {
  1684.   // Don't convert source text back into the DOM document
  1685.   gSourceContentWindow.value = "";
  1686.   gHTMLSourceChanged = false;
  1687.   SetDisplayMode(gPreviousNonSourceDisplayMode);
  1688. }
  1689.  
  1690. function FinishHTMLSource()
  1691. {
  1692.   //Here we need to check whether the HTML source contains <head> and <body> tags
  1693.   //Or RebuildDocumentFromSource() will fail.
  1694.   if (IsInHTMLSourceMode())
  1695.   {
  1696.     var htmlSource = gSourceContentWindow.value;
  1697.     if (htmlSource.length > 0)
  1698.     {
  1699.       var beginHead = htmlSource.indexOf("<head");
  1700.       if (beginHead == -1)
  1701.       {
  1702.         AlertWithTitle(GetString("Alert"), GetString("NoHeadTag"));
  1703.         //cheat to force back to Source Mode
  1704.         gEditorDisplayMode = kDisplayModePreview;
  1705.         SetDisplayMode(kDisplayModeSource);
  1706.         throw Components.results.NS_ERROR_FAILURE;
  1707.       }
  1708.  
  1709.       var beginBody = htmlSource.indexOf("<body");
  1710.       if (beginBody == -1)
  1711.       {
  1712.         AlertWithTitle(GetString("Alert"), GetString("NoBodyTag"));
  1713.         //cheat to force back to Source Mode
  1714.         gEditorDisplayMode = kDisplayModePreview;
  1715.         SetDisplayMode(kDisplayModeSource);
  1716.         throw Components.results.NS_ERROR_FAILURE;
  1717.       }
  1718.     }
  1719.   }
  1720.  
  1721.   // Switch edit modes -- converts source back into DOM document
  1722.   SetEditMode(gPreviousNonSourceDisplayMode);
  1723. }
  1724.  
  1725. function SetDisplayMode(mode)
  1726. {
  1727.   if (!IsHTMLEditor())
  1728.     return false;
  1729.  
  1730.   // Already in requested mode:
  1731.   //  return false to indicate we didn't switch
  1732.   if (mode == gEditorDisplayMode)
  1733.     return false;
  1734.  
  1735.   var previousMode = gEditorDisplayMode;
  1736.   gEditorDisplayMode = mode;
  1737.  
  1738.   ResetStructToolbar();
  1739.   if (mode == kDisplayModeSource)
  1740.   {
  1741.     // Switch to the sourceWindow (second in the deck)
  1742.     gContentWindowDeck.selectedIndex = 1;
  1743.  
  1744.     //Hide the formatting toolbar if not already hidden
  1745.     gFormatToolbarHidden = gFormatToolbar.hidden;
  1746.     gFormatToolbar.hidden = true;
  1747.     gViewFormatToolbar.hidden = true;
  1748.  
  1749.     gSourceContentWindow.focus();
  1750.   }
  1751.   else
  1752.   {
  1753.     // Save the last non-source mode so we can cancel source editing easily
  1754.     gPreviousNonSourceDisplayMode = mode;
  1755.  
  1756.     // Load/unload appropriate override style sheet
  1757.     try {
  1758.       var editor = GetCurrentEditor();
  1759.       editor.QueryInterface(nsIEditorStyleSheets);
  1760.  
  1761.       switch (mode)
  1762.       {
  1763.         case kDisplayModePreview:
  1764.           // Disable all extra "edit mode" style sheets 
  1765.           editor.enableStyleSheet(kNormalStyleSheet, false);
  1766.           editor.enableStyleSheet(kAllTagsStyleSheet, false);
  1767.           break;
  1768.  
  1769.         case kDisplayModeNormal:
  1770.           editor.addOverrideStyleSheet(kNormalStyleSheet);
  1771.           // Disable ShowAllTags mode
  1772.           editor.enableStyleSheet(kAllTagsStyleSheet, false);
  1773.           break;
  1774.  
  1775.         case kDisplayModeAllTags:
  1776.           editor.addOverrideStyleSheet(kNormalStyleSheet);
  1777.           editor.addOverrideStyleSheet(kAllTagsStyleSheet);
  1778.           break;
  1779.       }
  1780.     } catch(e) {}
  1781.  
  1782.     // Switch to the normal editor (first in the deck)
  1783.     gContentWindowDeck.selectedIndex = 0;
  1784.  
  1785.     // Restore menus and toolbars
  1786.     gFormatToolbar.hidden = gFormatToolbarHidden;
  1787.     gViewFormatToolbar.hidden = false;
  1788.  
  1789.     gContentWindow.focus();
  1790.   }
  1791.  
  1792.   // update commands to disable or re-enable stuff
  1793.   window.updateCommands("mode_switch");
  1794.  
  1795.   // Set the selected tab at bottom of window:
  1796.   // (Note: Setting "selectedIndex = mode" won't redraw tabs when menu is used.)
  1797.   document.getElementById("EditModeTabs").selectedItem = document.getElementById(kDisplayModeTabIDS[mode]);
  1798.  
  1799.   // Uncheck previous menuitem and set new check since toolbar may have been used
  1800.   if (previousMode >= 0)
  1801.     document.getElementById(kDisplayModeMenuIDs[previousMode]).setAttribute("checked","false");
  1802.   document.getElementById(kDisplayModeMenuIDs[mode]).setAttribute("checked","true");
  1803.   
  1804.  
  1805.   return true;
  1806. }
  1807.  
  1808. function EditorToggleParagraphMarks()
  1809. {
  1810.   var menuItem = document.getElementById("viewParagraphMarks");
  1811.   if (menuItem)
  1812.   {
  1813.     // Note that the 'type="checbox"' mechanism automatically
  1814.     //  toggles the "checked" state before the oncommand is called,
  1815.     //  so if "checked" is true now, it was just switched to that mode
  1816.     var checked = menuItem.getAttribute("checked");
  1817.     try {
  1818.       var editor = GetCurrentEditor();
  1819.       editor.QueryInterface(nsIEditorStyleSheets);
  1820.  
  1821.       if (checked == "true")
  1822.         editor.addOverrideStyleSheet(kParagraphMarksStyleSheet);
  1823.       else
  1824.         editor.enableStyleSheet(kParagraphMarksStyleSheet, false);
  1825.     }
  1826.     catch(e) { return; }
  1827.   }
  1828. }
  1829.  
  1830. function InitPasteAsMenu()
  1831. {
  1832.   var menuItem = document.getElementById("menu_pasteTable")
  1833.   if(menuItem)
  1834.   {
  1835.     menuItem.IsInTable  
  1836.     menuItem.setAttribute("label", GetString(IsInTable() ? "NestedTable" : "Table"));
  1837.    // menuItem.setAttribute("accesskey",GetString("ObjectPropertiesAccessKey"));
  1838.   }
  1839.   // TODO: Do enabling based on what is in the clipboard
  1840. }
  1841.  
  1842. function UpdateWindowTitle()
  1843. {
  1844.   try {
  1845.     var windowTitle = GetDocumentTitle();
  1846.     if (!windowTitle)
  1847.       windowTitle = GetString("untitled");
  1848.  
  1849.     // Append just the 'leaf' filename to the Doc. Title for the window caption
  1850.     var docUrl = GetDocumentUrl();
  1851.     if (docUrl && !IsUrlAboutBlank(docUrl))
  1852.     {
  1853.       var scheme = GetScheme(docUrl);
  1854.       var filename = GetFilename(docUrl);
  1855.       if (filename)
  1856.         windowTitle += " [" + scheme + ":/.../" + filename + "]";
  1857.  
  1858.       // Save changed title in the recent pages data in prefs
  1859.       SaveRecentFilesPrefs();
  1860.     }
  1861.     // Set window title with " - Composer" appended
  1862.     xulWin = document.documentElement;
  1863.     window.title = windowTitle + xulWin.getAttribute("titlemenuseparator") + 
  1864.                    xulWin.getAttribute("titlemodifier");
  1865.   } catch (e) { dump(e); }
  1866. }
  1867.  
  1868. function BuildRecentPagesMenu()
  1869. {
  1870.   var editor = GetCurrentEditor();
  1871.   if (!editor || !gPrefs)
  1872.     return;
  1873.  
  1874.   var popup = document.getElementById("menupopup_RecentFiles");
  1875.   if (!popup || !editor.document)
  1876.     return;
  1877.  
  1878.   // Delete existing menu
  1879.   while (popup.firstChild)
  1880.     popup.removeChild(popup.firstChild);
  1881.  
  1882.   // Current page is the "0" item in the list we save in prefs,
  1883.   //  but we don't include it in the menu.
  1884.   var curUrl = StripPassword(GetDocumentUrl());
  1885.   var historyCount = 10;
  1886.   try {
  1887.     historyCount = gPrefs.getIntPref("editor.history.url_maximum");
  1888.   } catch(e) {}
  1889.   var menuIndex = 1;
  1890.  
  1891.   for (var i = 0; i < historyCount; i++)
  1892.   {
  1893.     var url = GetUnicharPref("editor.history_url_"+i);
  1894.  
  1895.     // Skip over current url
  1896.     if (url && url != curUrl)
  1897.     {
  1898.       // Build the menu
  1899.       var title = GetUnicharPref("editor.history_title_"+i);
  1900.       AppendRecentMenuitem(popup, title, url, menuIndex);
  1901.       menuIndex++;
  1902.     }
  1903.   }
  1904. }
  1905.  
  1906. function SaveRecentFilesPrefs()
  1907. {
  1908.   // Can't do anything if no prefs
  1909.   if (!gPrefs) return;
  1910.  
  1911.   var curUrl = StripPassword(GetDocumentUrl());
  1912.   var historyCount = 10;
  1913.   try {
  1914.     historyCount = gPrefs.getIntPref("editor.history.url_maximum"); 
  1915.   } catch(e) {}
  1916.  
  1917.   var titleArray = [];
  1918.   var urlArray = [];
  1919.  
  1920.   if (historyCount && !IsUrlAboutBlank(curUrl) &&  GetScheme(curUrl) != "data")
  1921.   {
  1922.     titleArray.push(GetDocumentTitle());
  1923.     urlArray.push(curUrl);
  1924.   }
  1925.  
  1926.   for (var i = 0; i < historyCount && urlArray.length < historyCount; i++)
  1927.   {
  1928.     var url = GetUnicharPref("editor.history_url_"+i);
  1929.  
  1930.     // Continue if URL pref is missing because 
  1931.     //  a URL not found during loading may have been removed
  1932.  
  1933.     // Skip over current an "data" URLs
  1934.     if (url && url != curUrl && GetScheme(url) != "data")
  1935.     {
  1936.       var title = GetUnicharPref("editor.history_title_"+i);
  1937.       titleArray.push(title);
  1938.       urlArray.push(url);
  1939.     }
  1940.   }
  1941.  
  1942.   // Resave the list back to prefs in the new order
  1943.   for (i = 0; i < urlArray.length; i++)
  1944.   {
  1945.     SetUnicharPref("editor.history_title_"+i, titleArray[i]);
  1946.     SetUnicharPref("editor.history_url_"+i, urlArray[i]);
  1947.   }
  1948. }
  1949.  
  1950. function AppendRecentMenuitem(menupopup, title, url, menuIndex)
  1951. {
  1952.   if (menupopup)
  1953.   {
  1954.     var menuItem = document.createElementNS(XUL_NS, "menuitem");
  1955.     if (menuItem)
  1956.     {
  1957.       var accessKey;
  1958.       if (menuIndex <= 9)
  1959.         accessKey = String(menuIndex);
  1960.       else if (menuIndex == 10)
  1961.         accessKey = "0";
  1962.       else
  1963.         accessKey = " ";
  1964.  
  1965.       var itemString = accessKey+" ";
  1966.  
  1967.       // Show "title [url]" or just the URL
  1968.       if (title)
  1969.       {
  1970.        itemString += title;
  1971.        itemString += " [";
  1972.       }
  1973.       itemString += url;
  1974.       if (title)
  1975.         itemString += "]";
  1976.  
  1977.       menuItem.setAttribute("label", itemString);
  1978.       menuItem.setAttribute("value", url);
  1979.       if (accessKey != " ")
  1980.         menuItem.setAttribute("accesskey", accessKey);
  1981.       menupopup.appendChild(menuItem);
  1982.     }
  1983.   }
  1984. }
  1985.  
  1986. function EditorInitFileMenu()
  1987. {
  1988.   // Disable "Save" menuitem when editing remote url. User should use "Save As"
  1989.   var docUrl = GetDocumentUrl();
  1990.   var scheme = GetScheme(docUrl);
  1991.   if (scheme && scheme != "file")
  1992.     SetElementEnabledById("saveMenuitem", false);
  1993.  
  1994.   // Enable recent pages submenu if there are any history entries in prefs
  1995.   var historyUrl = "";
  1996.  
  1997.   var historyCount = 10;
  1998.   try { historyCount = gPrefs.getIntPref("editor.history.url_maximum"); } catch(e) {}
  1999.   if (historyCount)
  2000.   {
  2001.     historyUrl = GetUnicharPref("editor.history_url_0");
  2002.     
  2003.     // See if there's more if current file is only entry in history list
  2004.     if (historyUrl && historyUrl == docUrl)
  2005.       historyUrl = GetUnicharPref("editor.history_url_1");
  2006.   }
  2007.   SetElementEnabledById("menu_RecentFiles", historyUrl != "");
  2008. }
  2009.  
  2010. function EditorInitFormatMenu()
  2011. {
  2012.   try {
  2013.     InitObjectPropertiesMenuitem("objectProperties");
  2014.     InitRemoveStylesMenuitems("removeStylesMenuitem", "removeLinksMenuitem", "removeNamedAnchorsMenuitem");
  2015.   } catch(ex) {}
  2016. }
  2017.  
  2018. function InitObjectPropertiesMenuitem(id)
  2019. {
  2020.   // Set strings and enable for the [Object] Properties item
  2021.   // Note that we directly do the enabling instead of
  2022.   //  using goSetCommandEnabled since we already have the menuitem
  2023.   var menuItem = document.getElementById(id);
  2024.   if (!menuItem) return null;
  2025.  
  2026.   var element;
  2027.   var menuStr = GetString("AdvancedProperties");
  2028.   var name;
  2029.  
  2030.   if (IsEditingRenderedHTML())
  2031.     element = GetObjectForProperties();
  2032.  
  2033.   if (element && element.nodeName)
  2034.   {
  2035.     var objStr = "";
  2036.     menuItem.setAttribute("disabled", "");
  2037.     name = element.nodeName.toLowerCase();
  2038.     switch (name)
  2039.     {
  2040.       case "img":
  2041.         // Check if img is enclosed in link
  2042.         //  (use "href" to not be fooled by named anchor)
  2043.         try
  2044.         {
  2045.           if (GetCurrentEditor().getElementOrParentByTagName("href", element))
  2046.             objStr = GetString("ImageAndLink");
  2047.         } catch(e) {}
  2048.         
  2049.         if (objStr == "")
  2050.           objStr = GetString("Image");
  2051.         break;
  2052.       case "hr":
  2053.         objStr = GetString("HLine");
  2054.         break;
  2055.       case "table":
  2056.         objStr = GetString("Table");
  2057.         break;
  2058.       case "th":
  2059.         name = "td";
  2060.       case "td":
  2061.         objStr = GetString("TableCell");
  2062.         break;
  2063.       case "ol":
  2064.       case "ul":
  2065.       case "dl":
  2066.         objStr = GetString("List");
  2067.         break;
  2068.       case "li":
  2069.         objStr = GetString("ListItem");
  2070.         break;
  2071.       case "form":
  2072.         objStr = GetString("Form");
  2073.         break;
  2074.       case "input":
  2075.         var type = element.getAttribute("type");
  2076.         if (type && type.toLowerCase() == "image")
  2077.           objStr = GetString("InputImage");
  2078.         else
  2079.           objStr = GetString("InputTag");
  2080.         break;
  2081.       case "textarea":
  2082.         objStr = GetString("TextArea");
  2083.         break;
  2084.       case "select":
  2085.         objStr = GetString("Select");
  2086.         break;
  2087.       case "button":
  2088.         objStr = GetString("Button");
  2089.         break;
  2090.       case "label":
  2091.         objStr = GetString("Label");
  2092.         break;
  2093.       case "fieldset":
  2094.         objStr = GetString("FieldSet");
  2095.         break;
  2096.       case "a":
  2097.         if (element.name)
  2098.         {
  2099.           objStr = GetString("NamedAnchor");
  2100.           name = "anchor";
  2101.         }
  2102.         else if(element.href)
  2103.         {
  2104.           objStr = GetString("Link");
  2105.           name = "href";
  2106.         }
  2107.         break;
  2108.     }
  2109.     if (objStr)
  2110.       menuStr = GetString("ObjectProperties").replace(/%obj%/,objStr);
  2111.   }
  2112.   else
  2113.   {
  2114.     // We show generic "Properties" string, but disable menu item
  2115.     menuItem.setAttribute("disabled","true");
  2116.   }
  2117.   menuItem.setAttribute("label", menuStr);
  2118.   menuItem.setAttribute("accesskey",GetString("ObjectPropertiesAccessKey"));
  2119.   return name;
  2120. }
  2121.  
  2122. function InitParagraphMenu()
  2123. {
  2124.   var mixedObj = { value: null };
  2125.   var state;
  2126.   try {
  2127.     state = GetCurrentEditor().getParagraphState(mixedObj);
  2128.   }
  2129.   catch(e) {}
  2130.   var IDSuffix;
  2131.  
  2132.   // PROBLEM: When we get blockquote, it masks other styles contained by it
  2133.   // We need a separate method to get blockquote state
  2134.  
  2135.   // We use "x" as uninitialized paragraph state
  2136.   if (!state || state == "x")
  2137.     IDSuffix = "bodyText" // No paragraph container
  2138.   else
  2139.     IDSuffix = state;
  2140.  
  2141.   // Set "radio" check on one item, but...
  2142.   var menuItem = document.getElementById("menu_"+IDSuffix);
  2143.   menuItem.setAttribute("checked", "true");
  2144.  
  2145.   // ..."bodyText" is returned if mixed selection, so remove checkmark
  2146.   if (mixedObj.value)
  2147.     menuItem.setAttribute("checked", "false");
  2148. }
  2149.  
  2150. function GetListStateString()
  2151. {
  2152.   try {
  2153.     var editor = GetCurrentEditor();
  2154.  
  2155.     var mixedObj = { value: null };
  2156.     var hasOL = { value: false };
  2157.     var hasUL = { value: false };
  2158.     var hasDL = { value: false };
  2159.     editor.getListState(mixedObj, hasOL, hasUL, hasDL);
  2160.  
  2161.     if (mixedObj.value)
  2162.       return "mixed";
  2163.     if (hasOL.value)
  2164.       return "ol";
  2165.     if (hasUL.value)
  2166.       return "ul";
  2167.  
  2168.     if (hasDL.value)
  2169.     {
  2170.       var hasLI = { value: false };
  2171.       var hasDT = { value: false };
  2172.       var hasDD = { value: false };
  2173.       editor.getListItemState(mixedObj, hasLI, hasDT, hasDD);
  2174.       if (mixedObj.value)
  2175.         return "mixed";
  2176.       if (hasLI.value)
  2177.         return "li";
  2178.       if (hasDT.value)
  2179.         return "dt";
  2180.       if (hasDD.value)
  2181.         return "dd";
  2182.     }
  2183.   } catch (e) {}
  2184.  
  2185.   // return "noList" if we aren't in a list at all
  2186.   return "noList";
  2187. }
  2188.  
  2189. function InitListMenu()
  2190. {
  2191.   if (!IsHTMLEditor())
  2192.     return;
  2193.  
  2194.   var IDSuffix = GetListStateString();
  2195.  
  2196.   // Set enable state for the "None" menuitem
  2197.   goSetCommandEnabled("cmd_removeList", IDSuffix != "noList");
  2198.  
  2199.   // Set "radio" check on one item, but...
  2200.   // we won't find a match if it's "mixed"
  2201.   var menuItem = document.getElementById("menu_"+IDSuffix);
  2202.   if (menuItem)
  2203.     menuItem.setAttribute("checked", "true");
  2204. }
  2205.  
  2206. function GetAlignmentString()
  2207. {
  2208.   var mixedObj = { value: null };
  2209.   var alignObj = { value: null };
  2210.   try {
  2211.     GetCurrentEditor().getAlignment(mixedObj, alignObj);
  2212.   } catch (e) {}
  2213.  
  2214.   if (mixedObj.value)
  2215.     return "mixed";
  2216.   if (alignObj.value == nsIHTMLEditor.eLeft)
  2217.     return "left";
  2218.   if (alignObj.value == nsIHTMLEditor.eCenter)
  2219.     return "center";
  2220.   if (alignObj.value == nsIHTMLEditor.eRight)
  2221.     return "right";
  2222.   if (alignObj.value == nsIHTMLEditor.eJustify)
  2223.     return "justify";
  2224.  
  2225.   // return "left" if we got here
  2226.   return "left";
  2227. }
  2228.  
  2229. function InitAlignMenu()
  2230. {
  2231.   if (!IsHTMLEditor())
  2232.     return;
  2233.  
  2234.   var IDSuffix = GetAlignmentString();
  2235.  
  2236.   // we won't find a match if it's "mixed"
  2237.   var menuItem = document.getElementById("menu_"+IDSuffix);
  2238.   if (menuItem)
  2239.     menuItem.setAttribute("checked", "true");
  2240. }
  2241.  
  2242. function EditorSetDefaultPrefsAndDoctype()
  2243. {
  2244.   var editor = GetCurrentEditor();
  2245.  
  2246.   var domdoc;
  2247.   try { 
  2248.     domdoc = editor.document;
  2249.   } catch (e) { dump( e + "\n"); }
  2250.   if ( !domdoc )
  2251.   {
  2252.     dump("EditorSetDefaultPrefsAndDoctype: EDITOR DOCUMENT NOT FOUND\n");
  2253.     return;
  2254.   }
  2255.  
  2256.   // Insert a doctype element 
  2257.   // if it is missing from existing doc
  2258.   if (!domdoc.doctype)
  2259.   {
  2260.     var newdoctype = domdoc.implementation.createDocumentType("html", "-//W3C//DTD HTML 4.01 Transitional//EN","");
  2261.     if (newdoctype)
  2262.       domdoc.insertBefore(newdoctype, domdoc.firstChild);
  2263.   }
  2264.   
  2265.   // search for head; we'll need this for meta tag additions
  2266.   var headelement = 0;
  2267.   var headnodelist = domdoc.getElementsByTagName("head");
  2268.   if (headnodelist)
  2269.   {
  2270.     var sz = headnodelist.length;
  2271.     if ( sz >= 1 )
  2272.       headelement = headnodelist.item(0);
  2273.   }
  2274.   else
  2275.   {
  2276.     headelement = domdoc.createElement("head");
  2277.     if (headelement)
  2278.       domdoc.insertAfter(headelement, domdoc.firstChild);
  2279.   }
  2280.  
  2281.   /* only set default prefs for new documents */
  2282.   if (!IsUrlAboutBlank(GetDocumentUrl()))
  2283.     return;
  2284.  
  2285.   // search for author meta tag.
  2286.   // if one is found, don't do anything.
  2287.   // if not, create one and make it a child of the head tag
  2288.   //   and set its content attribute to the value of the editor.author preference.
  2289.  
  2290.   var nodelist = domdoc.getElementsByTagName("meta");
  2291.   if ( nodelist )
  2292.   {
  2293.     // we should do charset first since we need to have charset before
  2294.     // hitting other 8-bit char in other meta tags
  2295.     // grab charset pref and make it the default charset
  2296.     var element;
  2297.     var prefCharsetString = 0;
  2298.     try
  2299.     {
  2300.       prefCharsetString = gPrefs.getComplexValue("intl.charset.default",
  2301.                                                  Components.interfaces.nsIPrefLocalizedString).data;
  2302.     }
  2303.     catch (ex) {}
  2304.     if ( prefCharsetString && prefCharsetString != 0)
  2305.     {
  2306.         element = domdoc.createElement("meta");
  2307.         if ( element )
  2308.         {
  2309.           element.setAttribute("http-equiv", "content-type");
  2310.           element.setAttribute("content", "text/html; charset=" + prefCharsetString);
  2311.           headelement.insertBefore( element, headelement.firstChild );
  2312.         }
  2313.     }
  2314.  
  2315.     var node = 0;
  2316.     var listlength = nodelist.length;
  2317.  
  2318.     // let's start by assuming we have an author in case we don't have the pref
  2319.     var authorFound = false;
  2320.     for (var i = 0; i < listlength && !authorFound; i++)
  2321.     {
  2322.       node = nodelist.item(i);
  2323.       if ( node )
  2324.       {
  2325.         var value = node.getAttribute("name");
  2326.         if (value && value.toLowerCase() == "author")
  2327.         {
  2328.           authorFound = true;
  2329.         }
  2330.       }
  2331.     }
  2332.  
  2333.     var prefAuthorString = 0;
  2334.     try
  2335.     {
  2336.       prefAuthorString = gPrefs.getComplexValue("editor.author",
  2337.                                                 Components.interfaces.nsISupportsString).data;
  2338.     }
  2339.     catch (ex) {}
  2340.     if ( prefAuthorString && prefAuthorString != 0)
  2341.     {
  2342.       if ( !authorFound && headelement)
  2343.       {
  2344.         /* create meta tag with 2 attributes */
  2345.         element = domdoc.createElement("meta");
  2346.         if ( element )
  2347.         {
  2348.           element.setAttribute("name", "author");
  2349.           element.setAttribute("content", prefAuthorString);
  2350.           headelement.appendChild( element );
  2351.         }
  2352.       }
  2353.     }
  2354.   }
  2355.  
  2356.   // add title tag if not present
  2357.   var titlenodelist = editor.document.getElementsByTagName("title");
  2358.   if (headelement && titlenodelist && titlenodelist.length == 0)
  2359.   {
  2360.      titleElement = domdoc.createElement("title");
  2361.      if (titleElement)
  2362.        headelement.appendChild(titleElement);
  2363.   }
  2364.  
  2365.   // Get editor color prefs
  2366.   var use_custom_colors = false;
  2367.   try {
  2368.     use_custom_colors = gPrefs.getBoolPref("editor.use_custom_colors");
  2369.   }
  2370.   catch (ex) {}
  2371.  
  2372.   // find body node
  2373.   var bodyelement = GetBodyElement();
  2374.   if (bodyelement)
  2375.   {
  2376.     if ( use_custom_colors )
  2377.     {
  2378.       // try to get the default color values.  ignore them if we don't have them.
  2379.       var text_color;
  2380.       var link_color;
  2381.       var active_link_color;
  2382.       var followed_link_color;
  2383.       var background_color;
  2384.  
  2385.       try { text_color = gPrefs.getCharPref("editor.text_color"); } catch (e) {}
  2386.       try { link_color = gPrefs.getCharPref("editor.link_color"); } catch (e) {}
  2387.       try { active_link_color = gPrefs.getCharPref("editor.active_link_color"); } catch (e) {}
  2388.       try { followed_link_color = gPrefs.getCharPref("editor.followed_link_color"); } catch (e) {}
  2389.       try { background_color = gPrefs.getCharPref("editor.background_color"); } catch(e) {}
  2390.  
  2391.       // add the color attributes to the body tag.
  2392.       // and use them for the default text and background colors if not empty
  2393.       try {
  2394.         if (text_color)
  2395.         {
  2396.           editor.setAttributeOrEquivalent(bodyelement, "text", text_color, true);
  2397.           gDefaultTextColor = text_color;
  2398.         }
  2399.         if (background_color)
  2400.         {
  2401.           editor.setAttributeOrEquivalent(bodyelement, "bgcolor", background_color, true);
  2402.           gDefaultBackgroundColor = background_color
  2403.         }
  2404.  
  2405.         if (link_color)
  2406.           bodyelement.setAttribute("link", link_color);
  2407.         if (active_link_color)
  2408.           bodyelement.setAttribute("alink", active_link_color);
  2409.         if (followed_link_color)
  2410.           bodyelement.setAttribute("vlink", followed_link_color);
  2411.       } catch (e) {}
  2412.     }
  2413.     // Default image is independent of Custom colors???
  2414.     try {
  2415.       var background_image = gPrefs.getCharPref("editor.default_background_image");
  2416.       editor.setAttributeOrEquivalent(bodyelement, "background", background_image, true);
  2417.     } catch (e) {dump("BACKGROUND EXCEPTION: "+e+"\n"); }
  2418.  
  2419.   }
  2420.   // auto-save???
  2421. }
  2422.  
  2423. function GetBodyElement()
  2424. {
  2425.   try {
  2426.     return GetCurrentEditor().rootElement;
  2427.   }
  2428.   catch (ex) {
  2429.     dump("no body tag found?!\n");
  2430.     //  better have one, how can we blow things up here?
  2431.   }
  2432.   return null;
  2433. }
  2434.  
  2435. // --------------------------- Logging stuff ---------------------------
  2436.  
  2437. function EditorGetNodeFromOffsets(offsets)
  2438. {
  2439.   var node = null;
  2440.   try {
  2441.     node = GetCurrentEditor().document;
  2442.  
  2443.     for (var i = 0; i < offsets.length; i++)
  2444.       node = node.childNodes[offsets[i]];
  2445.   } catch (e) {}
  2446.   return node;
  2447. }
  2448.  
  2449. function EditorSetSelectionFromOffsets(selRanges)
  2450. {
  2451.   try {
  2452.     var editor = GetCurrentEditor();
  2453.     var selection = editor.selection;
  2454.     selection.removeAllRanges();
  2455.  
  2456.     var rangeArr, start, end, node, offset;
  2457.     for (var i = 0; i < selRanges.length; i++)
  2458.     {
  2459.       rangeArr = selRanges[i];
  2460.       start    = rangeArr[0];
  2461.       end      = rangeArr[1];
  2462.  
  2463.       var range = editor.document.createRange();
  2464.  
  2465.       node   = EditorGetNodeFromOffsets(start[0]);
  2466.       offset = start[1];
  2467.  
  2468.       range.setStart(node, offset);
  2469.  
  2470.       node   = EditorGetNodeFromOffsets(end[0]);
  2471.       offset = end[1];
  2472.  
  2473.       range.setEnd(node, offset);
  2474.  
  2475.       selection.addRange(range);
  2476.     }
  2477.   } catch (e) {}
  2478. }
  2479.  
  2480. //--------------------------------------------------------------------
  2481. function initFontStyleMenu(menuPopup)
  2482. {
  2483.   for (var i = 0; i < menuPopup.childNodes.length; i++)
  2484.   {
  2485.     var menuItem = menuPopup.childNodes[i];
  2486.     var theStyle = menuItem.getAttribute("state");
  2487.     if (theStyle)
  2488.     {
  2489.       menuItem.setAttribute("checked", theStyle);
  2490.     }
  2491.   }
  2492. }
  2493.  
  2494. //--------------------------------------------------------------------
  2495. function onButtonUpdate(button, commmandID)
  2496. {
  2497.   var commandNode = document.getElementById(commmandID);
  2498.   var state = commandNode.getAttribute("state");
  2499.   button.checked = state == "true";
  2500. }
  2501.  
  2502. //--------------------------------------------------------------------
  2503. function onStateButtonUpdate(button, commmandID, onState)
  2504. {
  2505.   var commandNode = document.getElementById(commmandID);
  2506.   var state = commandNode.getAttribute("state");
  2507.  
  2508.   button.checked = state == onState;
  2509. }
  2510.  
  2511. // --------------------------- Status calls ---------------------------
  2512. function getColorAndSetColorWell(ColorPickerID, ColorWellID)
  2513. {
  2514.   var colorWell;
  2515.   if (ColorWellID)
  2516.     colorWell = document.getElementById(ColorWellID);
  2517.  
  2518.   var colorPicker = document.getElementById(ColorPickerID);
  2519.   if (colorPicker)
  2520.   {
  2521.     // Extract color from colorPicker and assign to colorWell.
  2522.     var color = colorPicker.getAttribute("color");
  2523.  
  2524.     if (colorWell && color)
  2525.     {
  2526.       // Use setAttribute so colorwell can be a XUL element, such as button
  2527.       colorWell.setAttribute("style", "background-color: " + color);
  2528.     }
  2529.   }
  2530.   return color;
  2531. }
  2532.  
  2533. //-----------------------------------------------------------------------------------
  2534. function IsSpellCheckerInstalled()
  2535. {
  2536.   return "@mozilla.org/spellchecker;1" in Components.classes;
  2537. }
  2538.  
  2539. //-----------------------------------------------------------------------------------
  2540. function IsFindInstalled()
  2541. {
  2542.   return "@mozilla.org/embedcomp/rangefind;1" in Components.classes
  2543.           && "@mozilla.org/find/find_service;1" in Components.classes;
  2544. }
  2545.  
  2546. //-----------------------------------------------------------------------------------
  2547. function RemoveInapplicableUIElements()
  2548. {
  2549.   // For items that are in their own menu block, remove associated separator
  2550.   // (we can't use "hidden" since class="hide-in-IM" CSS rule interferes)
  2551.  
  2552.    // if no find, remove find ui
  2553.   if (!IsFindInstalled())
  2554.   {
  2555.     HideItem("menu_find");
  2556.     HideItem("menu_findnext");
  2557.     HideItem("menu_replace");
  2558.     HideItem("menu_find");
  2559.     RemoveItem("sep_find");
  2560.   }
  2561.  
  2562.    // if no spell checker, remove spell checker ui
  2563.   if (!IsSpellCheckerInstalled())
  2564.   {
  2565.     HideItem("spellingButton");
  2566.     HideItem("menu_checkspelling");
  2567.     RemoveItem("sep_checkspelling");
  2568.   }
  2569.   else
  2570.   {
  2571.     SetElementEnabled(document.getElementById("menu_checkspelling"), true);
  2572.     SetElementEnabled(document.getElementById("spellingButton"), true);
  2573.     SetElementEnabled(document.getElementById("checkspellingkb"), true);
  2574.   }
  2575.  
  2576.   // Remove menu items (from overlay shared with HTML editor) in non-HTML.
  2577.   if (!IsHTMLEditor())
  2578.   {
  2579.     HideItem("insertAnchor");
  2580.     HideItem("insertImage");
  2581.     HideItem("insertHline");
  2582.     HideItem("insertTable");
  2583.     HideItem("insertHTML");
  2584.     HideItem("insertFormMenu");
  2585.     HideItem("fileExportToText");
  2586.     HideItem("viewFormatToolbar");
  2587.     HideItem("viewEditModeToolbar");
  2588.   }
  2589. }
  2590.  
  2591. function HideItem(id)
  2592. {
  2593.   var item = document.getElementById(id);
  2594.   if (item)
  2595.     item.hidden = true;
  2596. }
  2597.  
  2598. function RemoveItem(id)
  2599. {
  2600.   var item = document.getElementById(id);
  2601.   if (item)
  2602.     item.parentNode.removeChild(item);
  2603. }
  2604.  
  2605. // Command Updating Strategy:
  2606. //   Don't update on on selection change, only when menu is displayed,
  2607. //   with this "oncreate" hander:
  2608. function EditorInitTableMenu()
  2609. {
  2610.   try {
  2611.     InitJoinCellMenuitem("menu_JoinTableCells");
  2612.   } catch (ex) {}
  2613.  
  2614.   // Set enable states for all table commands
  2615.   goUpdateTableMenuItems(document.getElementById("composerTableMenuItems"));
  2616. }
  2617.  
  2618. function InitJoinCellMenuitem(id)
  2619. {
  2620.   // Change text on the "Join..." item depending if we
  2621.   //   are joining selected cells or just cell to right
  2622.   // TODO: What to do about normal selection that crosses
  2623.   //       table border? Try to figure out all cells
  2624.   //       included in the selection?
  2625.   var menuText;
  2626.   var menuItem = document.getElementById(id);
  2627.   if (!menuItem) return;
  2628.  
  2629.   // Use "Join selected cells if there's more than 1 cell selected
  2630.   var numSelected;
  2631.   var foundElement;
  2632.   
  2633.   try {
  2634.     var tagNameObj = {};
  2635.     var countObj = {value:0}
  2636.     foundElement = GetCurrentTableEditor().getSelectedOrParentTableElement(tagNameObj, countObj);
  2637.     numSelected = countObj.value
  2638.   }
  2639.   catch(e) {}
  2640.   if (foundElement && numSelected > 1)
  2641.     menuText = GetString("JoinSelectedCells");
  2642.   else
  2643.     menuText = GetString("JoinCellToRight");
  2644.  
  2645.   menuItem.setAttribute("label",menuText);
  2646.   menuItem.setAttribute("accesskey",GetString("JoinCellAccesskey"));
  2647. }
  2648.  
  2649. function InitRemoveStylesMenuitems(removeStylesId, removeLinksId, removeNamedAnchorsId)
  2650. {
  2651.   var editor = GetCurrentEditor();
  2652.   if (!editor)
  2653.     return;
  2654.  
  2655.   // Change wording of menuitems depending on selection
  2656.   var stylesItem = document.getElementById(removeStylesId);
  2657.   var linkItem = document.getElementById(removeLinksId);
  2658.  
  2659.   var isCollapsed = editor.selection.isCollapsed;
  2660.   if (stylesItem)
  2661.   {
  2662.     stylesItem.setAttribute("label", isCollapsed ? GetString("StopTextStyles") : GetString("RemoveTextStyles"));
  2663.     stylesItem.setAttribute("accesskey", GetString("RemoveTextStylesAccesskey"));
  2664.   }
  2665.   if (linkItem)
  2666.   {
  2667.     linkItem.setAttribute("label", isCollapsed ? GetString("StopLinks") : GetString("RemoveLinks"));
  2668.     linkItem.setAttribute("accesskey", GetString("RemoveLinksAccesskey"));
  2669.     // Note: disabling text style is a pain since there are so many - forget it!
  2670.  
  2671.     // Disable if not in a link, but always allow "Remove"
  2672.     //  if selection isn't collapsed since we only look at anchor node
  2673.     try {
  2674.       SetElementEnabled(linkItem, !isCollapsed ||
  2675.                       editor.getElementOrParentByTagName("href", null));
  2676.     } catch(e) {}      
  2677.   }
  2678.   // Disable if selection is collapsed
  2679.   SetElementEnabledById(removeNamedAnchorsId, !isCollapsed);
  2680. }
  2681.  
  2682. function goUpdateTableMenuItems(commandset)
  2683. {
  2684.   var editor = GetCurrentTableEditor();
  2685.   if (!editor)
  2686.   {
  2687.     dump("goUpdateTableMenuItems: too early, not initialized\n");
  2688.     return;
  2689.   }
  2690.  
  2691.   var enabled = false;
  2692.   var enabledIfTable = false;
  2693.  
  2694.   var flags = editor.flags;
  2695.   if (!(flags & nsIPlaintextEditor.eEditorReadonlyMask) &&
  2696.       IsEditingRenderedHTML())
  2697.   {
  2698.     var tagNameObj = { value: "" };
  2699.     var element;
  2700.     try {
  2701.       element = editor.getSelectedOrParentTableElement(tagNameObj, {value:0});
  2702.     }
  2703.     catch(e) {}
  2704.  
  2705.     if (element)
  2706.     {
  2707.       // Value when we need to have a selected table or inside a table
  2708.       enabledIfTable = true;
  2709.  
  2710.       // All others require being inside a cell or selected cell
  2711.       enabled = (tagNameObj.value == "td");
  2712.     }
  2713.   }
  2714.  
  2715.   // Loop through command nodes
  2716.   for (var i = 0; i < commandset.childNodes.length; i++)
  2717.   {
  2718.     var commandID = commandset.childNodes[i].getAttribute("id");
  2719.     if (commandID)
  2720.     {
  2721.       if (commandID == "cmd_InsertTable" ||
  2722.           commandID == "cmd_JoinTableCells" ||
  2723.           commandID == "cmd_SplitTableCell" ||
  2724.           commandID == "cmd_ConvertToTable")
  2725.       {
  2726.         // Call the update method in the command class
  2727.         goUpdateCommand(commandID);
  2728.       }
  2729.       // Directly set with the values calculated here
  2730.       else if (commandID == "cmd_DeleteTable" ||
  2731.                commandID == "cmd_NormalizeTable" ||
  2732.                commandID == "cmd_editTable" ||
  2733.                commandID == "cmd_TableOrCellColor" ||
  2734.                commandID == "cmd_SelectTable")
  2735.       {
  2736.         goSetCommandEnabled(commandID, enabledIfTable);
  2737.       } else {
  2738.         goSetCommandEnabled(commandID, enabled);
  2739.       }
  2740.     }
  2741.   }
  2742. }
  2743.  
  2744. //-----------------------------------------------------------------------------------
  2745. // Helpers for inserting and editing tables:
  2746.  
  2747. function IsInTable()
  2748. {
  2749.   var editor = GetCurrentEditor();
  2750.   try {
  2751.     var flags = editor.flags;
  2752.     return (IsHTMLEditor() &&
  2753.             !(flags & nsIPlaintextEditor.eEditorReadonlyMask) &&
  2754.             IsEditingRenderedHTML() &&
  2755.             null != editor.getElementOrParentByTagName("table", null));
  2756.   } catch (e) {}
  2757.   return false;
  2758. }
  2759.  
  2760. function IsInTableCell()
  2761. {
  2762.   try {
  2763.     var editor = GetCurrentEditor();
  2764.     var flags = editor.flags;
  2765.     return (IsHTMLEditor() &&
  2766.             !(flags & nsIPlaintextEditor.eEditorReadonlyMask) && 
  2767.             IsEditingRenderedHTML() &&
  2768.             null != editor.getElementOrParentByTagName("td", null));
  2769.   } catch (e) {}
  2770.   return false;
  2771.  
  2772. }
  2773.  
  2774. function IsSelectionInOneCell()
  2775. {
  2776.   try {
  2777.     var editor = GetCurrentEditor();
  2778.     var selection = editor.selection;
  2779.  
  2780.     if (selection.rangeCount == 1)
  2781.     {
  2782.       // We have a "normal" single-range selection
  2783.       if (!selection.isCollapsed &&
  2784.          selection.anchorNode != selection.focusNode)
  2785.       {
  2786.         // Check if both nodes are within the same cell
  2787.         var anchorCell = editor.getElementOrParentByTagName("td", selection.anchorNode);
  2788.         var focusCell = editor.getElementOrParentByTagName("td", selection.focusNode);
  2789.         return (focusCell != null && anchorCell != null && (focusCell == anchorCell));
  2790.       }
  2791.       // Collapsed selection or anchor == focus (thus must be in 1 cell)
  2792.       return true;
  2793.     }
  2794.   } catch (e) {}
  2795.   return false;
  2796. }
  2797.  
  2798. // Call this with insertAllowed = true to allow inserting if not in existing table,
  2799. //   else use false to do nothing if not in a table
  2800. function EditorInsertOrEditTable(insertAllowed)
  2801. {
  2802.   if (IsInTable())
  2803.   {
  2804.     // Edit properties of existing table
  2805.     window.openDialog("chrome://editor/content/EdTableProps.xul", "_blank", "chrome,close,titlebar,modal", "","TablePanel");
  2806.     gContentWindow.focus();
  2807.   } 
  2808.   else if (insertAllowed)
  2809.   {
  2810.     try {
  2811.       if (GetCurrentEditor().selection.isCollapsed)
  2812.         // If we have a caret, insert a blank table...
  2813.         EditorInsertTable();
  2814.       else
  2815.         // else convert the selection into a table
  2816.         goDoCommand("cmd_ConvertToTable");
  2817.     } catch (e) {}
  2818.   }
  2819. }
  2820.  
  2821. function EditorInsertTable()
  2822. {
  2823.   // Insert a new table
  2824.   window.openDialog("chrome://editor/content/EdInsertTable.xul", "_blank", "chrome,close,titlebar,modal", "");
  2825.   gContentWindow.focus();
  2826. }
  2827.  
  2828. function EditorTableCellProperties()
  2829. {
  2830.   if (!IsHTMLEditor())
  2831.     return;
  2832.  
  2833.   try {
  2834.     var cell = GetCurrentEditor().getElementOrParentByTagName("td", null);
  2835.     if (cell) {
  2836.       // Start Table Properties dialog on the "Cell" panel
  2837.       window.openDialog("chrome://editor/content/EdTableProps.xul", "_blank", "chrome,close,titlebar,modal", "", "CellPanel");
  2838.       gContentWindow.focus();
  2839.     }
  2840.   } catch (e) {}
  2841. }
  2842.  
  2843. function GetNumberOfContiguousSelectedRows()
  2844. {
  2845.   if (!IsHTMLEditor())
  2846.     return 0;
  2847.  
  2848.   var rows = 0;
  2849.   try {
  2850.     var editor = GetCurrentTableEditor();
  2851.     var rowObj = { value: 0 };
  2852.     var colObj = { value: 0 };
  2853.     var cell = editor.getFirstSelectedCellInTable(rowObj, colObj);
  2854.     if (!cell)
  2855.       return 0;
  2856.  
  2857.     // We have at least one row
  2858.     rows++;
  2859.  
  2860.     var lastIndex = rowObj.value;
  2861.     do {
  2862.       cell = editor.getNextSelectedCell({value:0});
  2863.       if (cell)
  2864.       {
  2865.         editor.getCellIndexes(cell, rowObj, colObj);
  2866.         var index = rowObj.value;
  2867.         if (index == lastIndex + 1)
  2868.         {
  2869.           lastIndex = index;
  2870.           rows++;
  2871.         }
  2872.       }
  2873.     }
  2874.     while (cell);
  2875.   } catch (e) {}
  2876.  
  2877.   return rows;
  2878. }
  2879.  
  2880. function GetNumberOfContiguousSelectedColumns()
  2881. {
  2882.   if (!IsHTMLEditor())
  2883.     return 0;
  2884.  
  2885.   var columns = 0;
  2886.   try {
  2887.     var editor = GetCurrentTableEditor();
  2888.     var colObj = { value: 0 };
  2889.     var rowObj = { value: 0 };
  2890.     var cell = editor.getFirstSelectedCellInTable(rowObj, colObj);
  2891.     if (!cell)
  2892.       return 0;
  2893.  
  2894.     // We have at least one column
  2895.     columns++;
  2896.  
  2897.     var lastIndex = colObj.value;
  2898.     do {
  2899.       cell = editor.getNextSelectedCell({value:0});
  2900.       if (cell)
  2901.       {
  2902.         editor.getCellIndexes(cell, rowObj, colObj);
  2903.         var index = colObj.value;
  2904.         if (index == lastIndex +1)
  2905.         {
  2906.           lastIndex = index;
  2907.           columns++;
  2908.         }
  2909.       }
  2910.     }
  2911.     while (cell);
  2912.   } catch (e) {}
  2913.  
  2914.   return columns;
  2915. }
  2916.  
  2917. function EditorOnFocus()
  2918. {
  2919.   // Current window already has the InsertCharWindow
  2920.   if ("InsertCharWindow" in window && window.InsertCharWindow) return;
  2921.  
  2922.   // Find window with an InsertCharsWindow and switch association to this one
  2923.   var windowWithDialog = FindEditorWithInsertCharDialog();
  2924.   if (windowWithDialog)
  2925.   {
  2926.     // Switch the dialog to current window
  2927.     // this sets focus to dialog, so bring focus back to editor window
  2928.     if (SwitchInsertCharToThisWindow(windowWithDialog))
  2929.       window.focus();
  2930.   }
  2931. }
  2932.  
  2933. function SwitchInsertCharToThisWindow(windowWithDialog)
  2934. {
  2935.   if (windowWithDialog && "InsertCharWindow" in windowWithDialog &&
  2936.       windowWithDialog.InsertCharWindow)
  2937.   {
  2938.     // Move dialog association to the current window
  2939.     window.InsertCharWindow = windowWithDialog.InsertCharWindow;
  2940.     windowWithDialog.InsertCharWindow = null;
  2941.  
  2942.     // Switch the dialog's opener to current window's
  2943.     window.InsertCharWindow.opener = window;
  2944.  
  2945.     // Bring dialog to the forground
  2946.     window.InsertCharWindow.focus();
  2947.     return true;
  2948.   }
  2949.   return false;
  2950. }
  2951.  
  2952. function FindEditorWithInsertCharDialog()
  2953. {
  2954.   try {
  2955.     // Find window with an InsertCharsWindow and switch association to this one
  2956.     var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1'].getService();
  2957.     var windowManagerInterface = windowManager.QueryInterface( Components.interfaces.nsIWindowMediator);
  2958.     var enumerator = windowManagerInterface.getEnumerator( null );
  2959.  
  2960.     while ( enumerator.hasMoreElements()  )
  2961.     {
  2962.       var tempWindow = enumerator.getNext();
  2963.  
  2964.       if (tempWindow != window && "InsertCharWindow" in tempWindow &&
  2965.           tempWindow.InsertCharWindow)
  2966.       {
  2967.         return tempWindow;
  2968.       }
  2969.     }
  2970.   }
  2971.   catch(e) {}
  2972.   return null;
  2973. }
  2974.  
  2975. function EditorFindOrCreateInsertCharWindow()
  2976. {
  2977.   if ("InsertCharWindow" in window && window.InsertCharWindow)
  2978.     window.InsertCharWindow.focus();
  2979.   else
  2980.   {
  2981.     // Since we switch the dialog during EditorOnFocus(),
  2982.     //   this should really never be found, but it's good to be sure
  2983.     var windowWithDialog = FindEditorWithInsertCharDialog();
  2984.     if (windowWithDialog)
  2985.     {
  2986.       SwitchInsertCharToThisWindow(windowWithDialog);
  2987.     }
  2988.     else
  2989.     {
  2990.       // The dialog will set window.InsertCharWindow to itself
  2991.       window.openDialog("chrome://editor/content/EdInsertChars.xul", "_blank", "chrome,close,titlebar", "");
  2992.     }
  2993.   }
  2994. }
  2995.  
  2996. // Find another HTML editor window to associate with the InsertChar dialog
  2997. //   or close it if none found  (May be a mail composer)
  2998. function SwitchInsertCharToAnotherEditorOrClose()
  2999. {
  3000.   if ("InsertCharWindow" in window && window.InsertCharWindow)
  3001.   {
  3002.     var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1'].getService();
  3003.     var enumerator;
  3004.     try {
  3005.       var windowManagerInterface = windowManager.QueryInterface( Components.interfaces.nsIWindowMediator);
  3006.       enumerator = windowManagerInterface.getEnumerator( null );
  3007.     }
  3008.     catch(e) {}
  3009.     if (!enumerator) return;
  3010.  
  3011.     // TODO: Fix this to search for command controllers and look for "cmd_InsertChars"
  3012.     // For now, detect just Web Composer and HTML Mail Composer
  3013.     while ( enumerator.hasMoreElements()  )
  3014.     {
  3015.       var  tempWindow = enumerator.getNext();
  3016.       if (tempWindow != window && tempWindow != window.InsertCharWindow &&
  3017.           "GetCurrentEditor" in tempWindow && tmpWindow.GetCurrentEditor())
  3018.       {
  3019.         tempWindow.InsertCharWindow = window.InsertCharWindow;
  3020.         window.InsertCharWindow = null;
  3021.         tempWindow.InsertCharWindow.opener = tempWindow;
  3022.         return;
  3023.       }
  3024.     }
  3025.     // Didn't find another editor - close the dialog
  3026.     window.InsertCharWindow.close();
  3027.   }
  3028. }
  3029.  
  3030. function ResetStructToolbar()
  3031. {
  3032.   gLastFocusNode = null;
  3033.   UpdateStructToolbar();
  3034. }
  3035.  
  3036. function newCommandListener(element)
  3037. {
  3038.   return function() { return SelectFocusNodeAncestor(element); };
  3039. }
  3040.  
  3041. function newContextmenuListener(button, element)
  3042. {
  3043.   return function() { return InitStructBarContextMenu(button, element); };
  3044. }
  3045.  
  3046. function UpdateStructToolbar()
  3047. {
  3048.   var editor = GetCurrentEditor();
  3049.   if (!editor) return;
  3050.  
  3051.   var mixed = GetSelectionContainer();
  3052.   if (!mixed) return;
  3053.   var element = mixed.node;
  3054.   var oneElementSelected = mixed.oneElementSelected;
  3055.  
  3056.   if (!element) return;
  3057.  
  3058.   if (element == gLastFocusNode &&
  3059.       oneElementSelected == gLastFocusNodeWasSelected)
  3060.     return;
  3061.  
  3062.   gLastFocusNode = element;
  3063.   gLastFocusNodeWasSelected = mixed.oneElementSelected;
  3064.  
  3065.   var toolbar = document.getElementById("structToolbar");
  3066.   if (!toolbar) return;
  3067.   var childNodes = toolbar.childNodes;
  3068.   var childNodesLength = childNodes.length;
  3069.   // We need to leave the <label> to flex the buttons to the left
  3070.   // so, don't remove the last child at position length - 1
  3071.   for (var i = childNodesLength - 2; i >= 0; i--) {
  3072.     toolbar.removeChild(childNodes.item(i));
  3073.   }
  3074.  
  3075.   toolbar.removeAttribute("label");
  3076.  
  3077.   if ( IsInHTMLSourceMode() ) {
  3078.     // we have destroyed the contents of the status bar and are
  3079.     // about to recreate it ; but we don't want to do that in
  3080.     // Source mode
  3081.     return;
  3082.   }
  3083.  
  3084.   var tag, button;
  3085.   var bodyElement = GetBodyElement();
  3086.   var isFocusNode = true;
  3087.   var tmp;
  3088.   do {
  3089.     tag = element.nodeName.toLowerCase();
  3090.  
  3091.     button = document.createElementNS(XUL_NS, "toolbarbutton");
  3092.     button.setAttribute("label",   "<" + tag + ">");
  3093.     button.setAttribute("value",   tag);
  3094.     button.setAttribute("context", "structToolbarContext");
  3095.     button.className = "struct-button";
  3096.  
  3097.     toolbar.insertBefore(button, toolbar.firstChild);
  3098.  
  3099.     button.addEventListener("command", newCommandListener(element), false);
  3100.  
  3101.     button.addEventListener("contextmenu", newContextmenuListener(button, element), false);
  3102.  
  3103.     if (isFocusNode && oneElementSelected) {
  3104.       button.setAttribute("checked", "true");
  3105.       isFocusNode = false;
  3106.     }
  3107.  
  3108.     tmp = element;
  3109.     element = element.parentNode;
  3110.  
  3111.   } while (tmp != bodyElement);
  3112. }
  3113.  
  3114. function SelectFocusNodeAncestor(element)
  3115. {
  3116.   var editor = GetCurrentEditor();
  3117.   if (editor) {
  3118.     if (element == GetBodyElement())
  3119.       editor.selectAll();
  3120.     else
  3121.       editor.selectElement(element);
  3122.   }
  3123.   ResetStructToolbar();
  3124. }
  3125.  
  3126. function GetSelectionContainer()
  3127. {
  3128.   var editor = GetCurrentEditor();
  3129.   if (!editor) return null;
  3130.  
  3131.   try {
  3132.     var selection = editor.selection;
  3133.     if (!selection) return null;
  3134.   }
  3135.   catch (e) { return null; }
  3136.  
  3137.   var result = { oneElementSelected:false };
  3138.  
  3139.   if (selection.isCollapsed) {
  3140.     result.node = selection.focusNode;
  3141.   }
  3142.   else {
  3143.     var rangeCount = selection.rangeCount;
  3144.     if (rangeCount == 1) {
  3145.       result.node = editor.getSelectedElement("");
  3146.       var range = selection.getRangeAt(0);
  3147.  
  3148.       // check for a weird case : when we select a piece of text inside
  3149.       // a text node and apply an inline style to it, the selection starts
  3150.       // at the end of the text node preceding the style and ends after the
  3151.       // last char of the style. Assume the style element is selected for
  3152.       // user's pleasure
  3153.       if (!result.node &&
  3154.           range.startContainer.nodeType == Node.TEXT_NODE &&
  3155.           range.startOffset == range.startContainer.length &&
  3156.           range.endContainer.nodeType == Node.TEXT_NODE &&
  3157.           range.endOffset == range.endContainer.length &&
  3158.           range.endContainer.nextSibling == null &&
  3159.           range.startContainer.nextSibling == range.endContainer.parentNode)
  3160.         result.node = range.endContainer.parentNode;
  3161.  
  3162.       if (!result.node) {
  3163.         // let's rely on the common ancestor of the selection
  3164.         result.node = range.commonAncestorContainer;
  3165.       }
  3166.       else {
  3167.         result.oneElementSelected = true;
  3168.       }
  3169.     }
  3170.     else {
  3171.       // assume table cells !
  3172.       var i, container = null;
  3173.       for (i = 0; i < rangeCount; i++) {
  3174.         range = selection.getRangeAt(i);
  3175.         if (!container) {
  3176.           container = range.startContainer;
  3177.         }
  3178.         else if (container != range.startContainer) {
  3179.           // all table cells don't belong to same row so let's
  3180.           // select the parent of all rows
  3181.           result.node = container.parentNode;
  3182.           break;
  3183.         }
  3184.         result.node = container;
  3185.       }
  3186.     }
  3187.   }
  3188.  
  3189.   // make sure we have an element here
  3190.   while (result.node.nodeType != Node.ELEMENT_NODE)
  3191.     result.node = result.node.parentNode;
  3192.  
  3193.   // and make sure the element is not a special editor node like
  3194.   // the <br> we insert in blank lines
  3195.   while (result.node.hasAttribute("_moz_editor_bogus_node"))
  3196.     result.node = result.node.parentNode;
  3197.  
  3198.   return result;
  3199. }
  3200.  
  3201. function FillInHTMLTooltip(tooltip)
  3202. {
  3203.   var tooltipText = null;
  3204.   for (var node = document.tooltipNode; node; node = node.parentNode) {
  3205.     if (node instanceof Components.interfaces.nsIDOMHTMLImageElement ||
  3206.         node instanceof Components.interfaces.nsIDOMHTMLInputElement)
  3207.       tooltipText = node.getAttribute("src");
  3208.     else if (node instanceof Components.interfaces.nsIDOMHTMLAnchorElement)
  3209.       tooltipText = node.getAttribute("href") || node.name;
  3210.     if (tooltipText) {
  3211.       tooltip.setAttribute("label", tooltipText);
  3212.       return true;
  3213.     }
  3214.   }
  3215.   return false;
  3216. }
  3217.  
  3218. function UpdateTOC()
  3219. {
  3220.   window.openDialog("chrome://editor/content/EdInsertTOC.xul",
  3221.                     "_blank", "chrome,close,modal,titlebar");
  3222.   window._content.focus();
  3223. }
  3224.  
  3225. function InitTOCMenu()
  3226. {
  3227.   var elt = GetCurrentEditor().document.getElementById("mozToc");
  3228.   var createMenuitem = document.getElementById("insertTOCMenuitem");
  3229.   var updateMenuitem = document.getElementById("updateTOCMenuitem");
  3230.   var removeMenuitem = document.getElementById("removeTOCMenuitem");
  3231.   if (removeMenuitem && createMenuitem && updateMenuitem) {
  3232.     if (elt) {
  3233.       createMenuitem.setAttribute("disabled", "true");
  3234.       updateMenuitem.removeAttribute("disabled");
  3235.       removeMenuitem.removeAttribute("disabled");
  3236.     }
  3237.     else {
  3238.       createMenuitem.removeAttribute("disabled");
  3239.       removeMenuitem.setAttribute("disabled", "true");
  3240.       updateMenuitem.setAttribute("disabled", "true");
  3241.     }
  3242.   }
  3243. }
  3244.  
  3245. function RemoveTOC()
  3246. {
  3247.   var theDocument = GetCurrentEditor().document;
  3248.   var elt = theDocument.getElementById("mozToc");
  3249.   if (elt) {
  3250.     elt.parentNode.removeChild(elt);
  3251.   }
  3252.  
  3253.   function acceptNode(node)
  3254.   {
  3255.     if (node.nodeName.toLowerCase() == "a" &&
  3256.         node.hasAttribute("name") &&
  3257.         node.getAttribute("name").substr(0, 8) == "mozTocId") {
  3258.       return NodeFilter.FILTER_ACCEPT;
  3259.     }
  3260.     return NodeFilter.FILTER_SKIP;
  3261.   }
  3262.  
  3263.   var treeWalker = theDocument.createTreeWalker(theDocument.documentElement,
  3264.                                                 NodeFilter.SHOW_ELEMENT,
  3265.                                                 acceptNode,
  3266.                                                 true);
  3267.   if (treeWalker) {
  3268.     var anchorNode = treeWalker.nextNode();
  3269.     while (anchorNode) {
  3270.       var tmp = treeWalker.nextNode();
  3271.       anchorNode.parentNode.removeChild(anchorNode);
  3272.       anchorNode = tmp;
  3273.     }
  3274.   }
  3275. }
  3276.