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 / ComposerCommands.js next >
Text File  |  2003-06-08  |  120KB  |  3,887 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.  *    Simon Fraser (sfraser@netscape.com)
  24.  *    Ryan Cassin (rcassin@supernova.org)
  25.  *    Kathleen Brade (brade@netscape.com)
  26.  *    Daniel Glazman (glazman@netscape.com)
  27.  *
  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. /* Implementations of nsIControllerCommand for composer commands */
  44.  
  45. var gComposerJSCommandControllerID = 0;
  46.  
  47.  
  48. //-----------------------------------------------------------------------------------
  49. function SetupHTMLEditorCommands()
  50. {
  51.   var commandManager = GetComposerCommandManager();
  52.   if (!commandManager)
  53.     return;
  54.   
  55.   // Include everthing a text editor does
  56.   SetupTextEditorCommands();
  57.  
  58.   //dump("Registering HTML editor commands\n");
  59.  
  60.   commandManager.registerCommand("cmd_renderedHTMLEnabler", nsDummyHTMLCommand);
  61.  
  62.   commandManager.registerCommand("cmd_listProperties",  nsListPropertiesCommand);
  63.   commandManager.registerCommand("cmd_pageProperties",  nsPagePropertiesCommand);
  64.   commandManager.registerCommand("cmd_colorProperties", nsColorPropertiesCommand);
  65.   commandManager.registerCommand("cmd_advancedProperties", nsAdvancedPropertiesCommand);
  66.   commandManager.registerCommand("cmd_objectProperties",   nsObjectPropertiesCommand);
  67.   commandManager.registerCommand("cmd_removeLinks",        nsRemoveLinksCommand);
  68.   commandManager.registerCommand("cmd_removeNamedAnchors", nsRemoveNamedAnchorsCommand);
  69.   commandManager.registerCommand("cmd_editLink",        nsEditLinkCommand);
  70.   
  71.   commandManager.registerCommand("cmd_form",          nsFormCommand);
  72.   commandManager.registerCommand("cmd_inputtag",      nsInputTagCommand);
  73.   commandManager.registerCommand("cmd_inputimage",    nsInputImageCommand);
  74.   commandManager.registerCommand("cmd_textarea",      nsTextAreaCommand);
  75.   commandManager.registerCommand("cmd_select",        nsSelectCommand);
  76.   commandManager.registerCommand("cmd_button",        nsButtonCommand);
  77.   commandManager.registerCommand("cmd_label",         nsLabelCommand);
  78.   commandManager.registerCommand("cmd_fieldset",      nsFieldSetCommand);
  79.   commandManager.registerCommand("cmd_isindex",       nsIsIndexCommand);
  80.   commandManager.registerCommand("cmd_image",         nsImageCommand);
  81.   commandManager.registerCommand("cmd_hline",         nsHLineCommand);
  82.   commandManager.registerCommand("cmd_link",          nsLinkCommand);
  83.   commandManager.registerCommand("cmd_anchor",        nsAnchorCommand);
  84.   commandManager.registerCommand("cmd_insertHTMLWithDialog", nsInsertHTMLWithDialogCommand);
  85.   commandManager.registerCommand("cmd_insertBreak",   nsInsertBreakCommand);
  86.   commandManager.registerCommand("cmd_insertBreakAll",nsInsertBreakAllCommand);
  87.  
  88.   commandManager.registerCommand("cmd_table",              nsInsertOrEditTableCommand);
  89.   commandManager.registerCommand("cmd_editTable",          nsEditTableCommand);
  90.   commandManager.registerCommand("cmd_SelectTable",        nsSelectTableCommand);
  91.   commandManager.registerCommand("cmd_SelectRow",          nsSelectTableRowCommand);
  92.   commandManager.registerCommand("cmd_SelectColumn",       nsSelectTableColumnCommand);
  93.   commandManager.registerCommand("cmd_SelectCell",         nsSelectTableCellCommand);
  94.   commandManager.registerCommand("cmd_SelectAllCells",     nsSelectAllTableCellsCommand);
  95.   commandManager.registerCommand("cmd_InsertTable",        nsInsertTableCommand);
  96.   commandManager.registerCommand("cmd_InsertRowAbove",     nsInsertTableRowAboveCommand);
  97.   commandManager.registerCommand("cmd_InsertRowBelow",     nsInsertTableRowBelowCommand);
  98.   commandManager.registerCommand("cmd_InsertColumnBefore", nsInsertTableColumnBeforeCommand);
  99.   commandManager.registerCommand("cmd_InsertColumnAfter",  nsInsertTableColumnAfterCommand);
  100.   commandManager.registerCommand("cmd_InsertCellBefore",   nsInsertTableCellBeforeCommand);
  101.   commandManager.registerCommand("cmd_InsertCellAfter",    nsInsertTableCellAfterCommand);
  102.   commandManager.registerCommand("cmd_DeleteTable",        nsDeleteTableCommand);
  103.   commandManager.registerCommand("cmd_DeleteRow",          nsDeleteTableRowCommand);
  104.   commandManager.registerCommand("cmd_DeleteColumn",       nsDeleteTableColumnCommand);
  105.   commandManager.registerCommand("cmd_DeleteCell",         nsDeleteTableCellCommand);
  106.   commandManager.registerCommand("cmd_DeleteCellContents", nsDeleteTableCellContentsCommand);
  107.   commandManager.registerCommand("cmd_JoinTableCells",     nsJoinTableCellsCommand);
  108.   commandManager.registerCommand("cmd_SplitTableCell",     nsSplitTableCellCommand);
  109.   commandManager.registerCommand("cmd_TableOrCellColor",   nsTableOrCellColorCommand);
  110.   commandManager.registerCommand("cmd_NormalizeTable",     nsNormalizeTableCommand);
  111.   commandManager.registerCommand("cmd_smiley",             nsSetSmiley);
  112.   commandManager.registerCommand("cmd_ConvertToTable",     nsConvertToTable);
  113. }
  114.  
  115. function SetupTextEditorCommands()
  116. {
  117.   var commandManager = GetComposerCommandManager();
  118.   if (!commandManager)
  119.     return;
  120.   
  121.   //dump("Registering plain text editor commands\n");
  122.   
  123.   commandManager.registerCommand("cmd_find",       nsFindCommand);
  124.   commandManager.registerCommand("cmd_findNext",   new nsFindAgainCommand(false));
  125.   commandManager.registerCommand("cmd_findPrev",   new nsFindAgainCommand(true));
  126.   commandManager.registerCommand("cmd_spelling",   nsSpellingCommand);
  127.   commandManager.registerCommand("cmd_validate",   nsValidateCommand);
  128.   commandManager.registerCommand("cmd_checkLinks", nsCheckLinksCommand);
  129.   commandManager.registerCommand("cmd_insertChars", nsInsertCharsCommand);
  130. }
  131.  
  132. function SetupComposerWindowCommands()
  133. {
  134.   // Don't need to do this if already done
  135.   if (gComposerWindowControllerID)
  136.     return;
  137.  
  138.   // Create a command controller and register commands
  139.   //   specific to Web Composer window (file-related commands, HTML Source...)
  140.   //   We can't use the composer controller created on the content window else
  141.   //     we can't process commands when in HTMLSource editor
  142.   // IMPORTANT: For each of these commands, the doCommand method 
  143.   //            must first call FinishHTMLSource() 
  144.   //            to go from HTML Source mode to any other edit mode
  145.  
  146.   var windowControllers = window.controllers;
  147.  
  148.   if (!windowControllers) return;
  149.  
  150.   var commandManager;
  151.   var composerController;
  152.   var editorController;
  153.   try {
  154.     composerController = Components.classes["@mozilla.org/embedcomp/base-command-controller;1"].createInstance();
  155.     // Get the nsIControllerCommandManager interface we need to register commands
  156.     var interfaceRequestor = composerController.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
  157.     commandManager = interfaceRequestor.getInterface(Components.interfaces.nsIControllerCommandManager);
  158.     editorController = interfaceRequestor.QueryInterface(Components.interfaces.nsIControllerContext);
  159.   }
  160.   catch (e)
  161.   {
  162.     dump("Failed to create composerController\n");
  163.     return;
  164.   }
  165.  
  166.  
  167.   if (!commandManager)
  168.   {
  169.     dump("Failed to get interface for nsIControllerCommandManager\n");
  170.     return;
  171.   }
  172.  
  173.   // File-related commands
  174.   commandManager.registerCommand("cmd_open",           nsOpenCommand);
  175.   commandManager.registerCommand("cmd_save",           nsSaveCommand);
  176.   commandManager.registerCommand("cmd_saveAs",         nsSaveAsCommand);
  177.   commandManager.registerCommand("cmd_exportToText",   nsExportToTextCommand);
  178.   commandManager.registerCommand("cmd_saveAsCharset",  nsSaveAsCharsetCommand);
  179.   commandManager.registerCommand("cmd_publish",        nsPublishCommand);
  180.   commandManager.registerCommand("cmd_publishAs",      nsPublishAsCommand);
  181.   commandManager.registerCommand("cmd_publishSettings",nsPublishSettingsCommand);
  182.   commandManager.registerCommand("cmd_revert",         nsRevertCommand);
  183.   commandManager.registerCommand("cmd_openRemote",     nsOpenRemoteCommand);
  184.   commandManager.registerCommand("cmd_preview",        nsPreviewCommand);
  185.   commandManager.registerCommand("cmd_editSendPage",   nsSendPageCommand);
  186.   commandManager.registerCommand("cmd_print",          nsPrintCommand);
  187.   commandManager.registerCommand("cmd_printSetup",     nsPrintSetupCommand);
  188.   commandManager.registerCommand("cmd_quit",           nsQuitCommand);
  189.   commandManager.registerCommand("cmd_close",          nsCloseCommand);
  190.   commandManager.registerCommand("cmd_preferences",    nsPreferencesCommand);
  191.  
  192.   // Edit Mode commands
  193.   if (GetCurrentEditorType() == "html")
  194.   {
  195.     commandManager.registerCommand("cmd_NormalMode",         nsNormalModeCommand);
  196.     commandManager.registerCommand("cmd_AllTagsMode",        nsAllTagsModeCommand);
  197.     commandManager.registerCommand("cmd_HTMLSourceMode",     nsHTMLSourceModeCommand);
  198.     commandManager.registerCommand("cmd_PreviewMode",        nsPreviewModeCommand);
  199.     commandManager.registerCommand("cmd_FinishHTMLSource",   nsFinishHTMLSource);
  200.     commandManager.registerCommand("cmd_CancelHTMLSource",   nsCancelHTMLSource);
  201.     commandManager.registerCommand("cmd_updateStructToolbar", nsUpdateStructToolbarCommand);
  202.   }
  203.  
  204.   windowControllers.insertControllerAt(0, editorController);
  205.  
  206.   // Store the controller ID so we can be sure to get the right one later
  207.   gComposerWindowControllerID = windowControllers.getControllerId(editorController);
  208. }
  209.  
  210. //-----------------------------------------------------------------------------------
  211. function GetComposerCommandManager()
  212. {
  213.     var controller;
  214.   if (gComposerJSCommandControllerID)
  215.   {
  216.     try { 
  217.       controller = window.content.controllers.getControllerById(gComposerJSCommandControllerID);
  218.     } catch (e) {}
  219.   }
  220.   if (!controller)
  221.   {
  222.     //create it
  223.     controller = Components.classes["@mozilla.org/embedcomp/base-command-controller;1"].createInstance();
  224.     window.content.controllers.insertControllerAt(0, controller);
  225.   
  226.     // Store the controller ID so we can be sure to get the right one later
  227.     gComposerJSCommandControllerID = window.content.controllers.getControllerId(controller);
  228.   }
  229.  
  230.   if (controller)
  231.   {
  232.     var interfaceRequestor = controller.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
  233.     return interfaceRequestor.getInterface(Components.interfaces.nsIControllerCommandManager);
  234.   }
  235.   return null;
  236. }
  237.  
  238. //-----------------------------------------------------------------------------------
  239. function goUpdateCommandState(command)
  240. {
  241.   try
  242.   {
  243.     var controller = top.document.commandDispatcher.getControllerForCommand(command);
  244.     if (!(controller instanceof Components.interfaces.nsICommandController))
  245.       return;
  246.  
  247.     var params = newCommandParams();
  248.     if (!params) return;
  249.  
  250.     controller.getCommandStateWithParams(command, params);
  251.  
  252.     switch (command)
  253.     {
  254.       case "cmd_bold":
  255.       case "cmd_italic":
  256.       case "cmd_underline":
  257.       case "cmd_var":
  258.       case "cmd_samp":
  259.       case "cmd_code":
  260.       case "cmd_acronym":
  261.       case "cmd_abbr":
  262.       case "cmd_cite":
  263.       case "cmd_strong":
  264.       case "cmd_em":
  265.       case "cmd_superscript":
  266.       case "cmd_subscript":
  267.       case "cmd_strikethrough":
  268.       case "cmd_tt":
  269.       case "cmd_nobreak":
  270.       case "cmd_ul":
  271.       case "cmd_ol":
  272.         pokeStyleUI(command, params.getBooleanValue("state_all"));
  273.         break;
  274.  
  275.       case "cmd_paragraphState":
  276.       case "cmd_align":
  277.       case "cmd_highlight":
  278.       case "cmd_backgroundColor":
  279.       case "cmd_fontColor":
  280.       case "cmd_fontFace":
  281.       case "cmd_fontSize":
  282.         pokeMultiStateUI(command, params);
  283.         break;
  284.  
  285.       case "cmd_indent":
  286.       case "cmd_outdent":
  287.       case "cmd_increaseFont":
  288.       case "cmd_decreaseFont":
  289.       case "cmd_removeStyles":
  290.         break;
  291.  
  292.       default: dump("no update for command: " +command+"\n");
  293.     }
  294.   }
  295.   catch (e) { dump("An error occurred updating the "+command+" command: \n"+e+"\n"); }
  296. }
  297.  
  298. function goUpdateComposerMenuItems(commandset)
  299. {
  300.   //dump("Updating commands for " + commandset.id + "\n");
  301.  
  302.   for (var i = 0; i < commandset.childNodes.length; i++)
  303.   {
  304.     var commandNode = commandset.childNodes[i];
  305.     var commandID = commandNode.id;
  306.     if (commandID)
  307.     {
  308.       goUpdateCommand(commandID);  // enable or disable
  309.       if (commandNode.hasAttribute("state"))
  310.         goUpdateCommandState(commandID);
  311.     }
  312.   }
  313. }
  314.  
  315. //-----------------------------------------------------------------------------------
  316. function goDoCommandParams(command, params)
  317. {
  318.   try
  319.   {
  320.     var controller = top.document.commandDispatcher.getControllerForCommand(command);
  321.     if (controller && controller.isCommandEnabled(command))
  322.     {
  323.       if (controller instanceof Components.interfaces.nsICommandController)
  324.       {
  325.         controller.doCommandWithParams(command, params);
  326.  
  327.         // the following two lines should be removed when we implement observers
  328.         if (params)
  329.           controller.getCommandStateWithParams(command, params);
  330.       }
  331.       else
  332.       {
  333.         controller.doCommand(command);
  334.       }
  335.       ResetStructToolbar();
  336.     }
  337.   }
  338.   catch (e)
  339.   {
  340.     dump("An error occurred executing the "+command+" command\n");
  341.   }
  342. }
  343.  
  344. function pokeStyleUI(uiID, aDesiredState)
  345. {
  346.  try {
  347.   var commandNode = top.document.getElementById(uiID);
  348.   if (!commandNode)
  349.     return;
  350.  
  351.   var uiState = ("true" == commandNode.getAttribute("state"));
  352.   if (aDesiredState != uiState)
  353.   {
  354.     var newState;
  355.     if (aDesiredState)
  356.       newState = "true";
  357.     else
  358.       newState = "false";
  359.     commandNode.setAttribute("state", newState);
  360.   }
  361.  } catch(e) { dump("poking UI for "+uiID+" failed: "+e+"\n"); }
  362. }
  363.  
  364. function doStyleUICommand(cmdStr)
  365. {
  366.   try
  367.   {
  368.     var cmdParams = newCommandParams();
  369.     goDoCommandParams(cmdStr, cmdParams);
  370.     if (cmdParams)
  371.       pokeStyleUI(cmdStr, cmdParams.getBooleanValue("state_all"));
  372.  
  373.     ResetStructToolbar();
  374.   } catch(e) {}
  375. }
  376.  
  377. function pokeMultiStateUI(uiID, cmdParams)
  378. {
  379.   try
  380.   {
  381.     var commandNode = document.getElementById(uiID);
  382.     if (!commandNode)
  383.       return;
  384.  
  385.     var isMixed = cmdParams.getBooleanValue("state_mixed");
  386.     var desiredAttrib;
  387.     if (isMixed)
  388.       desiredAttrib = "mixed";
  389.     else
  390.       desiredAttrib = cmdParams.getCStringValue("state_attribute");
  391.  
  392.     var uiState = commandNode.getAttribute("state");
  393.     if (desiredAttrib != uiState)
  394.     {
  395.       commandNode.setAttribute("state", desiredAttrib);
  396.     }
  397.   } catch(e) {}
  398. }
  399.  
  400. function doStatefulCommand(commandID, newState)
  401. {
  402.   var commandNode = document.getElementById(commandID);
  403.   if (commandNode)
  404.       commandNode.setAttribute("state", newState);
  405.   gContentWindow.focus();   // needed for command dispatch to work
  406.  
  407.   try
  408.   {
  409.     var cmdParams = newCommandParams();
  410.     if (!cmdParams) return;
  411.  
  412.     cmdParams.setCStringValue("state_attribute", newState);
  413.     goDoCommandParams(commandID, cmdParams);
  414.  
  415.     pokeMultiStateUI(commandID, cmdParams);
  416.  
  417.     ResetStructToolbar();
  418.   } catch(e) { dump("error thrown in doStatefulCommand: "+e+"\n"); }
  419. }
  420.  
  421. //-----------------------------------------------------------------------------------
  422. function PrintObject(obj)
  423. {
  424.   dump("-----" + obj + "------\n");
  425.   var names = "";
  426.   for (var i in obj)
  427.   {
  428.     if (i == "value")
  429.       names += i + ": " + obj.value + "\n";
  430.     else if (i == "id")
  431.       names += i + ": " + obj.id + "\n";
  432.     else
  433.       names += i + "\n";
  434.   }
  435.   
  436.   dump(names + "-----------\n");
  437. }
  438.  
  439. //-----------------------------------------------------------------------------------
  440. function PrintNodeID(id)
  441. {
  442.   PrintObject(document.getElementById(id));
  443. }
  444.  
  445. //-----------------------------------------------------------------------------------
  446. var nsDummyHTMLCommand =
  447. {
  448.   isCommandEnabled: function(aCommand, dummy)
  449.   {
  450.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  451.   },
  452.  
  453.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  454.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  455.  
  456.   doCommand: function(aCommand)
  457.   {
  458.     // do nothing
  459.     dump("Hey, who's calling the dummy command?\n");
  460.   }
  461.  
  462. };
  463.  
  464. //-----------------------------------------------------------------------------------
  465. var nsOpenCommand =
  466. {
  467.   isCommandEnabled: function(aCommand, dummy)
  468.   {
  469.     return true;    // we can always do this
  470.   },
  471.  
  472.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  473.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  474.  
  475.   doCommand: function(aCommand)
  476.   {
  477.     var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
  478.     fp.init(window, GetString("OpenHTMLFile"), nsIFilePicker.modeOpen);
  479.  
  480.     SetFilePickerDirectory(fp, "html");
  481.  
  482.     // When loading into Composer, direct user to prefer HTML files and text files,
  483.     //   so we call separately to control the order of the filter list
  484.     fp.appendFilters(nsIFilePicker.filterHTML);
  485.     fp.appendFilters(nsIFilePicker.filterText);
  486.     fp.appendFilters(nsIFilePicker.filterAll);
  487.  
  488.     /* doesn't handle *.shtml files */
  489.     try {
  490.       fp.show();
  491.       /* need to handle cancel (uncaught exception at present) */
  492.     }
  493.     catch (ex) {
  494.       dump("filePicker.chooseInputFile threw an exception\n");
  495.     }
  496.   
  497.     /* This checks for already open window and activates it... 
  498.      * note that we have to test the native path length
  499.      *  since file.URL will be "file:///" if no filename picked (Cancel button used)
  500.      */
  501.     if (fp.file && fp.file.path.length > 0) {
  502.       SaveFilePickerDirectory(fp, "html");
  503.       editPage(fp.fileURL.spec, window, false);
  504.     }
  505.   }
  506. };
  507.  
  508. // STRUCTURE TOOLBAR
  509. //
  510. var nsUpdateStructToolbarCommand =
  511. {
  512.   isCommandEnabled: function(aCommand, dummy)
  513.   {
  514.     UpdateStructToolbar();
  515.     return true;
  516.   },
  517.  
  518.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  519.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  520.   doCommand: function(aCommand)  {}
  521. }
  522.  
  523. // ******* File output commands and utilities ******** //
  524. //-----------------------------------------------------------------------------------
  525. var nsSaveCommand =
  526. {
  527.   isCommandEnabled: function(aCommand, dummy)
  528.   {
  529.     // Always allow saving when editing a remote document,
  530.     //  otherwise the document modified state would prevent that
  531.     //  when you first open a remote file.
  532.     try {
  533.       var docUrl = GetDocumentUrl();
  534.       return IsDocumentEditable() &&
  535.         (IsDocumentModified() || window.gHTMLSourceChanged ||
  536.          IsUrlAboutBlank(docUrl) || GetScheme(docUrl) != "file");
  537.     } catch (e) {return false;}
  538.   },
  539.   
  540.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  541.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  542.  
  543.   doCommand: function(aCommand)
  544.   {
  545.     var result = false;
  546.     var editor = GetCurrentEditor();
  547.     if (editor)
  548.     {
  549.       FinishHTMLSource();
  550.       result = SaveDocument(IsUrlAboutBlank(GetDocumentUrl()), false, editor.contentsMIMEType);
  551.       window.content.focus();
  552.     }
  553.     return result;
  554.   }
  555. }
  556.  
  557. var nsSaveAsCommand =
  558. {
  559.   isCommandEnabled: function(aCommand, dummy)
  560.   {
  561.     return (IsDocumentEditable());
  562.   },
  563.  
  564.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  565.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  566.  
  567.   doCommand: function(aCommand)
  568.   {
  569.     var editor = GetCurrentEditor();
  570.     if (editor)
  571.     {
  572.       FinishHTMLSource();
  573.       var result = SaveDocument(true, false, editor.contentsMIMEType);
  574.       window.content.focus();
  575.       return result;
  576.     }
  577.     return false;
  578.   }
  579. }
  580.  
  581. var nsExportToTextCommand =
  582. {
  583.   isCommandEnabled: function(aCommand, dummy)
  584.   {
  585.     return (IsDocumentEditable());
  586.   },
  587.  
  588.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  589.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  590.  
  591.   doCommand: function(aCommand)
  592.   {
  593.     if (GetCurrentEditor())
  594.     {
  595.       FinishHTMLSource();
  596.       var result = SaveDocument(true, true, "text/plain");
  597.       window.content.focus();
  598.       return result;
  599.     }
  600.     return false;
  601.   }
  602. }
  603.  
  604. var nsSaveAsCharsetCommand =
  605. {
  606.   isCommandEnabled: function(aCommand, dummy)
  607.   {
  608.     return (IsDocumentEditable());
  609.   },
  610.  
  611.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  612.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  613.  
  614.   doCommand: function(aCommand)
  615.   {    
  616.     FinishHTMLSource();
  617.     window.ok = false;
  618.     window.exportToText = false;
  619.     var oldTitle = GetDocumentTitle();
  620.     window.openDialog("chrome://editor/content/EditorSaveAsCharset.xul","_blank", "chrome,close,titlebar,modal,resizable=yes");
  621.  
  622.     if (GetDocumentTitle() != oldTitle)
  623.       UpdateWindowTitle();
  624.  
  625.     if (window.ok)
  626.     {
  627.       if (window.exportToText)
  628.       {
  629.         window.ok = SaveDocument(true, true, "text/plain");
  630.       }
  631.       else
  632.       {
  633.         var editor = GetCurrentEditor();
  634.         window.ok = SaveDocument(true, false, editor ? editor.contentsMIMEType : null);
  635.       }
  636.     }
  637.  
  638.     window.content.focus();
  639.     return window.ok;
  640.   }
  641. };
  642.  
  643. var nsPublishCommand =
  644. {
  645.   isCommandEnabled: function(aCommand, dummy)
  646.   {
  647.     if (IsDocumentEditable())
  648.     {
  649.       // Always allow publishing when editing a local document,
  650.       //  otherwise the document modified state would prevent that
  651.       //  when you first open any local file.
  652.       try {
  653.         var docUrl = GetDocumentUrl();
  654.         return IsDocumentModified() || window.gHTMLSourceChanged
  655.                || IsUrlAboutBlank(docUrl) || GetScheme(docUrl) == "file";
  656.       } catch (e) {return false;}
  657.     }
  658.     return false;
  659.   },
  660.   
  661.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  662.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  663.  
  664.   doCommand: function(aCommand)
  665.   {
  666.     if (GetCurrentEditor())
  667.     {
  668.       var docUrl = GetDocumentUrl();
  669.       var filename = GetFilename(docUrl);
  670.       var publishData;
  671.       var showPublishDialog = false;
  672.  
  673.       // First check pref to always show publish dialog
  674.       try {
  675.         var prefs = GetPrefs();
  676.         if (prefs)
  677.           showPublishDialog = prefs.getBoolPref("editor.always_show_publish_dialog");
  678.       } catch(e) {}
  679.  
  680.       if (!showPublishDialog && filename)
  681.       {
  682.         // Try to get publish data from the document url
  683.         publishData = CreatePublishDataFromUrl(docUrl);
  684.  
  685.         // If none, use default publishing site? Need a pref for this
  686.         //if (!publishData)
  687.         //  publishData = GetPublishDataFromSiteName(GetDefaultPublishSiteName(), filename);
  688.       }
  689.  
  690.       if (showPublishDialog || !publishData)
  691.       {
  692.         // Show the publish dialog
  693.         publishData = {};
  694.         window.ok = false;
  695.         var oldTitle = GetDocumentTitle();
  696.         window.openDialog("chrome://editor/content/EditorPublish.xul","_blank", 
  697.                           "chrome,close,titlebar,modal", "", "", publishData);
  698.         if (GetDocumentTitle() != oldTitle)
  699.           UpdateWindowTitle();
  700.  
  701.         window.content.focus();
  702.         if (!window.ok)
  703.           return false;
  704.       }
  705.       if (publishData)
  706.       {
  707.         FinishHTMLSource();
  708.         return Publish(publishData);
  709.       }
  710.     }
  711.     return false;
  712.   }
  713. }
  714.  
  715. var nsPublishAsCommand =
  716. {
  717.   isCommandEnabled: function(aCommand, dummy)
  718.   {
  719.     return (IsDocumentEditable());
  720.   },
  721.   
  722.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  723.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  724.  
  725.   doCommand: function(aCommand)
  726.   {
  727.     if (GetCurrentEditor())
  728.     {
  729.       FinishHTMLSource();
  730.  
  731.       window.ok = false;
  732.       publishData = {};
  733.       var oldTitle = GetDocumentTitle();
  734.       window.openDialog("chrome://editor/content/EditorPublish.xul","_blank", 
  735.                         "chrome,close,titlebar,modal", "", "", publishData);
  736.       if (GetDocumentTitle() != oldTitle)
  737.         UpdateWindowTitle();
  738.  
  739.       window.content.focus();
  740.       if (window.ok)
  741.         return Publish(publishData);
  742.     }
  743.     return false;
  744.   }
  745. }
  746.  
  747. // ------- output utilites   ----- //
  748.  
  749. // returns a fileExtension string
  750. function GetExtensionBasedOnMimeType(aMIMEType)
  751. {
  752.   try {
  753.     var mimeService = null;
  754.     mimeService = Components.classes["@mozilla.org/mime;1"].getService();
  755.     mimeService = mimeService.QueryInterface(Components.interfaces.nsIMIMEService);
  756.  
  757.     var mimeInfo = mimeService.GetFromMIMEType(aMIMEType);
  758.     if (!mimeInfo) return "";
  759.  
  760.     var fileExtension = mimeInfo.primaryExtension;
  761.  
  762.     // the MIME service likes to give back ".htm" for text/html files,
  763.     // so do a special-case fix here.
  764.     if (fileExtension == "htm")
  765.       fileExtension = "html";
  766.  
  767.     return fileExtension;
  768.   }
  769.   catch (e) {}
  770.   return "";
  771. }
  772.  
  773. function GetSuggestedFileName(aDocumentURLString, aMIMEType)
  774. {
  775.   var extension = GetExtensionBasedOnMimeType(aMIMEType);
  776.   if (extension)
  777.     extension = "." + extension;
  778.  
  779.   // check for existing file name we can use
  780.   if (aDocumentURLString.length >= 0 && !IsUrlAboutBlank(aDocumentURLString))
  781.   {
  782.     var docURI = null;
  783.     try {
  784.  
  785.       var ioService = GetIOService();
  786.       docURI = ioService.newURI(aDocumentURLString, GetCurrentEditor().documentCharacterSet, null);
  787.       docURI = docURI.QueryInterface(Components.interfaces.nsIURL);
  788.  
  789.       // grab the file name
  790.       var url = docURI.fileBaseName;
  791.       if (url)
  792.         return url+extension;
  793.     } catch(e) {}
  794.   } 
  795.  
  796.   // check if there is a title we can use
  797.   var title = GetDocumentTitle();
  798.   if (title) // we have a title; let's see if it's usable
  799.   {
  800.     // clean up the title to make it a usable filename
  801.     title = title.replace(/\"/g, "");  // Strip out quote character: "
  802.     title = TrimString(title); // trim whitespace from beginning and end
  803.     title = title.replace(/[ \.\\@\/:]/g, "_");  //Replace "bad" filename characters with "_"
  804.     if (title.length > 0)
  805.       return title + extension;
  806.   }
  807.  
  808.   // if we still don't have a file name, let's just go with "untitled"
  809.   return GetString("untitled") + extension;
  810. }
  811.  
  812. // returns file picker result
  813. function PromptForSaveLocation(aDoSaveAsText, aEditorType, aMIMEType, aDocumentURLString)
  814. {
  815.   var dialogResult = {};
  816.   dialogResult.filepickerClick = nsIFilePicker.returnCancel;
  817.   dialogResult.resultingURI = "";
  818.   dialogResult.resultingLocalFile = null;
  819.  
  820.   var fp = null;
  821.   try {
  822.     fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
  823.   } catch (e) {}
  824.   if (!fp) return dialogResult;
  825.  
  826.   // determine prompt string based on type of saving we'll do
  827.   var promptString;
  828.   if (aDoSaveAsText || aEditorType == "text")
  829.     promptString = GetString("ExportToText");
  830.   else
  831.     promptString = GetString("SaveDocumentAs")
  832.  
  833.   fp.init(window, promptString, nsIFilePicker.modeSave);
  834.  
  835.   // Set filters according to the type of output
  836.   if (aDoSaveAsText)
  837.     fp.appendFilters(nsIFilePicker.filterText);
  838.   else
  839.     fp.appendFilters(nsIFilePicker.filterHTML);
  840.   fp.appendFilters(nsIFilePicker.filterAll);
  841.  
  842.   // now let's actually set the filepicker's suggested filename
  843.   var suggestedFileName = GetSuggestedFileName(aDocumentURLString, aMIMEType);
  844.   if (suggestedFileName)
  845.     fp.defaultString = suggestedFileName;
  846.  
  847.   // set the file picker's current directory
  848.   // assuming we have information needed (like prior saved location)
  849.   try {
  850.     var ioService = GetIOService();
  851.     var fileHandler = GetFileProtocolHandler();
  852.     
  853.     var isLocalFile = true;
  854.     try {
  855.       var docURI = ioService.newURI(aDocumentURLString, GetCurrentEditor().documentCharacterSet, null);
  856.       isLocalFile = docURI.schemeIs("file");
  857.     }
  858.     catch (e) {}
  859.  
  860.     var parentLocation = null;
  861.     if (isLocalFile)
  862.     {
  863.       var fileLocation = fileHandler.getFileFromURLSpec(aDocumentURLString); // this asserts if url is not local
  864.       parentLocation = fileLocation.parent;
  865.     }
  866.     if (parentLocation)
  867.     {
  868.       // Save current filepicker's default location
  869.       if ("gFilePickerDirectory" in window)
  870.         gFilePickerDirectory = fp.displayDirectory;
  871.  
  872.       fp.displayDirectory = parentLocation;
  873.     }
  874.     else
  875.     {
  876.       // Initialize to the last-used directory for the particular type (saved in prefs)
  877.       SetFilePickerDirectory(fp, aEditorType);
  878.     }
  879.   }
  880.   catch(e) {}
  881.  
  882.   dialogResult.filepickerClick = fp.show();
  883.   if (dialogResult.filepickerClick != nsIFilePicker.returnCancel)
  884.   {
  885.     // reset urlstring to new save location
  886.     dialogResult.resultingURIString = fileHandler.getURLSpecFromFile(fp.file);
  887.     dialogResult.resultingLocalFile = fp.file;
  888.     SaveFilePickerDirectory(fp, aEditorType);
  889.   }
  890.   else if ("gFilePickerDirectory" in window && gFilePickerDirectory)
  891.     fp.displayDirectory = gFilePickerDirectory; 
  892.  
  893.   return dialogResult;
  894. }
  895.  
  896. // returns a boolean (whether to continue (true) or not (false) because user canceled)
  897. function PromptAndSetTitleIfNone()
  898. {
  899.   if (GetDocumentTitle()) // we have a title; no need to prompt!
  900.     return true;
  901.  
  902.   var promptService = GetPromptService();
  903.   if (!promptService) return false;
  904.  
  905.   var result = {value:null};
  906.   var captionStr = GetString("DocumentTitle");
  907.   var msgStr = GetString("NeedDocTitle") + '\n' + GetString("DocTitleHelp");
  908.   var confirmed = promptService.prompt(window, captionStr, msgStr, result, null, {value:0});
  909.   if (confirmed)
  910.     SetDocumentTitle(TrimString(result.value));
  911.  
  912.   return confirmed;
  913. }
  914.  
  915. var gPersistObj;
  916.  
  917. // Don't forget to do these things after calling OutputFileWithPersistAPI:
  918. // we need to update the uri before notifying listeners
  919. //    if (doUpdateURI)
  920. //      SetDocumentURI(docURI);
  921. //    UpdateWindowTitle();
  922. //    if (!aSaveCopy)
  923. //      editor.resetModificationCount();
  924.       // this should cause notification to listeners that document has changed
  925.  
  926. const webPersist = Components.interfaces.nsIWebBrowserPersist;
  927. function OutputFileWithPersistAPI(editorDoc, aDestinationLocation, aRelatedFilesParentDir, aMimeType)
  928. {
  929.   gPersistObj = null;
  930.   var editor = GetCurrentEditor();
  931.   try {
  932.     var imeEditor = editor.QueryInterface(Components.interfaces.nsIEditorIMESupport);
  933.     imeEditor.ForceCompositionEnd();
  934.     } catch (e) {}
  935.  
  936.   var isLocalFile = false;
  937.   try {
  938.     var tmp1 = aDestinationLocation.QueryInterface(Components.interfaces.nsIFile);
  939.     isLocalFile = true;
  940.   } 
  941.   catch (e) {
  942.     try {
  943.       var tmp = aDestinationLocation.QueryInterface(Components.interfaces.nsIURI);
  944.       isLocalFile = tmp.schemeIs("file");
  945.     }
  946.     catch (e) {}
  947.   }
  948.  
  949.   try {
  950.     // we should supply a parent directory if/when we turn on functionality to save related documents
  951.     var persistObj = Components.classes["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].createInstance(webPersist);
  952.     persistObj.progressListener = gEditorOutputProgressListener;
  953.     
  954.     var wrapColumn = GetWrapColumn();
  955.     var outputFlags = GetOutputFlags(aMimeType, wrapColumn);
  956.  
  957.     // for 4.x parity as well as improving readability of file locally on server
  958.     // this will always send crlf for upload (http/ftp)
  959.     if (!isLocalFile) // if we aren't saving locally then send both cr and lf
  960.     {
  961.       outputFlags |= webPersist.ENCODE_FLAGS_CR_LINEBREAKS | webPersist.ENCODE_FLAGS_LF_LINEBREAKS;
  962.  
  963.       // we want to serialize the output for all remote publishing
  964.       // some servers can handle only one connection at a time
  965.       // some day perhaps we can make this user-configurable per site?
  966.       persistObj.persistFlags = persistObj.persistFlags | webPersist.PERSIST_FLAGS_SERIALIZE_OUTPUT;
  967.     }
  968.  
  969.     // note: we always want to set the replace existing files flag since we have
  970.     // already given user the chance to not replace an existing file (file picker)
  971.     // or the user picked an option where the file is implicitly being replaced (save)
  972.     persistObj.persistFlags = persistObj.persistFlags 
  973.                             | webPersist.PERSIST_FLAGS_NO_BASE_TAG_MODIFICATIONS
  974.                             | webPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES
  975.                             | webPersist.PERSIST_FLAGS_DONT_FIXUP_LINKS
  976.                             | webPersist.PERSIST_FLAGS_FIXUP_ORIGINAL_DOM;
  977.     persistObj.saveDocument(editorDoc, aDestinationLocation, aRelatedFilesParentDir, 
  978.                             aMimeType, outputFlags, wrapColumn);
  979.     gPersistObj = persistObj;
  980.   }
  981.   catch(e) { dump("caught an error, bail\n"); return false; }
  982.  
  983.   return true;
  984. }
  985.  
  986. // returns output flags based on mimetype, wrapCol and prefs
  987. function GetOutputFlags(aMimeType, aWrapColumn)
  988. {
  989.   var outputFlags = 0;
  990.   var editor = GetCurrentEditor();
  991.   var outputEntity = (editor && editor.documentCharacterSet == "ISO-8859-1")
  992.     ? webPersist.ENCODE_FLAGS_ENCODE_LATIN1_ENTITIES
  993.     : webPersist.ENCODE_FLAGS_ENCODE_BASIC_ENTITIES;
  994.   if (aMimeType == "text/plain")
  995.   {
  996.     // When saving in "text/plain" format, always do formatting
  997.     outputFlags |= webPersist.ENCODE_FLAGS_FORMATTED;
  998.   }
  999.   else
  1000.   {
  1001.     try {
  1002.       // Should we prettyprint? Check the pref
  1003.       var prefs = GetPrefs();
  1004.       if (prefs.getBoolPref("editor.prettyprint"))
  1005.         outputFlags |= webPersist.ENCODE_FLAGS_FORMATTED;
  1006.  
  1007.       // How much entity names should we output? Check the pref
  1008.       var encodeEntity = prefs.getCharPref("editor.encode_entity");
  1009.       switch (encodeEntity) {
  1010.         case "basic"  : outputEntity = webPersist.ENCODE_FLAGS_ENCODE_BASIC_ENTITIES; break;
  1011.         case "latin1" : outputEntity = webPersist.ENCODE_FLAGS_ENCODE_LATIN1_ENTITIES; break;
  1012.         case "html"   : outputEntity = webPersist.ENCODE_FLAGS_ENCODE_HTML_ENTITIES; break;
  1013.         case "none"   : outputEntity = 0; break;
  1014.       }
  1015.     }
  1016.     catch (e) {}
  1017.   }
  1018.   outputFlags |= outputEntity;
  1019.  
  1020.   if (aWrapColumn > 0)
  1021.     outputFlags |= webPersist.ENCODE_FLAGS_WRAP;
  1022.  
  1023.   return outputFlags;
  1024. }
  1025.  
  1026. // returns number of column where to wrap
  1027. const nsIWebBrowserPersist = Components.interfaces.nsIWebBrowserPersist;
  1028. function GetWrapColumn()
  1029. {
  1030.   try {
  1031.     return GetCurrentEditor().wrapWidth;
  1032.   } catch (e) {}
  1033.   return 0;
  1034. }
  1035.  
  1036. function GetPromptService()
  1037. {
  1038.   var promptService;
  1039.   try {
  1040.     promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService();
  1041.     promptService = promptService.QueryInterface(Components.interfaces.nsIPromptService);
  1042.   }
  1043.   catch (e) {}
  1044.   return promptService;
  1045. }
  1046.  
  1047. const gShowDebugOutputStateChange = false;
  1048. const gShowDebugOutputProgress = false;
  1049. const gShowDebugOutputStatusChange = false;
  1050.  
  1051. const gShowDebugOutputLocationChange = false;
  1052. const gShowDebugOutputSecurityChange = false;
  1053.  
  1054. const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
  1055. const nsIChannel = Components.interfaces.nsIChannel;
  1056.  
  1057. const kErrorBindingAborted = 2152398850;
  1058. const kErrorBindingRedirected = 2152398851;
  1059. const kFileNotFound = 2152857618;
  1060.  
  1061. var gEditorOutputProgressListener =
  1062. {
  1063.   onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus)
  1064.   {
  1065.     var editor = GetCurrentEditor();
  1066.  
  1067.     // Use this to access onStateChange flags
  1068.     var requestSpec;
  1069.     try {
  1070.       var channel = aRequest.QueryInterface(nsIChannel);
  1071.       requestSpec = StripUsernamePasswordFromURI(channel.URI);
  1072.     } catch (e) {
  1073.       if ( gShowDebugOutputStateChange)
  1074.         dump("***** onStateChange; NO REQUEST CHANNEL\n");
  1075.     }
  1076.  
  1077.     var pubSpec;
  1078.     if (gPublishData)
  1079.       pubSpec = gPublishData.publishUrl + gPublishData.docDir + gPublishData.filename;
  1080.  
  1081.     if (gShowDebugOutputStateChange)
  1082.     {
  1083.       dump("\n***** onStateChange request: " + requestSpec + "\n");
  1084.       dump("      state flags: ");
  1085.  
  1086.       if (aStateFlags & nsIWebProgressListener.STATE_START)
  1087.         dump(" STATE_START, ");
  1088.       if (aStateFlags & nsIWebProgressListener.STATE_STOP)
  1089.         dump(" STATE_STOP, ");
  1090.       if (aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK)
  1091.         dump(" STATE_IS_NETWORK ");
  1092.  
  1093.       dump("\n * requestSpec="+requestSpec+", pubSpec="+pubSpec+", aStatus="+aStatus+"\n");
  1094.  
  1095.       DumpDebugStatus(aStatus);
  1096.     }
  1097.     // The rest only concerns publishing, so bail out if no dialog
  1098.     if (!gProgressDialog)
  1099.       return;
  1100.  
  1101.     // Detect start of file upload of any file:
  1102.     // (We ignore any START messages after gPersistObj says publishing is finished
  1103.     if ((aStateFlags & nsIWebProgressListener.STATE_START)
  1104.          && gPersistObj && requestSpec
  1105.          && (gPersistObj.currentState != gPersistObj.PERSIST_STATE_FINISHED))
  1106.     {
  1107.       try {
  1108.         // Add url to progress dialog's list showing each file uploading
  1109.         gProgressDialog.SetProgressStatus(GetFilename(requestSpec), "busy");
  1110.       } catch(e) {}
  1111.     }
  1112.  
  1113.     // Detect end of file upload of any file:
  1114.     if (aStateFlags & nsIWebProgressListener.STATE_STOP)
  1115.     {
  1116.       // ignore aStatus == kErrorBindingAborted; check http response for possible errors
  1117.       try {
  1118.         // check http channel for response: 200 range is ok; other ranges are not
  1119.         var httpChannel = aRequest.QueryInterface(Components.interfaces.nsIHttpChannel);
  1120.         var httpResponse = httpChannel.responseStatus;
  1121.         if (httpResponse < 200 || httpResponse >= 300)
  1122.           aStatus = httpResponse;   // not a real error but enough to pass check below
  1123.         else if (aStatus == kErrorBindingAborted)
  1124.           aStatus = 0;
  1125.  
  1126.         if (gShowDebugOutputStateChange)
  1127.           dump("http response is: "+httpResponse+"\n");
  1128.       } 
  1129.       catch(e) 
  1130.       {
  1131.         if (aStatus == kErrorBindingAborted)
  1132.           aStatus = 0;
  1133.       }
  1134.  
  1135.       // We abort publishing for all errors except if image src file is not found
  1136.       var abortPublishing = (aStatus != 0 && aStatus != kFileNotFound);
  1137.  
  1138.       // Notify progress dialog when we receive the STOP
  1139.       //  notification for a file if there was an error 
  1140.       //  or a successful finish
  1141.       //  (Check requestSpec to be sure message is for destination url)
  1142.       if (aStatus != 0 
  1143.            || (requestSpec && requestSpec.indexOf(GetScheme(gPublishData.publishUrl)) == 0))
  1144.       {
  1145.         try {
  1146.           gProgressDialog.SetProgressFinished(GetFilename(requestSpec), aStatus);
  1147.         } catch(e) {}
  1148.       }
  1149.  
  1150.  
  1151.       if (abortPublishing)
  1152.       {
  1153.         // Cancel publishing
  1154.         gPersistObj.cancelSave();
  1155.  
  1156.         // Don't do any commands after failure
  1157.         gCommandAfterPublishing = null;
  1158.  
  1159.         // Restore original document to undo image src url adjustments
  1160.         if (gRestoreDocumentSource)
  1161.         {
  1162.           try {
  1163.             editor.rebuildDocumentFromSource(gRestoreDocumentSource);
  1164.  
  1165.             // Clear transaction cache since we just did a potentially 
  1166.             //  very large insert and this will eat up memory
  1167.             editor.transactionManager.clear();
  1168.           }
  1169.           catch (e) {}
  1170.         }
  1171.  
  1172.         // Notify progress dialog that we're finished
  1173.         //  and keep open to show error
  1174.         gProgressDialog.SetProgressFinished(null, 0);
  1175.  
  1176.         // We don't want to change location or reset mod count, etc.
  1177.         return;
  1178.       }
  1179.  
  1180.       //XXX HACK: "file://" protocol is not supported in network code
  1181.       //    (bug 151867 filed to add this support, bug 151869 filed
  1182.       //     to remove this and other code in nsIWebBrowserPersist)
  1183.       //    nsIWebBrowserPersist *does* copy the file(s), but we don't 
  1184.       //    get normal onStateChange messages.
  1185.  
  1186.       // Case 1: If images are included, we get fairly normal
  1187.       //    STATE_START/STATE_STOP & STATE_IS_NETWORK messages associated with the image files,
  1188.       //    thus we must finish HTML file progress below
  1189.  
  1190.       // Case 2: If just HTML file is uploaded, we get STATE_START and STATE_STOP
  1191.       //    notification with a null "requestSpec", and 
  1192.       //    the gPersistObj is destroyed before we get here!
  1193.       //    So create an new object so we can flow through normal processing below
  1194.       if (!requestSpec && GetScheme(gPublishData.publishUrl) == "file"
  1195.           && (!gPersistObj || gPersistObj.currentState == nsIWebBrowserPersist.PERSIST_STATE_FINISHED))
  1196.       {
  1197.         aStateFlags |= nsIWebProgressListener.STATE_IS_NETWORK;
  1198.         if (!gPersistObj)
  1199.         {          
  1200.           gPersistObj =
  1201.           {
  1202.             result : aStatus,
  1203.             currentState : nsIWebBrowserPersist.PERSIST_STATE_FINISHED
  1204.           }
  1205.         }
  1206.       }
  1207.  
  1208.       // STATE_IS_NETWORK signals end of publishing, as does the gPersistObj.currentState
  1209.       if (aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK
  1210.           && gPersistObj.currentState == nsIWebBrowserPersist.PERSIST_STATE_FINISHED)
  1211.       {
  1212.         if (GetScheme(gPublishData.publishUrl) == "file")
  1213.         {
  1214.           //XXX "file://" hack: We don't get notified about the HTML file, so end progress for it
  1215.           // (This covers both "Case 1 and 2" described above)
  1216.           gProgressDialog.SetProgressFinished(gPublishData.filename, gPersistObj.result);
  1217.         }
  1218.  
  1219.         if (gPersistObj.result == 0)
  1220.         {
  1221.           // All files are finished and publishing succeeded (some images may have failed)
  1222.           try {
  1223.             // Make a new docURI from the "browse location" in case "publish location" was FTP
  1224.             // We need to set document uri before notifying listeners
  1225.             var docUrl = GetDocUrlFromPublishData(gPublishData);
  1226.             SetDocumentURI(GetIOService().newURI(docUrl, editor.documentCharacterSet, null));
  1227.  
  1228.             UpdateWindowTitle();
  1229.  
  1230.             // this should cause notification to listeners that doc has changed
  1231.             editor.resetModificationCount();
  1232.  
  1233.             // Set UI based on whether we're editing a remote or local url
  1234.             SetSaveAndPublishUI(urlstring);
  1235.  
  1236.           } catch (e) {}
  1237.  
  1238.           // Save publishData to prefs
  1239.           if (gPublishData)
  1240.           {
  1241.             if (gPublishData.savePublishData)
  1242.             {
  1243.               // We published successfully, so we can safely
  1244.               //  save docDir and otherDir to prefs
  1245.               gPublishData.saveDirs = true;
  1246.               SavePublishDataToPrefs(gPublishData);
  1247.             }
  1248.             else
  1249.               SavePassword(gPublishData);
  1250.           }
  1251.  
  1252.           // Ask progress dialog to close, but it may not
  1253.           // if user checked checkbox to keep it open
  1254.           gProgressDialog.RequestCloseDialog();
  1255.         }
  1256.         else
  1257.         {
  1258.           // We previously aborted publishing because of error:
  1259.           //   Calling gPersistObj.cancelSave() resulted in a non-zero gPersistObj.result,
  1260.           //   so notify progress dialog we're finished
  1261.           gProgressDialog.SetProgressFinished(null, 0);
  1262.         }
  1263.       }
  1264.     }
  1265.   },
  1266.  
  1267.   onProgressChange : function(aWebProgress, aRequest, aCurSelfProgress,
  1268.                               aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress)
  1269.   {
  1270.     if (!gPersistObj)
  1271.       return;
  1272.  
  1273.     if (gShowDebugOutputProgress)
  1274.     {
  1275.       dump("\n onProgressChange: gPersistObj.result="+gPersistObj.result+"\n");
  1276.       try {
  1277.       var channel = aRequest.QueryInterface(nsIChannel);
  1278.       dump("***** onProgressChange request: " + channel.URI.spec + "\n");
  1279.       }
  1280.       catch (e) {}
  1281.       dump("*****       self:  "+aCurSelfProgress+" / "+aMaxSelfProgress+"\n");
  1282.       dump("*****       total: "+aCurTotalProgress+" / "+aMaxTotalProgress+"\n\n");
  1283.  
  1284.       if (gPersistObj.currentState == gPersistObj.PERSIST_STATE_READY)
  1285.         dump(" Persister is ready to save data\n\n");
  1286.       else if (gPersistObj.currentState == gPersistObj.PERSIST_STATE_SAVING)
  1287.         dump(" Persister is saving data.\n\n");
  1288.       else if (gPersistObj.currentState == gPersistObj.PERSIST_STATE_FINISHED)
  1289.         dump(" PERSISTER HAS FINISHED SAVING DATA\n\n\n");
  1290.     }
  1291.   },
  1292.  
  1293.   onLocationChange : function(aWebProgress, aRequest, aLocation)
  1294.   {
  1295.     if (gShowDebugOutputLocationChange)
  1296.     {
  1297.       dump("***** onLocationChange: "+aLocation.spec+"\n");
  1298.       try {
  1299.         var channel = aRequest.QueryInterface(nsIChannel);
  1300.         dump("*****          request: " + channel.URI.spec + "\n");
  1301.       }
  1302.       catch(e) {}
  1303.     }
  1304.   },
  1305.  
  1306.   onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
  1307.   {
  1308.     if (gShowDebugOutputStatusChange)
  1309.     {
  1310.       dump("***** onStatusChange: "+aMessage+"\n");
  1311.       try {
  1312.         var channel = aRequest.QueryInterface(nsIChannel);
  1313.         dump("*****        request: " + channel.URI.spec + "\n");
  1314.       }
  1315.       catch (e) { dump("          couldn't get request\n"); }
  1316.       
  1317.       DumpDebugStatus(aStatus);
  1318.  
  1319.       if (gPersistObj)
  1320.       {
  1321.         if(gPersistObj.currentState == gPersistObj.PERSIST_STATE_READY)
  1322.           dump(" Persister is ready to save data\n\n");
  1323.         else if(gPersistObj.currentState == gPersistObj.PERSIST_STATE_SAVING)
  1324.           dump(" Persister is saving data.\n\n");
  1325.         else if(gPersistObj.currentState == gPersistObj.PERSIST_STATE_FINISHED)
  1326.           dump(" PERSISTER HAS FINISHED SAVING DATA\n\n\n");
  1327.       }
  1328.     }
  1329.   },
  1330.  
  1331.   onSecurityChange : function(aWebProgress, aRequest, state)
  1332.   {
  1333.     if (gShowDebugOutputSecurityChange)
  1334.     {
  1335.       try {
  1336.         var channel = aRequest.QueryInterface(nsIChannel);
  1337.         dump("***** onSecurityChange request: " + channel.URI.spec + "\n");
  1338.       } catch (e) {}
  1339.     }
  1340.   },
  1341.  
  1342.   QueryInterface : function(aIID)
  1343.   {
  1344.     if (aIID.equals(Components.interfaces.nsIWebProgressListener)
  1345.     || aIID.equals(Components.interfaces.nsISupports)
  1346.     || aIID.equals(Components.interfaces.nsISupportsWeakReference)
  1347.     || aIID.equals(Components.interfaces.nsIPrompt)
  1348.     || aIID.equals(Components.interfaces.nsIAuthPrompt))
  1349.       return this;
  1350.     throw Components.results.NS_NOINTERFACE;
  1351.   },
  1352.  
  1353. // nsIPrompt
  1354.   alert : function(dlgTitle, text)
  1355.   {
  1356.     AlertWithTitle(dlgTitle, text, gProgressDialog ? gProgressDialog : window);
  1357.   },
  1358.   alertCheck : function(dialogTitle, text, checkBoxLabel, checkObj)
  1359.   {
  1360.     AlertWithTitle(dialogTitle, text);
  1361.   },
  1362.   confirm : function(dlgTitle, text)
  1363.   {
  1364.     return ConfirmWithTitle(dlgTitle, text, null, null);
  1365.   },
  1366.   confirmCheck : function(dlgTitle, text, checkBoxLabel, checkObj)
  1367.   {
  1368.     var promptServ = GetPromptService();
  1369.     if (!promptServ)
  1370.       return;
  1371.  
  1372.     promptServ.confirmEx(window, dlgTitle, text, nsIPromptService.STD_OK_CANCEL_BUTTONS,
  1373.                          "", "", "", checkBoxLabel, checkObj);
  1374.   },
  1375.   confirmEx : function(dlgTitle, text, btnFlags, btn0Title, btn1Title, btn2Title, checkBoxLabel, checkVal)
  1376.   {
  1377.     var promptServ = GetPromptService();
  1378.     if (!promptServ)
  1379.      return 0;
  1380.  
  1381.     return promptServ.confirmEx(window, dlgTitle, text, btnFlags,
  1382.                         btn0Title, btn1Title, btn2Title,
  1383.                         checkBoxLabel, checkVal);
  1384.   },
  1385.   prompt : function(dlgTitle, text, inoutText, checkBoxLabel, checkObj)
  1386.   {
  1387.     var promptServ = GetPromptService();
  1388.     if (!promptServ)
  1389.      return false;
  1390.  
  1391.     return promptServ.prompt(window, dlgTitle, text, inoutText, checkBoxLabel, checkObj);
  1392.   },
  1393.   promptPassword : function(dlgTitle, text, pwObj, checkBoxLabel, savePWObj)
  1394.   {
  1395.  
  1396.     var promptServ = GetPromptService();
  1397.     if (!promptServ)
  1398.      return false;
  1399.  
  1400.     var ret = false;
  1401.     try {
  1402.       // Note difference with nsIAuthPrompt::promptPassword, which has 
  1403.       // just "in" savePassword param, while nsIPrompt is "inout"
  1404.       // Initialize with user's previous preference for this site
  1405.       if (gPublishData)
  1406.         savePWObj.value = gPublishData.savePassword;
  1407.  
  1408.       ret = promptServ.promptPassword(gProgressDialog ? gProgressDialog : window,
  1409.                                       dlgTitle, text, pwObj, checkBoxLabel, savePWObj);
  1410.  
  1411.       if (!ret)
  1412.         setTimeout(CancelPublishing, 0);
  1413.  
  1414.       if (ret && gPublishData)
  1415.         UpdateUsernamePasswordFromPrompt(gPublishData, gPublishData.username, pwObj.value, savePWObj.value);
  1416.     } catch(e) {}
  1417.  
  1418.     return ret;
  1419.   },
  1420.   promptUsernameAndPassword : function(dlgTitle, text, userObj, pwObj, checkBoxLabel, savePWObj)
  1421.   {
  1422.     var ret = PromptUsernameAndPassword(dlgTitle, text, savePWObj.value, userObj, pwObj);
  1423.     if (!ret)
  1424.       setTimeout(CancelPublishing, 0);
  1425.  
  1426.     return ret;
  1427.   },
  1428.   select : function(dlgTitle, text, count, selectList, outSelection)
  1429.   {
  1430.     var promptServ = GetPromptService();
  1431.     if (!promptServ)
  1432.       return false;
  1433.  
  1434.     return promptServ.select(window, dlgTitle, text, count, selectList, outSelection);
  1435.   },
  1436.  
  1437. // nsIAuthPrompt
  1438.   prompt : function(dlgTitle, text, pwrealm, savePW, defaultText, result)
  1439.   {
  1440.     var promptServ = GetPromptService();
  1441.     if (!promptServ)
  1442.       return false;
  1443.  
  1444.     var savePWObj = {value:savePW};
  1445.     var ret = promptServ.prompt(gProgressDialog ? gProgressDialog : window,
  1446.                                 dlgTitle, text, defaultText, pwrealm, savePWObj);
  1447.     if (!ret)
  1448.       setTimeout(CancelPublishing, 0);
  1449.     return ret;
  1450.   },
  1451.  
  1452.   promptUsernameAndPassword : function(dlgTitle, text, pwrealm, savePW, userObj, pwObj)
  1453.   {
  1454.     var ret = PromptUsernameAndPassword(dlgTitle, text, savePW, userObj, pwObj);
  1455.     if (!ret)
  1456.       setTimeout(CancelPublishing, 0);
  1457.     return ret;
  1458.   },
  1459.  
  1460.   promptPassword : function(dlgTitle, text, pwrealm, savePW, pwObj)
  1461.   {
  1462.     var ret = false;
  1463.     try {
  1464.       var promptServ = GetPromptService();
  1465.       if (!promptServ)
  1466.         return false;
  1467.  
  1468.       // Note difference with nsIPrompt::promptPassword, which has 
  1469.       // "inout" savePassword param, while nsIAuthPrompt is just "in"
  1470.       // Also nsIAuth doesn't supply "checkBoxLabel"
  1471.       // Initialize with user's previous preference for this site
  1472.       var savePWObj = {value:savePW};
  1473.       // Initialize with user's previous preference for this site
  1474.       if (gPublishData)
  1475.         savePWObj.value = gPublishData.savePassword;
  1476.  
  1477.       ret = promptServ.promptPassword(gProgressDialog ? gProgressDialog : window,
  1478.                                       dlgTitle, text, pwObj, GetString("SavePassword"), savePWObj);
  1479.  
  1480.       if (!ret)
  1481.         setTimeout(CancelPublishing, 0);
  1482.  
  1483.       if (ret && gPublishData)
  1484.         UpdateUsernamePasswordFromPrompt(gPublishData, gPublishData.username, pwObj.value, savePWObj.value);
  1485.     } catch(e) {}
  1486.  
  1487.     return ret;
  1488.   }
  1489. }
  1490.  
  1491. function PromptUsernameAndPassword(dlgTitle, text, savePW, userObj, pwObj)
  1492. {
  1493.   // HTTP prompts us twice even if user Cancels from 1st attempt!
  1494.   // So never put up dialog if there's no publish data
  1495.   if (!gPublishData)
  1496.     return false
  1497.  
  1498.   var ret = false;
  1499.   try {
  1500.     var promptServ = GetPromptService();
  1501.     if (!promptServ)
  1502.       return false;
  1503.  
  1504.     var savePWObj = {value:savePW};
  1505.  
  1506.     // Initialize with user's previous preference for this site
  1507.     if (gPublishData)
  1508.     {
  1509.       // HTTP put uses this dialog if either username or password is bad,
  1510.       //   so prefill username input field with the previous value for modification
  1511.       savePWObj.value = gPublishData.savePassword;
  1512.       if (!userObj.value)
  1513.         userObj.value = gPublishData.username;
  1514.     }
  1515.  
  1516.     ret = promptServ.promptUsernameAndPassword(gProgressDialog ? gProgressDialog : window, 
  1517.                                                dlgTitle, text, userObj, pwObj, 
  1518.                                                GetString("SavePassword"), savePWObj);
  1519.     if (ret && gPublishData)
  1520.       UpdateUsernamePasswordFromPrompt(gPublishData, userObj.value, pwObj.value, savePWObj.value);
  1521.  
  1522.   } catch (e) {}
  1523.  
  1524.   return ret;
  1525. }
  1526.  
  1527. function DumpDebugStatus(aStatus)
  1528. {
  1529.   // see nsError.h and netCore.h and ftpCore.h
  1530.  
  1531.   if (aStatus == kErrorBindingAborted)
  1532.     dump("***** status is NS_BINDING_ABORTED\n");
  1533.   else if (aStatus == kErrorBindingRedirected)
  1534.     dump("***** status is NS_BINDING_REDIRECTED\n");
  1535.   else if (aStatus == 2152398859) // in netCore.h 11
  1536.     dump("***** status is ALREADY_CONNECTED\n");
  1537.   else if (aStatus == 2152398860) // in netCore.h 12
  1538.     dump("***** status is NOT_CONNECTED\n");
  1539.   else if (aStatus == 2152398861) //  in nsISocketTransportService.idl 13
  1540.     dump("***** status is CONNECTION_REFUSED\n");
  1541.   else if (aStatus == 2152398862) // in nsISocketTransportService.idl 14
  1542.     dump("***** status is NET_TIMEOUT\n");
  1543.   else if (aStatus == 2152398863) // in netCore.h 15
  1544.     dump("***** status is IN_PROGRESS\n");
  1545.   else if (aStatus == 2152398864) // 0x804b0010 in netCore.h 16
  1546.     dump("***** status is OFFLINE\n");
  1547.   else if (aStatus == 2152398865) // in netCore.h 17
  1548.     dump("***** status is NO_CONTENT\n");
  1549.   else if (aStatus == 2152398866) // in netCore.h 18
  1550.     dump("***** status is UNKNOWN_PROTOCOL\n");
  1551.   else if (aStatus == 2152398867) // in netCore.h 19
  1552.     dump("***** status is PORT_ACCESS_NOT_ALLOWED\n");
  1553.   else if (aStatus == 2152398868) // in nsISocketTransportService.idl 20
  1554.     dump("***** status is NET_RESET\n");
  1555.   else if (aStatus == 2152398869) // in ftpCore.h 21
  1556.     dump("***** status is FTP_LOGIN\n");
  1557.   else if (aStatus == 2152398870) // in ftpCore.h 22
  1558.     dump("***** status is FTP_CWD\n");
  1559.   else if (aStatus == 2152398871) // in ftpCore.h 23
  1560.     dump("***** status is FTP_PASV\n");
  1561.   else if (aStatus == 2152398872) // in ftpCore.h 24
  1562.     dump("***** status is FTP_PWD\n");
  1563.   else if (aStatus == 2152857601)
  1564.     dump("***** status is UNRECOGNIZED_PATH\n");
  1565.   else if (aStatus == 2152857602)
  1566.     dump("***** status is UNRESOLABLE SYMLINK\n");
  1567.   else if (aStatus == 2152857604)
  1568.     dump("***** status is UNKNOWN_TYPE\n");
  1569.   else if (aStatus == 2152857605)
  1570.     dump("***** status is DESTINATION_NOT_DIR\n");
  1571.   else if (aStatus == 2152857606)
  1572.     dump("***** status is TARGET_DOES_NOT_EXIST\n");
  1573.   else if (aStatus == 2152857608)
  1574.     dump("***** status is ALREADY_EXISTS\n");
  1575.   else if (aStatus == 2152857609)
  1576.     dump("***** status is INVALID_PATH\n");
  1577.   else if (aStatus == 2152857610)
  1578.     dump("***** status is DISK_FULL\n");
  1579.   else if (aStatus == 2152857612)
  1580.     dump("***** status is NOT_DIRECTORY\n");
  1581.   else if (aStatus == 2152857613)
  1582.     dump("***** status is IS_DIRECTORY\n");
  1583.   else if (aStatus == 2152857614)
  1584.     dump("***** status is IS_LOCKED\n");
  1585.   else if (aStatus == 2152857615)
  1586.     dump("***** status is TOO_BIG\n");
  1587.   else if (aStatus == 2152857616)
  1588.     dump("***** status is NO_DEVICE_SPACE\n");
  1589.   else if (aStatus == 2152857617)
  1590.     dump("***** status is NAME_TOO_LONG\n");
  1591.   else if (aStatus == 2152857618) // 80520012
  1592.     dump("***** status is FILE_NOT_FOUND\n");
  1593.   else if (aStatus == 2152857619)
  1594.     dump("***** status is READ_ONLY\n");
  1595.   else if (aStatus == 2152857620)
  1596.     dump("***** status is DIR_NOT_EMPTY\n");
  1597.   else if (aStatus == 2152857621)
  1598.     dump("***** status is ACCESS_DENIED\n");
  1599.   else if (aStatus == 2152398878)
  1600.     dump("***** status is ? (No connection or time out?)\n");
  1601.   else
  1602.     dump("***** status is " + aStatus + "\n");
  1603. }
  1604.  
  1605. // Update any data that the user supplied in a prompt dialog
  1606. function UpdateUsernamePasswordFromPrompt(publishData, username, password, savePassword)
  1607. {
  1608.   if (!publishData)
  1609.     return;
  1610.   
  1611.   // Set flag to save publish data after publishing if it changed in dialog 
  1612.   //  and the "SavePassword" checkbox was checked
  1613.   //  or we already had site data for this site
  1614.   // (Thus we don't automatically create a site until user brings up Publish As dialog)
  1615.   publishData.savePublishData = (gPublishData.username != username || gPublishData.password != password)
  1616.                                 && (savePassword || !publishData.notInSiteData);
  1617.  
  1618.   publishData.username = username;
  1619.   publishData.password = password;
  1620.   publishData.savePassword = savePassword;
  1621. }
  1622.  
  1623. const kSupportedTextMimeTypes =
  1624. [
  1625.   "text/plain",
  1626.   "text/css",
  1627.   "text/rdf",
  1628.   "text/xsl",
  1629.   "text/javascript",
  1630.   "application/x-javascript",
  1631.   "text/xul",
  1632.   "application/vnd.mozilla.xul+xml"
  1633. ];
  1634.  
  1635. function IsSupportedTextMimeType(aMimeType)
  1636. {
  1637.   for (var i = 0; i < kSupportedTextMimeTypes.length; i++)
  1638.   {
  1639.     if (kSupportedTextMimeTypes[i] == aMimeType)
  1640.       return true;
  1641.   }
  1642.   return false;
  1643. }
  1644.  
  1645. // throws an error or returns true if user attempted save; false if user canceled save
  1646. function SaveDocument(aSaveAs, aSaveCopy, aMimeType)
  1647. {
  1648.   var editor = GetCurrentEditor();
  1649.   if (!aMimeType || aMimeType == "" || !editor)
  1650.     throw NS_ERROR_NOT_INITIALIZED;
  1651.  
  1652.   var editorDoc = editor.document;
  1653.   if (!editorDoc)
  1654.     throw NS_ERROR_NOT_INITIALIZED;
  1655.  
  1656.   // if we don't have the right editor type bail (we handle text and html)
  1657.   var editorType = GetCurrentEditorType();
  1658.   if (editorType != "text" && editorType != "html" 
  1659.       && editorType != "htmlmail" && editorType != "textmail")
  1660.     throw NS_ERROR_NOT_IMPLEMENTED;
  1661.  
  1662.   var saveAsTextFile = IsSupportedTextMimeType(aMimeType);
  1663.  
  1664.   // check if the file is to be saved is a format we don't understand; if so, bail
  1665.   if (aMimeType != "text/html" && !saveAsTextFile)
  1666.     throw NS_ERROR_NOT_IMPLEMENTED;
  1667.  
  1668.   if (saveAsTextFile)
  1669.     aMimeType = "text/plain";
  1670.  
  1671.   var urlstring = GetDocumentUrl();
  1672.   var mustShowFileDialog = (aSaveAs || IsUrlAboutBlank(urlstring) || (urlstring == ""));
  1673.  
  1674.   // If editing a remote URL, force SaveAs dialog
  1675.   if (!mustShowFileDialog && GetScheme(urlstring) != "file")
  1676.     mustShowFileDialog = true;
  1677.  
  1678.   var replacing = !aSaveAs;
  1679.   var titleChanged = false;
  1680.   var doUpdateURI = false;
  1681.   var tempLocalFile = null;
  1682.  
  1683.   if (mustShowFileDialog)
  1684.   {
  1685.       try {
  1686.         // Prompt for title if we are saving to HTML
  1687.         if (!saveAsTextFile && (editorType == "html"))
  1688.         {
  1689.           var userContinuing = PromptAndSetTitleIfNone(); // not cancel
  1690.           if (!userContinuing)
  1691.             return false;
  1692.         }
  1693.  
  1694.         var dialogResult = PromptForSaveLocation(saveAsTextFile, editorType, aMimeType, urlstring);
  1695.         if (dialogResult.filepickerClick == nsIFilePicker.returnCancel)
  1696.           return false;
  1697.  
  1698.         replacing = (dialogResult.filepickerClick == nsIFilePicker.returnReplace);
  1699.         urlstring = dialogResult.resultingURIString;
  1700.         tempLocalFile = dialogResult.resultingLocalFile;
  1701.  
  1702.       // update the new URL for the webshell unless we are saving a copy
  1703.       if (!aSaveCopy)
  1704.         doUpdateURI = true;
  1705.    } catch (e) {  return false; }
  1706.   } // mustShowFileDialog
  1707.  
  1708.   var success = true;
  1709.   var ioService;
  1710.   try {
  1711.     // if somehow we didn't get a local file but we did get a uri, 
  1712.     // attempt to create the localfile if it's a "file" url
  1713.     var docURI;
  1714.     if (!tempLocalFile)
  1715.     {
  1716.       ioService = GetIOService();
  1717.       docURI = ioService.newURI(urlstring, editor.documentCharacterSet, null);
  1718.       
  1719.       if (docURI.schemeIs("file"))
  1720.       {
  1721.         var fileHandler = GetFileProtocolHandler();
  1722.         tempLocalFile = fileHandler.getFileFromURLSpec(urlstring).QueryInterface(Components.interfaces.nsILocalFile);
  1723.       }
  1724.     }
  1725.  
  1726.     // this is the location where the related files will go
  1727.     var relatedFilesDir = null;
  1728.     
  1729.     // First check pref for saving associated files
  1730.     var saveAssociatedFiles = false;
  1731.     try {
  1732.       var prefs = GetPrefs();
  1733.       saveAssociatedFiles = prefs.getBoolPref("editor.save_associated_files");
  1734.     } catch (e) {}
  1735.  
  1736.     // Only change links or move files if pref is set 
  1737.     //  and we are saving to a new location
  1738.     if (saveAssociatedFiles && aSaveAs)
  1739.     {
  1740.       try {
  1741.         if (tempLocalFile)
  1742.         {
  1743.           // if we are saving to the same parent directory, don't set relatedFilesDir
  1744.           // grab old location, chop off file
  1745.           // grab new location, chop off file, compare
  1746.           var oldLocation = GetDocumentUrl();
  1747.           var oldLocationLastSlash = oldLocation.lastIndexOf("\/");
  1748.           if (oldLocationLastSlash != -1)
  1749.             oldLocation = oldLocation.slice(0, oldLocationLastSlash);
  1750.  
  1751.           var relatedFilesDirStr = urlstring;
  1752.           var newLocationLastSlash = relatedFilesDirStr.lastIndexOf("\/");
  1753.           if (newLocationLastSlash != -1)
  1754.             relatedFilesDirStr = relatedFilesDirStr.slice(0, newLocationLastSlash);
  1755.           if (oldLocation == relatedFilesDirStr || IsUrlAboutBlank(oldLocation))
  1756.             relatedFilesDir = null;
  1757.           else
  1758.             relatedFilesDir = tempLocalFile.parent;  // this is wrong if parent is the root!
  1759.         }
  1760.         else
  1761.         {
  1762.           var lastSlash = urlstring.lastIndexOf("\/");
  1763.           if (lastSlash != -1)
  1764.           {
  1765.             var relatedFilesDirString = urlstring.slice(0, lastSlash + 1);  // include last slash
  1766.             ioService = GetIOService();
  1767.             relatedFilesDir = ioService.newURI(relatedFilesDirString, editor.documentCharacterSet, null);
  1768.           }
  1769.         }
  1770.       } catch(e) { relatedFilesDir = null; }
  1771.     }
  1772.  
  1773.     var destinationLocation;
  1774.     if (tempLocalFile)
  1775.       destinationLocation = tempLocalFile;
  1776.     else
  1777.       destinationLocation = docURI;
  1778.  
  1779.     success = OutputFileWithPersistAPI(editorDoc, destinationLocation, relatedFilesDir, aMimeType);
  1780.   }
  1781.   catch (e)
  1782.   {
  1783.     success = false;
  1784.   }
  1785.  
  1786.   if (success)
  1787.   {
  1788.     try {
  1789.       if (doUpdateURI)
  1790.       {
  1791.          // If a local file, we must create a new uri from nsILocalFile
  1792.         if (tempLocalFile)
  1793.           docURI = GetFileProtocolHandler().newFileURI(tempLocalFile);
  1794.  
  1795.         // We need to set new document uri before notifying listeners
  1796.         SetDocumentURI(docURI);
  1797.       }
  1798.  
  1799.       // Update window title to show possibly different filename
  1800.       // This also covers problem that after undoing a title change,
  1801.       //   window title loses the extra [filename] part that this adds
  1802.       UpdateWindowTitle();
  1803.  
  1804.       if (!aSaveCopy)
  1805.         editor.resetModificationCount();
  1806.       // this should cause notification to listeners that document has changed
  1807.  
  1808.       // Set UI based on whether we're editing a remote or local url
  1809.       SetSaveAndPublishUI(urlstring);
  1810.     } catch (e) {}
  1811.   }
  1812.   else
  1813.   {
  1814.     var saveDocStr = GetString("SaveDocument");
  1815.     var failedStr = GetString("SaveFileFailed");
  1816.     AlertWithTitle(saveDocStr, failedStr);
  1817.   }
  1818.   return success;
  1819. }
  1820.  
  1821. function SetDocumentURI(uri)
  1822. {
  1823.   try {
  1824.     // XXX WE'LL NEED TO GET "CURRENT" CONTENT FRAME ONCE MULTIPLE EDITORS ARE ALLOWED
  1825.     GetCurrentEditorElement().docShell.setCurrentURI(uri);
  1826.   } catch (e) { dump("SetDocumentURI:\n"+e +"\n"); }
  1827. }
  1828.  
  1829.  
  1830. //-------------------------------  Publishing
  1831. var gPublishData;
  1832. var gProgressDialog;
  1833. var gCommandAfterPublishing = null;
  1834. var gRestoreDocumentSource;
  1835.  
  1836. function Publish(publishData)
  1837. {
  1838.   if (!publishData)
  1839.     return false;
  1840.  
  1841.   // Set data in global for username password requests
  1842.   //  and to do "post saving" actions after monitoring nsIWebProgressListener messages
  1843.   //  and we are sure file transfer was successful
  1844.   gPublishData = publishData;
  1845.  
  1846.   gPublishData.docURI = CreateURIFromPublishData(publishData, true);
  1847.   if (!gPublishData.docURI)
  1848.   {
  1849.     AlertWithTitle(GetString("Publish"), GetString("PublishFailed"));
  1850.     return false;
  1851.   }
  1852.  
  1853.   if (gPublishData.publishOtherFiles)
  1854.     gPublishData.otherFilesURI = CreateURIFromPublishData(publishData, false);
  1855.   else
  1856.     gPublishData.otherFilesURI = null;
  1857.  
  1858.   if (gShowDebugOutputStateChange)
  1859.   {
  1860.     dump("\n *** publishData: PublishUrl="+publishData.publishUrl+", BrowseUrl="+publishData.browseUrl+
  1861.       ", Username="+publishData.username+", Dir="+publishData.docDir+
  1862.       ", Filename="+publishData.filename+"\n");
  1863.     dump(" * gPublishData.docURI.spec w/o pass="+StripPassword(gPublishData.docURI.spec)+", PublishOtherFiles="+gPublishData.publishOtherFiles+"\n");
  1864.   }
  1865.  
  1866.   // XXX Missing username will make FTP fail 
  1867.   // and it won't call us for prompt dialog (bug 132320)
  1868.   // (It does prompt if just password is missing)
  1869.   // So we should do the prompt ourselves before trying to publish
  1870.   if (GetScheme(publishData.publishUrl) == "ftp" && !publishData.username)
  1871.   {
  1872.     var message = GetString("PromptFTPUsernamePassword").replace(/%host%/, GetHost(publishData.publishUrl));
  1873.     var savePWobj = {value:publishData.savePassword};
  1874.     var userObj = {value:publishData.username};
  1875.     var pwObj = {value:publishData.password};
  1876.     if (!PromptUsernameAndPassword(GetString("Prompt"), message, savePWobj, userObj, pwObj))
  1877.       return false; // User canceled out of dialog
  1878.  
  1879.     // Reset data in URI objects
  1880.     gPublishData.docURI.username = publishData.username;
  1881.     gPublishData.docURI.password = publishData.password;
  1882.  
  1883.     if (gPublishData.otherFilesURI)
  1884.     {
  1885.       gPublishData.otherFilesURI.username = publishData.username;
  1886.       gPublishData.otherFilesURI.password = publishData.password;
  1887.     }
  1888.   }
  1889.  
  1890.   try {
  1891.     // We launch dialog as a dependent 
  1892.     // Don't allow editing document!
  1893.     SetDocumentEditable(false);
  1894.  
  1895.     // Start progress monitoring
  1896.     gProgressDialog =
  1897.       window.openDialog("chrome://editor/content/EditorPublishProgress.xul", "_blank",
  1898.                         "chrome,dependent,titlebar", gPublishData, gPersistObj);
  1899.  
  1900.   } catch (e) {}
  1901.  
  1902.   // Network transfer is often too quick for the progress dialog to be initialized
  1903.   //  and we can completely miss messages for quickly-terminated bad URLs,
  1904.   //  so we can't call OutputFileWithPersistAPI right away.
  1905.   // StartPublishing() is called at the end of the dialog's onload method
  1906.   return true;
  1907. }
  1908.  
  1909. function StartPublishing()
  1910. {
  1911.   var editor = GetCurrentEditor();
  1912.   if (editor && gPublishData && gPublishData.docURI && gProgressDialog)
  1913.   {
  1914.     gRestoreDocumentSource = null;
  1915.  
  1916.     // Save backup document since nsIWebBrowserPersist changes image src urls
  1917.     // but we only need to do this if publishing images and other related files
  1918.     if (gPublishData.otherFilesURI)
  1919.     {
  1920.       try {
  1921.         // (256 = Output encoded entities)
  1922.         gRestoreDocumentSource = 
  1923.           editor.outputToString(editor.contentsMIMEType, 256);
  1924.       } catch (e) {}
  1925.     }
  1926.  
  1927.     OutputFileWithPersistAPI(editor.document, 
  1928.                              gPublishData.docURI, gPublishData.otherFilesURI, 
  1929.                              editor.contentsMIMEType);
  1930.   }
  1931. }
  1932.  
  1933. function CancelPublishing()
  1934. {
  1935.   try {
  1936.     gPersistObj.cancelSave();
  1937.     gProgressDialog.SetProgressStatusCancel();
  1938.   } catch (e) {}
  1939.  
  1940.   // If canceling publishing do not do any commands after this    
  1941.   gCommandAfterPublishing = null;
  1942.  
  1943.   if (gProgressDialog)
  1944.   {
  1945.     // Close Progress dialog 
  1946.     // (this will call FinishPublishing())
  1947.     gProgressDialog.CloseDialog();
  1948.   }
  1949.   else
  1950.     FinishPublishing();
  1951. }
  1952.  
  1953. function FinishPublishing()
  1954. {
  1955.   SetDocumentEditable(true);
  1956.   gProgressDialog = null;
  1957.   gPublishData = null;
  1958.   gRestoreDocumentSource = null;
  1959.  
  1960.   if (gCommandAfterPublishing)
  1961.   {
  1962.     // Be sure to null out the global now incase of trouble when executing command
  1963.     var command = gCommandAfterPublishing;
  1964.     gCommandAfterPublishing = null;
  1965.     goDoCommand(command);
  1966.   }
  1967. }
  1968.  
  1969. // Create a nsIURI object filled in with all required publishing info
  1970. function CreateURIFromPublishData(publishData, doDocUri)
  1971. {
  1972.   if (!publishData || !publishData.publishUrl)
  1973.     return null;
  1974.  
  1975.   var URI;
  1976.   try {
  1977.     var spec = publishData.publishUrl;
  1978.     if (doDocUri)
  1979.       spec += FormatDirForPublishing(publishData.docDir) + publishData.filename; 
  1980.     else
  1981.       spec += FormatDirForPublishing(publishData.otherDir);
  1982.  
  1983.     var ioService = GetIOService();
  1984.     URI = ioService.newURI(spec, GetCurrentEditor().documentCharacterSet, null);
  1985.  
  1986.     if (publishData.username)
  1987.       URI.username = publishData.username;
  1988.     if (publishData.password)
  1989.       URI.password = publishData.password;
  1990.   }
  1991.   catch (e) {}
  1992.  
  1993.   return URI;
  1994. }
  1995.  
  1996. // Resolve the correct "http:" document URL when publishing via ftp
  1997. function GetDocUrlFromPublishData(publishData)
  1998. {
  1999.   if (!publishData || !publishData.filename || !publishData.publishUrl)
  2000.     return "";
  2001.  
  2002.   // If user was previously editing an "ftp" url, then keep that as the new scheme
  2003.   var url;
  2004.   var docScheme = GetScheme(GetDocumentUrl());
  2005.  
  2006.   // Always use the "HTTP" address if available
  2007.   // XXX Should we do some more validation here for bad urls???
  2008.   // Let's at least check for a scheme!
  2009.   if (!GetScheme(publishData.browseUrl))
  2010.     url = publishData.publishUrl;
  2011.   else
  2012.     url = publishData.browseUrl;
  2013.  
  2014.   url += FormatDirForPublishing(publishData.docDir) + publishData.filename;
  2015.  
  2016.   if (GetScheme(url) == "ftp")
  2017.     url = InsertUsernameIntoUrl(url, publishData.username);
  2018.  
  2019.   return url;
  2020. }
  2021.  
  2022. function SetSaveAndPublishUI(urlstring)
  2023. {
  2024.   // Be sure enabled state of toolbar buttons are correct
  2025.   goUpdateCommand("cmd_save");
  2026.   goUpdateCommand("cmd_publish");
  2027. }
  2028.  
  2029. function SetDocumentEditable(isDocEditable)
  2030. {
  2031.   var editor = GetCurrentEditor();
  2032.   if (editor && editor.document)
  2033.   {
  2034.     try {
  2035.       var flags = editor.flags;
  2036.       editor.flags = isDocEditable ?  
  2037.             flags &= ~nsIPlaintextEditor.eEditorReadonlyMask :
  2038.             flags | nsIPlaintextEditor.eEditorReadonlyMask;
  2039.     } catch(e) {}
  2040.  
  2041.     // update all commands
  2042.     window.updateCommands("create");
  2043.   }  
  2044. }
  2045.  
  2046. // ****** end of save / publish **********//
  2047.  
  2048. //-----------------------------------------------------------------------------------
  2049. var nsPublishSettingsCommand =
  2050. {
  2051.   isCommandEnabled: function(aCommand, dummy)
  2052.   {
  2053.     return (IsDocumentEditable());
  2054.   },
  2055.  
  2056.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2057.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2058.  
  2059.   doCommand: function(aCommand)
  2060.   {
  2061.     if (GetCurrentEditor())
  2062.     {
  2063.       // Launch Publish Settings dialog
  2064.  
  2065.       window.ok = window.openDialog("chrome://editor/content/EditorPublishSettings.xul","_blank", "chrome,close,titlebar,modal", "");
  2066.       window.content.focus();
  2067.       return window.ok;
  2068.     }
  2069.     return false;
  2070.   }
  2071. }
  2072.  
  2073. //-----------------------------------------------------------------------------------
  2074. var nsRevertCommand =
  2075. {
  2076.   isCommandEnabled: function(aCommand, dummy)
  2077.   {
  2078.     return (IsDocumentEditable() &&
  2079.             IsDocumentModified() &&
  2080.             !IsUrlAboutBlank(GetDocumentUrl()));
  2081.   },
  2082.  
  2083.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2084.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2085.  
  2086.   doCommand: function(aCommand)
  2087.   {
  2088.     // Confirm with the user to abandon current changes
  2089.     var promptService = GetPromptService();
  2090.     if (promptService)
  2091.     {
  2092.       // Put the page title in the message string
  2093.       var title = GetDocumentTitle();
  2094.       if (!title)
  2095.         title = GetString("untitled");
  2096.  
  2097.       var msg = GetString("AbandonChanges").replace(/%title%/,title);
  2098.  
  2099.       var result = promptService.confirmEx(window, GetString("RevertCaption"), msg,
  2100.                                 (promptService.BUTTON_TITLE_REVERT * promptService.BUTTON_POS_0) +
  2101.                                 (promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_1),
  2102.                                 null, null, null, null, {value:0});
  2103.  
  2104.       // Reload page if first button (Revert) was pressed
  2105.       if(result == 0)
  2106.       {
  2107.         CancelHTMLSource();
  2108.         EditorLoadUrl(GetDocumentUrl());
  2109.       }
  2110.     }
  2111.   }
  2112. };
  2113.  
  2114. //-----------------------------------------------------------------------------------
  2115. var nsCloseCommand =
  2116. {
  2117.   isCommandEnabled: function(aCommand, dummy)
  2118.   {
  2119.     return GetCurrentEditor() != null;
  2120.   },
  2121.   
  2122.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2123.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2124.  
  2125.   doCommand: function(aCommand)
  2126.   {
  2127.     CloseWindow();
  2128.   }
  2129. };
  2130.  
  2131. function CloseWindow()
  2132. {
  2133.   // Check to make sure document is saved. "true" means allow "Don't Save" button,
  2134.   //   so user can choose to close without saving
  2135.   if (CheckAndSaveDocument("cmd_close", true)) 
  2136.   {
  2137.     if (window.InsertCharWindow)
  2138.       SwitchInsertCharToAnotherEditorOrClose();
  2139.  
  2140.     try {
  2141.       var basewin = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
  2142.                       .getInterface(Components.interfaces.nsIWebNavigation)
  2143.                       .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
  2144.                       .treeOwner
  2145.                       .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
  2146.                       .getInterface(Components.interfaces.nsIBaseWindow);
  2147.       basewin.destroy();
  2148.     } catch (e) {}
  2149.   }
  2150. }
  2151.  
  2152. //-----------------------------------------------------------------------------------
  2153. var nsOpenRemoteCommand =
  2154. {
  2155.   isCommandEnabled: function(aCommand, dummy)
  2156.   {
  2157.     return true;    // we can always do this
  2158.   },
  2159.  
  2160.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2161.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2162.  
  2163.   doCommand: function(aCommand)
  2164.   {
  2165.       /* The last parameter is the current browser window.
  2166.          Use 0 and the default checkbox will be to load into an editor
  2167.          and loading into existing browser option is removed
  2168.        */
  2169.       window.openDialog( "chrome://communicator/content/openLocation.xul", "_blank", "chrome,modal,titlebar", 0);
  2170.     window.content.focus();
  2171.   }
  2172. };
  2173.  
  2174. //-----------------------------------------------------------------------------------
  2175. var nsPreviewCommand =
  2176. {
  2177.   isCommandEnabled: function(aCommand, dummy)
  2178.   {
  2179.     return (IsDocumentEditable() && 
  2180.             IsHTMLEditor() && 
  2181.             (DocumentHasBeenSaved() || IsDocumentModified()));
  2182.   },
  2183.  
  2184.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2185.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2186.  
  2187.   doCommand: function(aCommand)
  2188.   {
  2189.       // Don't continue if user canceled during prompt for saving
  2190.     // DocumentHasBeenSaved will test if we have a URL and suppress "Don't Save" button if not
  2191.     if (!CheckAndSaveDocument("cmd_preview", DocumentHasBeenSaved()))
  2192.         return;
  2193.  
  2194.     // Check if we saved again just in case?
  2195.       if (DocumentHasBeenSaved())
  2196.     {
  2197.       var browser;
  2198.       try {
  2199.         // Find a browser with this URL
  2200.         var windowManager = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService();
  2201.         var windowManagerInterface = windowManager.QueryInterface(Components.interfaces.nsIWindowMediator);
  2202.         var enumerator = windowManagerInterface.getEnumerator("navigator:browser");
  2203.  
  2204.         var documentURI = GetDocumentUrl();
  2205.         while ( enumerator.hasMoreElements() )
  2206.         {
  2207.           browser = enumerator.getNext().QueryInterface(Components.interfaces.nsIDOMWindowInternal);
  2208.           if ( browser && (documentURI == browser.getBrowser().currentURI.spec))
  2209.             break;
  2210.  
  2211.           browser = null;
  2212.         }
  2213.       }
  2214.       catch (ex) {}
  2215.  
  2216.       // If none found, open a new browser
  2217.       if (!browser)
  2218.       {
  2219.         browser = window.openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no", documentURI);
  2220.       }
  2221.       else
  2222.       {
  2223.         try {
  2224.           browser.BrowserReloadSkipCache();
  2225.           browser.focus();
  2226.         } catch (ex) {}
  2227.       }
  2228.     }
  2229.   }
  2230. };
  2231.  
  2232. //-----------------------------------------------------------------------------------
  2233. var nsSendPageCommand =
  2234. {
  2235.   isCommandEnabled: function(aCommand, dummy)
  2236.   {
  2237.     return (IsDocumentEditable() &&
  2238.             (DocumentHasBeenSaved() || IsDocumentModified()));
  2239.   },
  2240.  
  2241.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2242.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2243.  
  2244.   doCommand: function(aCommand)
  2245.   {
  2246.     // Don't continue if user canceled during prompt for saving
  2247.     // DocumentHasBeenSaved will test if we have a URL and suppress "Don't Save" button if not
  2248.     if (!CheckAndSaveDocument("cmd_editSendPage", DocumentHasBeenSaved()))
  2249.         return;
  2250.  
  2251.     // Check if we saved again just in case?
  2252.     if (DocumentHasBeenSaved())
  2253.     {
  2254.       // Launch Messenger Composer window with current page as contents
  2255.       try
  2256.       {
  2257.         openComposeWindow(GetDocumentUrl(), GetDocumentTitle());        
  2258.       } catch (ex) { dump("Cannot Send Page: " + ex + "\n"); }
  2259.     }
  2260.   }
  2261. };
  2262.  
  2263. //-----------------------------------------------------------------------------------
  2264. var nsPrintCommand =
  2265. {
  2266.   isCommandEnabled: function(aCommand, dummy)
  2267.   {
  2268.     return true;    // we can always do this
  2269.   },
  2270.  
  2271.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2272.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2273.  
  2274.   doCommand: function(aCommand)
  2275.   {
  2276.     // In editor.js
  2277.     FinishHTMLSource();
  2278.     try {
  2279.       NSPrint();
  2280.     } catch (e) {}
  2281.   }
  2282. };
  2283.  
  2284. //-----------------------------------------------------------------------------------
  2285. var nsPrintSetupCommand =
  2286. {
  2287.   isCommandEnabled: function(aCommand, dummy)
  2288.   {
  2289.     return true;    // we can always do this
  2290.   },
  2291.  
  2292.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2293.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2294.  
  2295.   doCommand: function(aCommand)
  2296.   {
  2297.     // In editor.js
  2298.     FinishHTMLSource();
  2299.     goPageSetup();
  2300.   }
  2301. };
  2302.  
  2303. //-----------------------------------------------------------------------------------
  2304. var nsQuitCommand =
  2305. {
  2306.   isCommandEnabled: function(aCommand, dummy)
  2307.   {
  2308.     return true;    // we can always do this
  2309.   },
  2310.  
  2311.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2312.   doCommandParams: function(aCommand, aParams, aRefCon) {}
  2313.  
  2314.   /* The doCommand is not used, since cmd_quit's oncommand="goQuitApplication()" in platformCommunicatorOverlay.xul
  2315.   doCommand: function(aCommand)
  2316.   {
  2317.     // In editor.js
  2318.     FinishHTMLSource();
  2319.     goQuitApplication();
  2320.   }
  2321.   */
  2322. };
  2323.  
  2324. //-----------------------------------------------------------------------------------
  2325. var nsFindCommand =
  2326. {
  2327.   isCommandEnabled: function(aCommand, dummy)
  2328.   {
  2329.     return GetCurrentEditor() != null;
  2330.   },
  2331.  
  2332.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2333.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2334.  
  2335.   doCommand: function(aCommand)
  2336.   {
  2337.     try {
  2338.       window.openDialog("chrome://editor/content/EdReplace.xul", "_blank",
  2339.                         "chrome,dependent,titlebar", "");
  2340.     }
  2341.     catch(ex) {
  2342.       dump("*** Exception: couldn't open Replace Dialog\n");
  2343.     }
  2344.     window.content.focus();
  2345.   }
  2346. };
  2347.  
  2348. //-----------------------------------------------------------------------------------
  2349.  
  2350. function nsFindAgainCommand(isFindPrev)
  2351. {
  2352.   this.isFindPrev = isFindPrev;
  2353. }
  2354.  
  2355. nsFindAgainCommand.prototype =
  2356. {
  2357.   isCommandEnabled: function(aCommand, dummy)
  2358.   {
  2359.     // we can only do this if the search pattern is non-empty. Not sure how
  2360.     // to get that from here
  2361.     return (GetCurrentEditor() != null && !IsInHTMLSourceMode());
  2362.   },
  2363.  
  2364.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2365.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2366.  
  2367.   doCommand: function(aCommand)
  2368.   {
  2369.     try {
  2370.       var findInst = GetCurrentEditorElement().webBrowserFind;
  2371.       var findService = Components.classes["@mozilla.org/find/find_service;1"]
  2372.                              .getService(Components.interfaces.nsIFindService);    
  2373.       findInst.findBackwards = findService.findBackwards ^ this.isFindPrev;
  2374.       findInst.findNext();
  2375.       // reset to what it was in dialog, otherwise dialog setting can get reversed
  2376.       findInst.findBackwards = findService.findBackwards; 
  2377.     }
  2378.     catch (ex) {}
  2379.   }
  2380. };
  2381.  
  2382. //-----------------------------------------------------------------------------------
  2383. var nsSpellingCommand =
  2384. {
  2385.   isCommandEnabled: function(aCommand, dummy)
  2386.   {
  2387.     return (IsDocumentEditable() && 
  2388.             !IsInHTMLSourceMode() && IsSpellCheckerInstalled());
  2389.   },
  2390.  
  2391.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2392.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2393.  
  2394.   doCommand: function(aCommand)
  2395.   {
  2396.     window.cancelSendMessage = false;
  2397.     try {
  2398.       var skipBlockQuotes = (window.document.firstChild.getAttribute("windowtype") == "msgcompose");
  2399.       window.openDialog("chrome://editor/content/EdSpellCheck.xul", "_blank",
  2400.               "chrome,close,titlebar,modal", false, skipBlockQuotes);
  2401.     }
  2402.     catch(ex) {}
  2403.     window.content.focus();
  2404.   }
  2405. };
  2406.  
  2407. // Validate using http://validator.w3.org/file-upload.html
  2408. var URL2Validate;
  2409. var nsValidateCommand =
  2410. {
  2411.   isCommandEnabled: function(aCommand, dummy)
  2412.   {
  2413.     return GetCurrentEditor() != null;
  2414.   },
  2415.  
  2416.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2417.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2418.  
  2419.   doCommand: function(aCommand)
  2420.   {
  2421.     // If the document hasn't been modified,
  2422.     // then just validate the current url.
  2423.     if (IsDocumentModified() || gHTMLSourceChanged)
  2424.     {
  2425.       if (!CheckAndSaveDocument("cmd_validate", false))
  2426.         return;
  2427.  
  2428.       // Check if we saved again just in case?
  2429.       if (!DocumentHasBeenSaved())    // user hit cancel?
  2430.         return;
  2431.     }
  2432.  
  2433.     URL2Validate = GetDocumentUrl();
  2434.     // See if it's a file:
  2435.     var ifile;
  2436.     try {
  2437.       var fileHandler = GetFileProtocolHandler();
  2438.       ifile = fileHandler.getFileFromURLSpec(URL2Validate);
  2439.       // nsIFile throws an exception if it's not a file url
  2440.     } catch (e) { ifile = null; }
  2441.     if (ifile)
  2442.     {
  2443.       URL2Validate = ifile.path;
  2444.       var vwin = window.open("http://validator.w3.org/file-upload.html",
  2445.                              "EditorValidate");
  2446.       // Window loads asynchronously, so pass control to the load listener:
  2447.       vwin.addEventListener("load", this.validateFilePageLoaded, false);
  2448.     }
  2449.     else
  2450.     {
  2451.       var vwin2 = window.open("http://validator.w3.org/check?uri="
  2452.                               + URL2Validate
  2453.                               + "&doctype=Inline",
  2454.                               "EditorValidate");
  2455.       // This does the validation, no need to wait for page loaded.
  2456.     }
  2457.   },
  2458.   validateFilePageLoaded: function(event)
  2459.   {
  2460.     event.target.forms[0].uploaded_file.value = URL2Validate;
  2461.   }
  2462. };
  2463.  
  2464. var nsCheckLinksCommand =
  2465. {
  2466.   isCommandEnabled: function(aCommand, dummy)
  2467.   {
  2468.     return (IsDocumentEditable());
  2469.   },
  2470.  
  2471.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2472.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2473.  
  2474.   doCommand: function(aCommand)
  2475.   {
  2476.     window.openDialog("chrome://editor/content/EdLinkChecker.xul","_blank", "chrome,close,titlebar,modal");
  2477.     window.content.focus();
  2478.   }
  2479. };
  2480.  
  2481. //-----------------------------------------------------------------------------------
  2482. var nsFormCommand =
  2483. {
  2484.   isCommandEnabled: function(aCommand, dummy)
  2485.   {
  2486.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  2487.   },
  2488.  
  2489.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2490.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2491.  
  2492.   doCommand: function(aCommand)
  2493.   {
  2494.     window.openDialog("chrome://editor/content/EdFormProps.xul", "_blank", "chrome,close,titlebar,modal");
  2495.     window.content.focus();
  2496.   }
  2497. };
  2498.  
  2499. //-----------------------------------------------------------------------------------
  2500. var nsInputTagCommand =
  2501. {
  2502.   isCommandEnabled: function(aCommand, dummy)
  2503.   {
  2504.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  2505.   },
  2506.  
  2507.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2508.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2509.  
  2510.   doCommand: function(aCommand)
  2511.   {
  2512.     window.openDialog("chrome://editor/content/EdInputProps.xul", "_blank", "chrome,close,titlebar,modal");
  2513.     window.content.focus();
  2514.   }
  2515. };
  2516.  
  2517. //-----------------------------------------------------------------------------------
  2518. var nsInputImageCommand =
  2519. {
  2520.   isCommandEnabled: function(aCommand, dummy)
  2521.   {
  2522.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  2523.   },
  2524.  
  2525.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2526.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2527.  
  2528.   doCommand: function(aCommand)
  2529.   {
  2530.     window.openDialog("chrome://editor/content/EdInputImage.xul", "_blank", "chrome,close,titlebar,modal");
  2531.     window.content.focus();
  2532.   }
  2533. };
  2534.  
  2535. //-----------------------------------------------------------------------------------
  2536. var nsTextAreaCommand =
  2537. {
  2538.   isCommandEnabled: function(aCommand, dummy)
  2539.   {
  2540.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  2541.   },
  2542.  
  2543.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2544.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2545.  
  2546.   doCommand: function(aCommand)
  2547.   {
  2548.     window.openDialog("chrome://editor/content/EdTextAreaProps.xul", "_blank", "chrome,close,titlebar,modal");
  2549.     window.content.focus();
  2550.   }
  2551. };
  2552.  
  2553. //-----------------------------------------------------------------------------------
  2554. var nsSelectCommand =
  2555. {
  2556.   isCommandEnabled: function(aCommand, dummy)
  2557.   {
  2558.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  2559.   },
  2560.  
  2561.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2562.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2563.  
  2564.   doCommand: function(aCommand)
  2565.   {
  2566.     window.openDialog("chrome://editor/content/EdSelectProps.xul", "_blank", "chrome,close,titlebar,modal");
  2567.     window.content.focus();
  2568.   }
  2569. };
  2570.  
  2571. //-----------------------------------------------------------------------------------
  2572. var nsButtonCommand =
  2573. {
  2574.   isCommandEnabled: function(aCommand, dummy)
  2575.   {
  2576.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  2577.   },
  2578.  
  2579.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2580.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2581.  
  2582.   doCommand: function(aCommand)
  2583.   {
  2584.     window.openDialog("chrome://editor/content/EdButtonProps.xul", "_blank", "chrome,close,titlebar,modal");
  2585.     window.content.focus();
  2586.   }
  2587. };
  2588.  
  2589. //-----------------------------------------------------------------------------------
  2590. var nsLabelCommand =
  2591. {
  2592.   isCommandEnabled: function(aCommand, dummy)
  2593.   {
  2594.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  2595.   },
  2596.  
  2597.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2598.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2599.  
  2600.   doCommand: function(aCommand)
  2601.   {
  2602.     var tagName = "label";
  2603.     try {
  2604.       var editor = GetCurrentEditor();
  2605.       // Find selected label or if start/end of selection is in label 
  2606.       var labelElement = editor.getSelectedElement(tagName);
  2607.       if (!labelElement)
  2608.         labelElement = editor.getElementOrParentByTagName(tagName, editor.selection.anchorNode);
  2609.       if (!labelElement)
  2610.         labelElement = editor.getElementOrParentByTagName(tagName, editor.selection.focusNode);
  2611.       if (labelElement) {
  2612.         // We only open the dialog for an existing label
  2613.         window.openDialog("chrome://editor/content/EdLabelProps.xul", "_blank", "chrome,close,titlebar,modal", labelElement);
  2614.         window.content.focus();
  2615.       } else {
  2616.         EditorSetTextProperty(tagName, "", "");
  2617.       }
  2618.     } catch (e) {}
  2619.   }
  2620. };
  2621.  
  2622. //-----------------------------------------------------------------------------------
  2623. var nsFieldSetCommand =
  2624. {
  2625.   isCommandEnabled: function(aCommand, dummy)
  2626.   {
  2627.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  2628.   },
  2629.  
  2630.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2631.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2632.  
  2633.   doCommand: function(aCommand)
  2634.   {
  2635.     window.openDialog("chrome://editor/content/EdFieldSetProps.xul", "_blank", "chrome,close,titlebar,modal");
  2636.     window.content.focus();
  2637.   }
  2638. };
  2639.  
  2640. //-----------------------------------------------------------------------------------
  2641. var nsIsIndexCommand =
  2642. {
  2643.   isCommandEnabled: function(aCommand, dummy)
  2644.   {
  2645.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  2646.   },
  2647.  
  2648.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2649.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2650.  
  2651.   doCommand: function(aCommand)
  2652.   {
  2653.     try {
  2654.       var editor = GetCurrentEditor();
  2655.       var isindexElement = editor.createElementWithDefaults("isindex");
  2656.       isindexElement.setAttribute("prompt", editor.outputToString("text/plain", 1)); // OutputSelectionOnly
  2657.       editor.insertElementAtSelection(isindexElement, true);
  2658.     } catch (e) {}
  2659.   }
  2660. };
  2661.  
  2662. //-----------------------------------------------------------------------------------
  2663. var nsImageCommand =
  2664. {
  2665.   isCommandEnabled: function(aCommand, dummy)
  2666.   {
  2667.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  2668.   },
  2669.  
  2670.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2671.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2672.  
  2673.   doCommand: function(aCommand)
  2674.   {
  2675.     window.openDialog("chrome://editor/content/EdImageProps.xul","_blank", "chrome,close,titlebar,modal");
  2676.     window.content.focus();
  2677.   }
  2678. };
  2679.  
  2680. //-----------------------------------------------------------------------------------
  2681. var nsHLineCommand =
  2682. {
  2683.   isCommandEnabled: function(aCommand, dummy)
  2684.   {
  2685.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  2686.   },
  2687.  
  2688.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2689.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2690.  
  2691.   doCommand: function(aCommand)
  2692.   {
  2693.     // Inserting an HLine is different in that we don't use properties dialog
  2694.     //  unless we are editing an existing line's attributes
  2695.     //  We get the last-used attributes from the prefs and insert immediately
  2696.  
  2697.     var tagName = "hr";
  2698.     var editor = GetCurrentEditor();
  2699.       
  2700.     var hLine;
  2701.     try {
  2702.       hLine = editor.getSelectedElement(tagName);
  2703.     } catch (e) {return;}
  2704.  
  2705.     if (hLine)
  2706.     {
  2707.       // We only open the dialog for an existing HRule
  2708.       window.openDialog("chrome://editor/content/EdHLineProps.xul", "_blank", "chrome,close,titlebar,modal");
  2709.       window.content.focus();
  2710.     } 
  2711.     else
  2712.     {
  2713.       try {
  2714.         hLine = editor.createElementWithDefaults(tagName);
  2715.  
  2716.         // We change the default attributes to those saved in the user prefs
  2717.         var prefs = GetPrefs();
  2718.         var align = prefs.getIntPref("editor.hrule.align");
  2719.         if (align == 0)
  2720.           editor.setAttributeOrEquivalent(hLine, "align", "left", true);
  2721.         else if (align == 2)
  2722.           editor.setAttributeOrEquivalent(hLine, "align", "right", true);
  2723.  
  2724.         //Note: Default is center (don't write attribute)
  2725.   
  2726.         var width = prefs.getIntPref("editor.hrule.width");
  2727.         var percent = prefs.getBoolPref("editor.hrule.width_percent");
  2728.         if (percent)
  2729.           width = width +"%";
  2730.  
  2731.         editor.setAttributeOrEquivalent(hLine, "width", width, true);
  2732.  
  2733.         var height = prefs.getIntPref("editor.hrule.height");
  2734.         editor.setAttributeOrEquivalent(hLine, "size", String(height), true);
  2735.  
  2736.         var shading = prefs.getBoolPref("editor.hrule.shading");
  2737.         if (shading)
  2738.           hLine.removeAttribute("noshade");
  2739.         else
  2740.           hLine.setAttribute("noshade", "noshade");
  2741.  
  2742.         editor.insertElementAtSelection(hLine, true);
  2743.  
  2744.       } catch (e) {}
  2745.     }
  2746.   }
  2747. };
  2748.  
  2749. //-----------------------------------------------------------------------------------
  2750. var nsLinkCommand =
  2751. {
  2752.   isCommandEnabled: function(aCommand, dummy)
  2753.   {
  2754.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  2755.   },
  2756.  
  2757.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2758.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2759.  
  2760.   doCommand: function(aCommand)
  2761.   {
  2762.     // If selected element is an image, launch that dialog instead 
  2763.     // since last tab panel handles link around an image
  2764.     var element = GetObjectForProperties();
  2765.     if (element && element.nodeName.toLowerCase() == "img")
  2766.       window.openDialog("chrome://editor/content/EdImageProps.xul","_blank", "chrome,close,titlebar,modal", null, true);
  2767.     else
  2768.       window.openDialog("chrome://editor/content/EdLinkProps.xul","_blank", "chrome,close,titlebar,modal");
  2769.     window.content.focus();
  2770.   }
  2771. };
  2772.  
  2773. //-----------------------------------------------------------------------------------
  2774. var nsAnchorCommand =
  2775. {
  2776.   isCommandEnabled: function(aCommand, dummy)
  2777.   {
  2778.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  2779.   },
  2780.  
  2781.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2782.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2783.  
  2784.   doCommand: function(aCommand)
  2785.   {
  2786.     window.openDialog("chrome://editor/content/EdNamedAnchorProps.xul", "_blank", "chrome,close,titlebar,modal", "");
  2787.     window.content.focus();
  2788.   }
  2789. };
  2790.  
  2791. //-----------------------------------------------------------------------------------
  2792. var nsInsertHTMLWithDialogCommand =
  2793. {
  2794.   isCommandEnabled: function(aCommand, dummy)
  2795.   {
  2796.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  2797.   },
  2798.  
  2799.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2800.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2801.  
  2802.   doCommand: function(aCommand)
  2803.   {
  2804.     window.openDialog("chrome://editor/content/EdInsSrc.xul","_blank", "chrome,close,titlebar,modal,resizable", "");
  2805.     window.content.focus();
  2806.   }
  2807. };
  2808.  
  2809. //-----------------------------------------------------------------------------------
  2810. var nsInsertCharsCommand =
  2811. {
  2812.   isCommandEnabled: function(aCommand, dummy)
  2813.   {
  2814.     return (IsDocumentEditable());
  2815.   },
  2816.  
  2817.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2818.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2819.  
  2820.   doCommand: function(aCommand)
  2821.   {
  2822.     EditorFindOrCreateInsertCharWindow();
  2823.   }
  2824. };
  2825.  
  2826. //-----------------------------------------------------------------------------------
  2827. var nsInsertBreakCommand =
  2828. {
  2829.   isCommandEnabled: function(aCommand, dummy)
  2830.   {
  2831.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  2832.   },
  2833.  
  2834.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2835.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2836.  
  2837.   doCommand: function(aCommand)
  2838.   {
  2839.     try {
  2840.       GetCurrentEditor().insertHTML("<br>");
  2841.     } catch (e) {}
  2842.   }
  2843. };
  2844.  
  2845. //-----------------------------------------------------------------------------------
  2846. var nsInsertBreakAllCommand =
  2847. {
  2848.   isCommandEnabled: function(aCommand, dummy)
  2849.   {
  2850.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  2851.   },
  2852.  
  2853.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2854.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2855.  
  2856.   doCommand: function(aCommand)
  2857.   {
  2858.     try {
  2859.       GetCurrentEditor().insertHTML("<br clear='all'>");
  2860.     } catch (e) {}
  2861.   }
  2862. };
  2863.  
  2864. //-----------------------------------------------------------------------------------
  2865. var nsListPropertiesCommand =
  2866. {
  2867.   isCommandEnabled: function(aCommand, dummy)
  2868.   {
  2869.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  2870.   },
  2871.  
  2872.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2873.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2874.  
  2875.   doCommand: function(aCommand)
  2876.   {
  2877.     window.openDialog("chrome://editor/content/EdListProps.xul","_blank", "chrome,close,titlebar,modal");
  2878.     window.content.focus();
  2879.   }
  2880. };
  2881.  
  2882.  
  2883. //-----------------------------------------------------------------------------------
  2884. var nsPagePropertiesCommand =
  2885. {
  2886.   isCommandEnabled: function(aCommand, dummy)
  2887.   {
  2888.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  2889.   },
  2890.  
  2891.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2892.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2893.  
  2894.   doCommand: function(aCommand)
  2895.   {
  2896.     var oldTitle = GetDocumentTitle();
  2897.     window.openDialog("chrome://editor/content/EdPageProps.xul","_blank", "chrome,close,titlebar,modal", "");
  2898.  
  2899.     // Update main window title and 
  2900.     // recent menu data in prefs if doc title changed
  2901.     if (GetDocumentTitle() != oldTitle)
  2902.       UpdateWindowTitle();
  2903.  
  2904.     window.content.focus();
  2905.   }
  2906. };
  2907.  
  2908. //-----------------------------------------------------------------------------------
  2909. var nsObjectPropertiesCommand =
  2910. {
  2911.   isCommandEnabled: function(aCommand, dummy)
  2912.   {
  2913.     var isEnabled = false;
  2914.     if (IsDocumentEditable() && IsEditingRenderedHTML())
  2915.     {
  2916.       isEnabled = (GetObjectForProperties() != null ||
  2917.                    GetCurrentEditor().getSelectedElement("href") != null);
  2918.     }
  2919.     return isEnabled;
  2920.   },
  2921.  
  2922.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  2923.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  2924.  
  2925.   doCommand: function(aCommand)
  2926.   {
  2927.     // Launch Object properties for appropriate selected element 
  2928.     var element = GetObjectForProperties();
  2929.     if (element)
  2930.     {
  2931.       var name = element.nodeName.toLowerCase();
  2932.       switch (name)
  2933.       {
  2934.         case 'img':
  2935.           goDoCommand("cmd_image");
  2936.           break;
  2937.         case 'hr':
  2938.           goDoCommand("cmd_hline");
  2939.           break;
  2940.         case 'form':
  2941.           goDoCommand("cmd_form");
  2942.           break;
  2943.         case 'input':
  2944.           var type = element.getAttribute("type");
  2945.           if (type && type.toLowerCase() == "image")
  2946.             goDoCommand("cmd_inputimage");
  2947.           else
  2948.             goDoCommand("cmd_inputtag");
  2949.           break;
  2950.         case 'textarea':
  2951.           goDoCommand("cmd_textarea");
  2952.           break;
  2953.         case 'select':
  2954.           goDoCommand("cmd_select");
  2955.           break;
  2956.         case 'button':
  2957.           goDoCommand("cmd_button");
  2958.           break;
  2959.         case 'label':
  2960.           goDoCommand("cmd_label");
  2961.           break;
  2962.         case 'fieldset':
  2963.           goDoCommand("cmd_fieldset");
  2964.           break;
  2965.         case 'table':
  2966.           EditorInsertOrEditTable(false);
  2967.           break;
  2968.         case 'td':
  2969.         case 'th':
  2970.           EditorTableCellProperties();
  2971.           break;
  2972.         case 'ol':
  2973.         case 'ul':
  2974.         case 'dl':
  2975.         case 'li':
  2976.           goDoCommand("cmd_listProperties");
  2977.           break;
  2978.         case 'a':
  2979.           if (element.name)
  2980.           {
  2981.             goDoCommand("cmd_anchor");
  2982.           }
  2983.           else if(element.href)
  2984.           {
  2985.             goDoCommand("cmd_link");
  2986.           }
  2987.           break;
  2988.         default:
  2989.           doAdvancedProperties(element);
  2990.           break;
  2991.       }
  2992.     } else {
  2993.       // We get a partially-selected link if asked for specifically
  2994.       try {
  2995.         element = GetCurrentEditor().getSelectedElement("href");
  2996.       } catch (e) {}
  2997.       if (element)
  2998.         goDoCommand("cmd_link");
  2999.     }
  3000.     window.content.focus();
  3001.   }
  3002. };
  3003.  
  3004.  
  3005. //-----------------------------------------------------------------------------------
  3006. var nsSetSmiley =
  3007. {
  3008.   isCommandEnabled: function(aCommand, dummy)
  3009.   {
  3010.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  3011.   },
  3012.  
  3013.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3014.   doCommandParams: function(aCommand, aParams, aRefCon)
  3015.   {
  3016.     var smileyCode = aParams.getCStringValue("state_attribute");
  3017.     
  3018.     var strSml;
  3019.     switch(smileyCode)
  3020.     {
  3021.         case ":-)": strSml="s1"; 
  3022.         break;
  3023.         case ":-(": strSml="s2";
  3024.         break;
  3025.         case ";-)": strSml="s3";
  3026.         break;
  3027.         case ":-P": strSml="s4";
  3028.         break;
  3029.         case ":-D": strSml="s5";
  3030.         break;
  3031.         case ":-[": strSml="s6";
  3032.         break;
  3033.         case ":-\\": strSml="s7";
  3034.         break;
  3035.         default:    strSml="";
  3036.         break;
  3037.     }
  3038.  
  3039.     try 
  3040.     {
  3041.       var editor = GetCurrentEditor();
  3042.       var selection = editor.selection;
  3043.       var extElement = editor.createElementWithDefaults("span");
  3044.       extElement.setAttribute("class", "moz-smiley-" + strSml);
  3045.  
  3046.       var intElement = editor.createElementWithDefaults("span");
  3047.       if (!intElement)
  3048.         return;
  3049.  
  3050.       //just for mailnews, because of the way it removes HTML
  3051.       var smileButMenu = document.getElementById('smileButtonMenu');      
  3052.       if (smileButMenu.getAttribute("padwithspace"))
  3053.          smileyCode = " " + smileyCode + " ";
  3054.  
  3055.       var txtElement =  editor.document.createTextNode(smileyCode);
  3056.       if (!txtElement)
  3057.         return;
  3058.  
  3059.       intElement.appendChild (txtElement);
  3060.       extElement.appendChild (intElement);
  3061.  
  3062.  
  3063.       editor.insertElementAtSelection(extElement,true);
  3064.       window.content.focus();        
  3065.  
  3066.     } 
  3067.     catch (e) 
  3068.     {
  3069.         dump("Exception occured in smiley InsertElementAtSelection\n");
  3070.     }
  3071.   },
  3072.   // This is now deprecated in favor of "doCommandParams"
  3073.   doCommand: function(aCommand) {}
  3074. };
  3075.  
  3076.  
  3077. function doAdvancedProperties(element)
  3078. {
  3079.   if (element)
  3080.   {
  3081.     window.openDialog("chrome://editor/content/EdAdvancedEdit.xul", "_blank", "chrome,close,titlebar,modal,resizable=yes", "", element);
  3082.     window.content.focus();
  3083.   }
  3084. }
  3085.  
  3086. var nsAdvancedPropertiesCommand =
  3087. {
  3088.   isCommandEnabled: function(aCommand, dummy)
  3089.   {
  3090.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  3091.   },
  3092.  
  3093.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3094.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3095.  
  3096.   doCommand: function(aCommand)
  3097.   {
  3098.     // Launch AdvancedEdit dialog for the selected element
  3099.     try {
  3100.       var element = GetCurrentEditor().getSelectedElement("");
  3101.       doAdvancedProperties(element);
  3102.     } catch (e) {}
  3103.   }
  3104. };
  3105.  
  3106. //-----------------------------------------------------------------------------------
  3107. var nsColorPropertiesCommand =
  3108. {
  3109.   isCommandEnabled: function(aCommand, dummy)
  3110.   {
  3111.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  3112.   },
  3113.  
  3114.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3115.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3116.  
  3117.   doCommand: function(aCommand)
  3118.   {
  3119.     window.openDialog("chrome://editor/content/EdColorProps.xul","_blank", "chrome,close,titlebar,modal", ""); 
  3120.     UpdateDefaultColors(); 
  3121.     window.content.focus();
  3122.   }
  3123. };
  3124.  
  3125. //-----------------------------------------------------------------------------------
  3126. var nsRemoveLinksCommand =
  3127. {
  3128.   isCommandEnabled: function(aCommand, dummy)
  3129.   {
  3130.     // We could see if there's any link in selection, but it doesn't seem worth the work!
  3131.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  3132.   },
  3133.  
  3134.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3135.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3136.  
  3137.   doCommand: function(aCommand)
  3138.   {
  3139.     EditorRemoveTextProperty("href", "");
  3140.     window.content.focus();
  3141.   }
  3142. };
  3143.  
  3144.  
  3145. //-----------------------------------------------------------------------------------
  3146. var nsRemoveNamedAnchorsCommand =
  3147. {
  3148.   isCommandEnabled: function(aCommand, dummy)
  3149.   {
  3150.     // We could see if there's any link in selection, but it doesn't seem worth the work!
  3151.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  3152.   },
  3153.  
  3154.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3155.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3156.  
  3157.   doCommand: function(aCommand)
  3158.   {
  3159.     EditorRemoveTextProperty("name", "");
  3160.     window.content.focus();
  3161.   }
  3162. };
  3163.  
  3164.  
  3165. //-----------------------------------------------------------------------------------
  3166. var nsEditLinkCommand =
  3167. {
  3168.   isCommandEnabled: function(aCommand, dummy)
  3169.   {
  3170.     // Not really used -- this command is only in context menu, and we do enabling there
  3171.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  3172.   },
  3173.  
  3174.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3175.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3176.  
  3177.   doCommand: function(aCommand)
  3178.   {
  3179.     try {
  3180.       var element = GetCurrentEditor().getSelectedElement("href");
  3181.       if (element)
  3182.         editPage(element.href, window, false);
  3183.     } catch (e) {}
  3184.     window.content.focus();
  3185.   }
  3186. };
  3187.  
  3188.  
  3189. //-----------------------------------------------------------------------------------
  3190. var nsNormalModeCommand =
  3191. {
  3192.   isCommandEnabled: function(aCommand, dummy)
  3193.   {
  3194.     return IsHTMLEditor() && IsDocumentEditable();
  3195.   },
  3196.  
  3197.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3198.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3199.  
  3200.   doCommand: function(aCommand)
  3201.   {
  3202.     SetEditMode(kDisplayModeNormal);
  3203.   }
  3204. };
  3205.  
  3206. var nsAllTagsModeCommand =
  3207. {
  3208.   isCommandEnabled: function(aCommand, dummy)
  3209.   {
  3210.     return (IsDocumentEditable() && IsHTMLEditor());
  3211.   },
  3212.  
  3213.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3214.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3215.  
  3216.   doCommand: function(aCommand)
  3217.   {
  3218.     SetEditMode(kDisplayModeAllTags);
  3219.   }
  3220. };
  3221.  
  3222. var nsHTMLSourceModeCommand =
  3223. {
  3224.   isCommandEnabled: function(aCommand, dummy)
  3225.   {
  3226.     return (IsDocumentEditable() && IsHTMLEditor());
  3227.   },
  3228.  
  3229.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3230.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3231.  
  3232.   doCommand: function(aCommand)
  3233.   {
  3234.     SetEditMode(kDisplayModeSource);
  3235.   }
  3236. };
  3237.  
  3238. var nsPreviewModeCommand =
  3239. {
  3240.   isCommandEnabled: function(aCommand, dummy)
  3241.   {
  3242.     return (IsDocumentEditable() && IsHTMLEditor());
  3243.   },
  3244.  
  3245.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3246.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3247.  
  3248.   doCommand: function(aCommand)
  3249.   {
  3250.     SetEditMode(kDisplayModePreview);
  3251.   }
  3252. };
  3253.  
  3254. //-----------------------------------------------------------------------------------
  3255. var nsInsertOrEditTableCommand =
  3256. {
  3257.   isCommandEnabled: function(aCommand, dummy)
  3258.   {
  3259.     return (IsDocumentEditable() && IsEditingRenderedHTML());
  3260.   },
  3261.  
  3262.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3263.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3264.  
  3265.   doCommand: function(aCommand)
  3266.   {
  3267.     if (IsInTableCell())
  3268.       EditorTableCellProperties();
  3269.     else
  3270.       EditorInsertOrEditTable(true);
  3271.   }
  3272. };
  3273.  
  3274. //-----------------------------------------------------------------------------------
  3275. var nsEditTableCommand =
  3276. {
  3277.   isCommandEnabled: function(aCommand, dummy)
  3278.   {
  3279.     return IsInTable();
  3280.   },
  3281.  
  3282.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3283.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3284.  
  3285.   doCommand: function(aCommand)
  3286.   {
  3287.     EditorInsertOrEditTable(false);
  3288.   }
  3289. };
  3290.  
  3291. //-----------------------------------------------------------------------------------
  3292. var nsSelectTableCommand =
  3293. {
  3294.   isCommandEnabled: function(aCommand, dummy)
  3295.   {
  3296.     return IsInTable();
  3297.   },
  3298.  
  3299.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3300.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3301.  
  3302.   doCommand: function(aCommand)
  3303.   {
  3304.     try {
  3305.       GetCurrentTableEditor().selectTable();
  3306.     } catch(e) {}
  3307.     window.content.focus();
  3308.   }
  3309. };
  3310.  
  3311. var nsSelectTableRowCommand =
  3312. {
  3313.   isCommandEnabled: function(aCommand, dummy)
  3314.   {
  3315.     return IsInTableCell();
  3316.   },
  3317.  
  3318.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3319.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3320.  
  3321.   doCommand: function(aCommand)
  3322.   {
  3323.     try {
  3324.       GetCurrentTableEditor().selectTableRow();
  3325.     } catch(e) {}
  3326.     window.content.focus();
  3327.   }
  3328. };
  3329.  
  3330. var nsSelectTableColumnCommand =
  3331. {
  3332.   isCommandEnabled: function(aCommand, dummy)
  3333.   {
  3334.     return IsInTableCell();
  3335.   },
  3336.  
  3337.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3338.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3339.  
  3340.   doCommand: function(aCommand)
  3341.   {
  3342.     try {
  3343.       GetCurrentTableEditor().selectTableColumn();
  3344.     } catch(e) {}
  3345.     window.content.focus();
  3346.   }
  3347. };
  3348.  
  3349. var nsSelectTableCellCommand =
  3350. {
  3351.   isCommandEnabled: function(aCommand, dummy)
  3352.   {
  3353.     return IsInTableCell();
  3354.   },
  3355.  
  3356.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3357.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3358.  
  3359.   doCommand: function(aCommand)
  3360.   {
  3361.     try {
  3362.       GetCurrentTableEditor().selectTableCell();
  3363.     } catch(e) {}
  3364.     window.content.focus();
  3365.   }
  3366. };
  3367.  
  3368. var nsSelectAllTableCellsCommand =
  3369. {
  3370.   isCommandEnabled: function(aCommand, dummy)
  3371.   {
  3372.     return IsInTable();
  3373.   },
  3374.  
  3375.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3376.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3377.  
  3378.   doCommand: function(aCommand)
  3379.   {
  3380.     try {
  3381.       GetCurrentTableEditor().selectAllTableCells();
  3382.     } catch(e) {}
  3383.     window.content.focus();
  3384.   }
  3385. };
  3386.  
  3387. //-----------------------------------------------------------------------------------
  3388. var nsInsertTableCommand =
  3389. {
  3390.   isCommandEnabled: function(aCommand, dummy)
  3391.   {
  3392.     return IsDocumentEditable() && IsEditingRenderedHTML();
  3393.   },
  3394.  
  3395.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3396.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3397.  
  3398.   doCommand: function(aCommand)
  3399.   {
  3400.     EditorInsertTable();
  3401.   }
  3402. };
  3403.  
  3404. var nsInsertTableRowAboveCommand =
  3405. {
  3406.   isCommandEnabled: function(aCommand, dummy)
  3407.   {
  3408.     return IsInTableCell();
  3409.   },
  3410.  
  3411.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3412.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3413.  
  3414.   doCommand: function(aCommand)
  3415.   {
  3416.     try {
  3417.       GetCurrentTableEditor().insertTableRow(1, false);
  3418.     } catch(e) {}
  3419.     window.content.focus();
  3420.   }
  3421. };
  3422.  
  3423. var nsInsertTableRowBelowCommand =
  3424. {
  3425.   isCommandEnabled: function(aCommand, dummy)
  3426.   {
  3427.     return IsInTableCell();
  3428.   },
  3429.  
  3430.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3431.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3432.  
  3433.   doCommand: function(aCommand)
  3434.   {
  3435.     try {
  3436.       GetCurrentTableEditor().insertTableRow(1, true);
  3437.     } catch(e) {}
  3438.     window.content.focus();
  3439.   }
  3440. };
  3441.  
  3442. var nsInsertTableColumnBeforeCommand =
  3443. {
  3444.   isCommandEnabled: function(aCommand, dummy)
  3445.   {
  3446.     return IsInTableCell();
  3447.   },
  3448.  
  3449.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3450.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3451.  
  3452.   doCommand: function(aCommand)
  3453.   {
  3454.     try {
  3455.       GetCurrentTableEditor().insertTableColumn(1, false);
  3456.     } catch(e) {}
  3457.     window.content.focus();
  3458.   }
  3459. };
  3460.  
  3461. var nsInsertTableColumnAfterCommand =
  3462. {
  3463.   isCommandEnabled: function(aCommand, dummy)
  3464.   {
  3465.     return IsInTableCell();
  3466.   },
  3467.  
  3468.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3469.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3470.  
  3471.   doCommand: function(aCommand)
  3472.   {
  3473.     try {
  3474.       GetCurrentTableEditor().insertTableColumn(1, true);
  3475.     } catch(e) {}
  3476.     window.content.focus();
  3477.   }
  3478. };
  3479.  
  3480. var nsInsertTableCellBeforeCommand =
  3481. {
  3482.   isCommandEnabled: function(aCommand, dummy)
  3483.   {
  3484.     return IsInTableCell();
  3485.   },
  3486.  
  3487.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3488.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3489.  
  3490.   doCommand: function(aCommand)
  3491.   {
  3492.     try {
  3493.       GetCurrentTableEditor().insertTableCell(1, false);
  3494.     } catch(e) {}
  3495.     window.content.focus();
  3496.   }
  3497. };
  3498.  
  3499. var nsInsertTableCellAfterCommand =
  3500. {
  3501.   isCommandEnabled: function(aCommand, dummy)
  3502.   {
  3503.     return IsInTableCell();
  3504.   },
  3505.  
  3506.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3507.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3508.  
  3509.   doCommand: function(aCommand)
  3510.   {
  3511.     try {
  3512.       GetCurrentTableEditor().insertTableCell(1, true);
  3513.     } catch(e) {}
  3514.     window.content.focus();
  3515.   }
  3516. };
  3517.  
  3518. //-----------------------------------------------------------------------------------
  3519. var nsDeleteTableCommand =
  3520. {
  3521.   isCommandEnabled: function(aCommand, dummy)
  3522.   {
  3523.     return IsInTable();
  3524.   },
  3525.  
  3526.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3527.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3528.  
  3529.   doCommand: function(aCommand)
  3530.   {
  3531.     try {
  3532.       GetCurrentTableEditor().deleteTable();
  3533.     } catch(e) {}
  3534.     window.content.focus();
  3535.   }
  3536. };
  3537.  
  3538. var nsDeleteTableRowCommand =
  3539. {
  3540.   isCommandEnabled: function(aCommand, dummy)
  3541.   {
  3542.     return IsInTableCell();
  3543.   },
  3544.  
  3545.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3546.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3547.  
  3548.   doCommand: function(aCommand)
  3549.   {
  3550.     var rows = GetNumberOfContiguousSelectedRows();
  3551.     // Delete at least one row
  3552.     if (rows == 0)
  3553.       rows = 1;
  3554.  
  3555.     try {
  3556.       var editor = GetCurrentTableEditor();
  3557.       editor.beginTransaction();
  3558.  
  3559.       // Loop to delete all blocks of contiguous, selected rows
  3560.       while (rows)
  3561.       {
  3562.         editor.deleteTableRow(rows);
  3563.         rows = GetNumberOfContiguousSelectedRows();
  3564.       }
  3565.     } finally { editor.endTransaction(); }
  3566.     window.content.focus();
  3567.   }
  3568. };
  3569.  
  3570. var nsDeleteTableColumnCommand =
  3571. {
  3572.   isCommandEnabled: function(aCommand, dummy)
  3573.   {
  3574.     return IsInTableCell();
  3575.   },
  3576.  
  3577.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3578.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3579.  
  3580.   doCommand: function(aCommand)
  3581.   {
  3582.     var columns = GetNumberOfContiguousSelectedColumns();
  3583.     // Delete at least one column
  3584.     if (columns == 0)
  3585.       columns = 1;
  3586.  
  3587.     try {
  3588.       var editor = GetCurrentTableEditor();
  3589.       editor.beginTransaction();
  3590.  
  3591.       // Loop to delete all blocks of contiguous, selected columns
  3592.       while (columns)
  3593.       {
  3594.         editor.deleteTableColumn(columns);
  3595.         columns = GetNumberOfContiguousSelectedColumns();
  3596.       }
  3597.     } finally { editor.endTransaction(); }
  3598.     window.content.focus();
  3599.   }
  3600. };
  3601.  
  3602. var nsDeleteTableCellCommand =
  3603. {
  3604.   isCommandEnabled: function(aCommand, dummy)
  3605.   {
  3606.     return IsInTableCell();
  3607.   },
  3608.  
  3609.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3610.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3611.  
  3612.   doCommand: function(aCommand)
  3613.   {
  3614.     try {
  3615.       GetCurrentTableEditor().deleteTableCell(1);   
  3616.     } catch (e) {}
  3617.     window.content.focus();
  3618.   }
  3619. };
  3620.  
  3621. var nsDeleteTableCellContentsCommand =
  3622. {
  3623.   isCommandEnabled: function(aCommand, dummy)
  3624.   {
  3625.     return IsInTableCell();
  3626.   },
  3627.  
  3628.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3629.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3630.  
  3631.   doCommand: function(aCommand)
  3632.   {
  3633.     try {
  3634.       GetCurrentTableEditor().deleteTableCellContents();   
  3635.     } catch (e) {}
  3636.     window.content.focus();
  3637.   }
  3638. };
  3639.  
  3640.  
  3641. //-----------------------------------------------------------------------------------
  3642. var nsNormalizeTableCommand =
  3643. {
  3644.   isCommandEnabled: function(aCommand, dummy)
  3645.   {
  3646.     return IsInTable();
  3647.   },
  3648.  
  3649.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3650.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3651.  
  3652.   doCommand: function(aCommand)
  3653.   {
  3654.     // Use nsnull to let editor find table enclosing current selection
  3655.     try {
  3656.       GetCurrentTableEditor().normalizeTable(null);   
  3657.     } catch (e) {}
  3658.     window.content.focus();
  3659.   }
  3660. };
  3661.  
  3662. //-----------------------------------------------------------------------------------
  3663. var nsJoinTableCellsCommand =
  3664. {
  3665.   isCommandEnabled: function(aCommand, dummy)
  3666.   {
  3667.     if (IsDocumentEditable() && IsEditingRenderedHTML())
  3668.     {
  3669.       try {
  3670.         var editor = GetCurrentTableEditor();
  3671.         var tagNameObj = { value: "" };
  3672.         var countObj = { value: 0 };
  3673.         var cell = editor.getSelectedOrParentTableElement(tagNameObj, countObj);
  3674.  
  3675.         // We need a cell and either > 1 selected cell or a cell to the right
  3676.         //  (this cell may originate in a row spanned from above current row)
  3677.         // Note that editor returns "td" for "th" also.
  3678.         // (this is a pain! Editor and gecko use lowercase tagNames, JS uses uppercase!)
  3679.         if( cell && (tagNameObj.value == "td"))
  3680.         {
  3681.           // Selected cells
  3682.           if (countObj.value > 1) return true;
  3683.  
  3684.           var colSpan = cell.getAttribute("colspan");
  3685.  
  3686.           // getAttribute returns string, we need number
  3687.           // no attribute means colspan = 1
  3688.           if (!colSpan)
  3689.             colSpan = Number(1);
  3690.           else
  3691.             colSpan = Number(colSpan);
  3692.  
  3693.           var rowObj = { value: 0 };
  3694.           var colObj = { value: 0 };
  3695.           editor.getCellIndexes(cell, rowObj, colObj);
  3696.  
  3697.           // Test if cell exists to the right of current cell
  3698.           // (cells with 0 span should never have cells to the right
  3699.           //  if there is, user can select the 2 cells to join them)
  3700.           return (colSpan && editor.getCellAt(null, rowObj.value,
  3701.                                               colObj.value + colSpan));
  3702.         }
  3703.       } catch (e) {}
  3704.     }
  3705.     return false;
  3706.   },
  3707.  
  3708.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3709.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3710.  
  3711.   doCommand: function(aCommand)
  3712.   {
  3713.     // Param: Don't merge non-contiguous cells
  3714.     try {
  3715.       GetCurrentTableEditor().joinTableCells(false);
  3716.     } catch (e) {}
  3717.     window.content.focus();
  3718.   }
  3719. };
  3720.  
  3721. //-----------------------------------------------------------------------------------
  3722. var nsSplitTableCellCommand =
  3723. {
  3724.   isCommandEnabled: function(aCommand, dummy)
  3725.   {
  3726.     if (IsDocumentEditable() && IsEditingRenderedHTML())
  3727.     {
  3728.       var tagNameObj = { value: "" };
  3729.       var countObj = { value: 0 };
  3730.       var cell;
  3731.       try {
  3732.         cell = GetCurrentTableEditor().getSelectedOrParentTableElement(tagNameObj, countObj);
  3733.       } catch (e) {}
  3734.  
  3735.       // We need a cell parent and there's just 1 selected cell 
  3736.       // or selection is entirely inside 1 cell
  3737.       if ( cell && (tagNameObj.value == "td") && 
  3738.            countObj.value <= 1 &&
  3739.            IsSelectionInOneCell() )
  3740.       {
  3741.         var colSpan = cell.getAttribute("colspan");
  3742.         var rowSpan = cell.getAttribute("rowspan");
  3743.         if (!colSpan) colSpan = 1;
  3744.         if (!rowSpan) rowSpan = 1;
  3745.         return (colSpan > 1  || rowSpan > 1 ||
  3746.                 colSpan == 0 || rowSpan == 0);
  3747.       }
  3748.     }
  3749.     return false;
  3750.   },
  3751.  
  3752.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3753.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3754.  
  3755.   doCommand: function(aCommand)
  3756.   {
  3757.     try {
  3758.       GetCurrentTableEditor().splitTableCell();
  3759.     } catch (e) {}
  3760.     window.content.focus();
  3761.   }
  3762. };
  3763.  
  3764. //-----------------------------------------------------------------------------------
  3765. var nsTableOrCellColorCommand =
  3766. {
  3767.   isCommandEnabled: function(aCommand, dummy)
  3768.   {
  3769.     return IsInTable();
  3770.   },
  3771.  
  3772.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3773.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3774.  
  3775.   doCommand: function(aCommand)
  3776.   {
  3777.     EditorSelectColor("TableOrCell");
  3778.   }
  3779. };
  3780.  
  3781. //-----------------------------------------------------------------------------------
  3782. var nsPreferencesCommand =
  3783. {
  3784.   isCommandEnabled: function(aCommand, dummy)
  3785.   {
  3786.     return true;
  3787.   },
  3788.  
  3789.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3790.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3791.  
  3792.   doCommand: function(aCommand)
  3793.   {
  3794.     goPreferences('editor', 'chrome://editor/content/pref-composer.xul','editor');
  3795.     window.content.focus();
  3796.   }
  3797. };
  3798.  
  3799.  
  3800. var nsFinishHTMLSource =
  3801. {
  3802.   isCommandEnabled: function(aCommand, dummy)
  3803.   {
  3804.     return true;
  3805.   },
  3806.  
  3807.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3808.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3809.  
  3810.   doCommand: function(aCommand)
  3811.   {
  3812.     // In editor.js
  3813.     FinishHTMLSource();
  3814.   }
  3815. };
  3816.  
  3817. var nsCancelHTMLSource =
  3818. {
  3819.   isCommandEnabled: function(aCommand, dummy)
  3820.   {
  3821.     return true;
  3822.   },
  3823.  
  3824.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3825.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3826.  
  3827.   doCommand: function(aCommand)
  3828.   {
  3829.     // In editor.js
  3830.     CancelHTMLSource();
  3831.   }
  3832. };
  3833.  
  3834. var nsConvertToTable =
  3835. {
  3836.   isCommandEnabled: function(aCommand, dummy)
  3837.   {
  3838.     if (IsDocumentEditable() && IsEditingRenderedHTML())
  3839.     {
  3840.       var selection;
  3841.       try {
  3842.         selection = GetCurrentEditor().selection;
  3843.       } catch (e) {}
  3844.  
  3845.       if (selection && !selection.isCollapsed)
  3846.       {
  3847.         // Don't allow if table or cell is the selection
  3848.         var element;
  3849.         try {
  3850.           element = GetCurrentEditor().getSelectedElement("");
  3851.         } catch (e) {}
  3852.         if (element)
  3853.         {
  3854.           var name = element.nodeName.toLowerCase();
  3855.           if (name == "td" ||
  3856.               name == "th" ||
  3857.               name == "caption" ||
  3858.               name == "table")
  3859.             return false;
  3860.         }
  3861.  
  3862.         // Selection start and end must be in the same cell
  3863.         //   in same cell or both are NOT in a cell
  3864.         if ( GetParentTableCell(selection.focusNode) !=
  3865.              GetParentTableCell(selection.anchorNode) )
  3866.           return false
  3867.       
  3868.         return true;
  3869.       }
  3870.     }
  3871.     return false;
  3872.   },
  3873.  
  3874.   getCommandStateParams: function(aCommand, aParams, aRefCon) {},
  3875.   doCommandParams: function(aCommand, aParams, aRefCon) {},
  3876.  
  3877.   doCommand: function(aCommand)
  3878.   {
  3879.     if (this.isCommandEnabled())
  3880.     {
  3881.       window.openDialog("chrome://editor/content/EdConvertToTable.xul","_blank", "chrome,close,titlebar,modal")
  3882.     }
  3883.     window.content.focus();
  3884.   }
  3885. };
  3886.  
  3887.