4067 строки
111 KiB
JavaScript
4067 строки
111 KiB
JavaScript
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/* Implementations of nsIControllerCommand for composer commands */
|
|
|
|
// Linting is disabled in chunks of this file because it contains code that never
|
|
// runs in Thunderbird, and references things that don't exist in Thunderbird.
|
|
|
|
/* import-globals-from editor.js */
|
|
/* import-globals-from editorUtilities.js */
|
|
/* globals CreatePublishDataFromUrl editPage FormatDirForPublishing getTopWin
|
|
goPreferences nsIPromptService openComposeWindow openNewPrivateWith
|
|
PrintPreviewListener SavePublishDataToPrefs SavePassword savePWObj */
|
|
|
|
var gComposerJSCommandControllerID = 0;
|
|
|
|
function SetupHTMLEditorCommands() {
|
|
var commandTable = GetComposerCommandTable();
|
|
if (!commandTable) {
|
|
return;
|
|
}
|
|
|
|
// Include everything a text editor does
|
|
SetupTextEditorCommands();
|
|
|
|
// dump("Registering HTML editor commands\n");
|
|
|
|
commandTable.registerCommand("cmd_renderedHTMLEnabler", nsDummyHTMLCommand);
|
|
|
|
commandTable.registerCommand("cmd_grid", nsGridCommand);
|
|
|
|
commandTable.registerCommand("cmd_listProperties", nsListPropertiesCommand);
|
|
commandTable.registerCommand("cmd_pageProperties", nsPagePropertiesCommand);
|
|
commandTable.registerCommand("cmd_colorProperties", nsColorPropertiesCommand);
|
|
commandTable.registerCommand("cmd_increaseFontStep", nsIncreaseFontCommand);
|
|
commandTable.registerCommand("cmd_decreaseFontStep", nsDecreaseFontCommand);
|
|
commandTable.registerCommand(
|
|
"cmd_advancedProperties",
|
|
nsAdvancedPropertiesCommand
|
|
);
|
|
commandTable.registerCommand(
|
|
"cmd_objectProperties",
|
|
nsObjectPropertiesCommand
|
|
);
|
|
commandTable.registerCommand(
|
|
"cmd_removeNamedAnchors",
|
|
nsRemoveNamedAnchorsCommand
|
|
);
|
|
commandTable.registerCommand("cmd_editLink", nsEditLinkCommand);
|
|
|
|
commandTable.registerCommand("cmd_form", nsFormCommand);
|
|
commandTable.registerCommand("cmd_inputtag", nsInputTagCommand);
|
|
commandTable.registerCommand("cmd_inputimage", nsInputImageCommand);
|
|
commandTable.registerCommand("cmd_textarea", nsTextAreaCommand);
|
|
commandTable.registerCommand("cmd_select", nsSelectCommand);
|
|
commandTable.registerCommand("cmd_button", nsButtonCommand);
|
|
commandTable.registerCommand("cmd_label", nsLabelCommand);
|
|
commandTable.registerCommand("cmd_fieldset", nsFieldSetCommand);
|
|
commandTable.registerCommand("cmd_isindex", nsIsIndexCommand);
|
|
commandTable.registerCommand("cmd_image", nsImageCommand);
|
|
commandTable.registerCommand("cmd_hline", nsHLineCommand);
|
|
commandTable.registerCommand("cmd_link", nsLinkCommand);
|
|
commandTable.registerCommand("cmd_anchor", nsAnchorCommand);
|
|
commandTable.registerCommand(
|
|
"cmd_insertHTMLWithDialog",
|
|
nsInsertHTMLWithDialogCommand
|
|
);
|
|
commandTable.registerCommand(
|
|
"cmd_insertMathWithDialog",
|
|
nsInsertMathWithDialogCommand
|
|
);
|
|
commandTable.registerCommand("cmd_insertBreak", nsInsertBreakCommand);
|
|
commandTable.registerCommand("cmd_insertBreakAll", nsInsertBreakAllCommand);
|
|
|
|
commandTable.registerCommand("cmd_table", nsInsertOrEditTableCommand);
|
|
commandTable.registerCommand("cmd_editTable", nsEditTableCommand);
|
|
commandTable.registerCommand("cmd_SelectTable", nsSelectTableCommand);
|
|
commandTable.registerCommand("cmd_SelectRow", nsSelectTableRowCommand);
|
|
commandTable.registerCommand("cmd_SelectColumn", nsSelectTableColumnCommand);
|
|
commandTable.registerCommand("cmd_SelectCell", nsSelectTableCellCommand);
|
|
commandTable.registerCommand(
|
|
"cmd_SelectAllCells",
|
|
nsSelectAllTableCellsCommand
|
|
);
|
|
commandTable.registerCommand("cmd_InsertTable", nsInsertTableCommand);
|
|
commandTable.registerCommand(
|
|
"cmd_InsertRowAbove",
|
|
nsInsertTableRowAboveCommand
|
|
);
|
|
commandTable.registerCommand(
|
|
"cmd_InsertRowBelow",
|
|
nsInsertTableRowBelowCommand
|
|
);
|
|
commandTable.registerCommand(
|
|
"cmd_InsertColumnBefore",
|
|
nsInsertTableColumnBeforeCommand
|
|
);
|
|
commandTable.registerCommand(
|
|
"cmd_InsertColumnAfter",
|
|
nsInsertTableColumnAfterCommand
|
|
);
|
|
commandTable.registerCommand(
|
|
"cmd_InsertCellBefore",
|
|
nsInsertTableCellBeforeCommand
|
|
);
|
|
commandTable.registerCommand(
|
|
"cmd_InsertCellAfter",
|
|
nsInsertTableCellAfterCommand
|
|
);
|
|
commandTable.registerCommand("cmd_DeleteTable", nsDeleteTableCommand);
|
|
commandTable.registerCommand("cmd_DeleteRow", nsDeleteTableRowCommand);
|
|
commandTable.registerCommand("cmd_DeleteColumn", nsDeleteTableColumnCommand);
|
|
commandTable.registerCommand("cmd_DeleteCell", nsDeleteTableCellCommand);
|
|
commandTable.registerCommand(
|
|
"cmd_DeleteCellContents",
|
|
nsDeleteTableCellContentsCommand
|
|
);
|
|
commandTable.registerCommand("cmd_JoinTableCells", nsJoinTableCellsCommand);
|
|
commandTable.registerCommand("cmd_SplitTableCell", nsSplitTableCellCommand);
|
|
commandTable.registerCommand(
|
|
"cmd_TableOrCellColor",
|
|
nsTableOrCellColorCommand
|
|
);
|
|
commandTable.registerCommand("cmd_NormalizeTable", nsNormalizeTableCommand);
|
|
commandTable.registerCommand("cmd_smiley", nsSetSmiley);
|
|
commandTable.registerCommand("cmd_ConvertToTable", nsConvertToTable);
|
|
}
|
|
|
|
function SetupTextEditorCommands() {
|
|
var commandTable = GetComposerCommandTable();
|
|
if (!commandTable) {
|
|
return;
|
|
}
|
|
|
|
// dump("Registering plain text editor commands\n");
|
|
|
|
commandTable.registerCommand("cmd_findReplace", nsFindReplaceCommand);
|
|
commandTable.registerCommand("cmd_find", nsFindCommand);
|
|
commandTable.registerCommand("cmd_findNext", nsFindAgainCommand);
|
|
commandTable.registerCommand("cmd_findPrev", nsFindAgainCommand);
|
|
commandTable.registerCommand("cmd_rewrap", nsRewrapCommand);
|
|
commandTable.registerCommand("cmd_spelling", nsSpellingCommand);
|
|
commandTable.registerCommand("cmd_validate", nsValidateCommand);
|
|
commandTable.registerCommand("cmd_insertChars", nsInsertCharsCommand);
|
|
}
|
|
|
|
function SetupComposerWindowCommands() {
|
|
// Don't need to do this if already done
|
|
if (gComposerWindowControllerID) {
|
|
return;
|
|
}
|
|
|
|
// Create a command controller and register commands
|
|
// specific to Web Composer window (file-related commands, HTML Source...)
|
|
// We can't use the composer controller created on the content window else
|
|
// we can't process commands when in HTMLSource editor
|
|
// IMPORTANT: For each of these commands, the doCommand method
|
|
// must first call SetEditMode(gPreviousNonSourceDisplayMode);
|
|
// to go from HTML Source mode to any other edit mode
|
|
|
|
var windowControllers = window.controllers;
|
|
|
|
if (!windowControllers) {
|
|
return;
|
|
}
|
|
|
|
var commandTable;
|
|
var composerController;
|
|
var editorController;
|
|
try {
|
|
composerController = Cc[
|
|
"@mozilla.org/embedcomp/base-command-controller;1"
|
|
].createInstance();
|
|
|
|
editorController = composerController.QueryInterface(
|
|
Ci.nsIControllerContext
|
|
);
|
|
|
|
// Get the nsIControllerCommandTable interface we need to register commands
|
|
var interfaceRequestor = composerController.QueryInterface(
|
|
Ci.nsIInterfaceRequestor
|
|
);
|
|
commandTable = interfaceRequestor.getInterface(
|
|
Ci.nsIControllerCommandTable
|
|
);
|
|
} catch (e) {
|
|
dump("Failed to create composerController\n");
|
|
return;
|
|
}
|
|
|
|
if (!commandTable) {
|
|
dump("Failed to get interface for nsIControllerCommandManager\n");
|
|
return;
|
|
}
|
|
|
|
// File-related commands
|
|
commandTable.registerCommand("cmd_open", nsOpenCommand);
|
|
commandTable.registerCommand("cmd_save", nsSaveCommand);
|
|
commandTable.registerCommand("cmd_saveAs", nsSaveAsCommand);
|
|
commandTable.registerCommand("cmd_exportToText", nsExportToTextCommand);
|
|
commandTable.registerCommand(
|
|
"cmd_saveAndChangeEncoding",
|
|
nsSaveAndChangeEncodingCommand
|
|
);
|
|
commandTable.registerCommand("cmd_publish", nsPublishCommand);
|
|
commandTable.registerCommand("cmd_publishAs", nsPublishAsCommand);
|
|
commandTable.registerCommand("cmd_publishSettings", nsPublishSettingsCommand);
|
|
commandTable.registerCommand("cmd_revert", nsRevertCommand);
|
|
commandTable.registerCommand("cmd_openRemote", nsOpenRemoteCommand);
|
|
commandTable.registerCommand("cmd_preview", nsPreviewCommand);
|
|
commandTable.registerCommand("cmd_editSendPage", nsSendPageCommand);
|
|
commandTable.registerCommand("cmd_print", nsPrintCommand);
|
|
commandTable.registerCommand("cmd_printpreview", nsPrintPreviewCommand);
|
|
commandTable.registerCommand("cmd_printSetup", nsPrintSetupCommand);
|
|
commandTable.registerCommand("cmd_close", nsCloseCommand);
|
|
commandTable.registerCommand("cmd_preferences", nsPreferencesCommand);
|
|
|
|
// Edit Mode commands
|
|
if (GetCurrentEditorType() == "html") {
|
|
commandTable.registerCommand("cmd_NormalMode", nsNormalModeCommand);
|
|
commandTable.registerCommand("cmd_AllTagsMode", nsAllTagsModeCommand);
|
|
commandTable.registerCommand("cmd_HTMLSourceMode", nsHTMLSourceModeCommand);
|
|
commandTable.registerCommand("cmd_PreviewMode", nsPreviewModeCommand);
|
|
commandTable.registerCommand("cmd_FinishHTMLSource", nsFinishHTMLSource);
|
|
commandTable.registerCommand("cmd_CancelHTMLSource", nsCancelHTMLSource);
|
|
commandTable.registerCommand(
|
|
"cmd_updateStructToolbar",
|
|
nsUpdateStructToolbarCommand
|
|
);
|
|
}
|
|
|
|
windowControllers.insertControllerAt(0, editorController);
|
|
|
|
// Store the controller ID so we can be sure to get the right one later
|
|
gComposerWindowControllerID = windowControllers.getControllerId(
|
|
editorController
|
|
);
|
|
}
|
|
|
|
function GetComposerCommandTable() {
|
|
var controller;
|
|
if (gComposerJSCommandControllerID) {
|
|
try {
|
|
controller = window.content.controllers.getControllerById(
|
|
gComposerJSCommandControllerID
|
|
);
|
|
} catch (e) {}
|
|
}
|
|
if (!controller) {
|
|
// create it
|
|
controller = Cc[
|
|
"@mozilla.org/embedcomp/base-command-controller;1"
|
|
].createInstance();
|
|
|
|
var editorController = controller.QueryInterface(Ci.nsIControllerContext);
|
|
editorController.setCommandContext(GetCurrentEditorElement());
|
|
window.content.controllers.insertControllerAt(0, controller);
|
|
|
|
// Store the controller ID so we can be sure to get the right one later
|
|
gComposerJSCommandControllerID = window.content.controllers.getControllerId(
|
|
controller
|
|
);
|
|
}
|
|
|
|
if (controller) {
|
|
var interfaceRequestor = controller.QueryInterface(
|
|
Ci.nsIInterfaceRequestor
|
|
);
|
|
return interfaceRequestor.getInterface(Ci.nsIControllerCommandTable);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/* eslint-disable complexity */
|
|
function goUpdateCommandState(command) {
|
|
try {
|
|
var controller = top.document.commandDispatcher.getControllerForCommand(
|
|
command
|
|
);
|
|
if (!(controller instanceof Ci.nsICommandController)) {
|
|
return;
|
|
}
|
|
|
|
var params = newCommandParams();
|
|
if (!params) {
|
|
return;
|
|
}
|
|
|
|
controller.getCommandStateWithParams(command, params);
|
|
|
|
switch (command) {
|
|
case "cmd_bold":
|
|
case "cmd_italic":
|
|
case "cmd_underline":
|
|
case "cmd_var":
|
|
case "cmd_samp":
|
|
case "cmd_code":
|
|
case "cmd_acronym":
|
|
case "cmd_abbr":
|
|
case "cmd_cite":
|
|
case "cmd_strong":
|
|
case "cmd_em":
|
|
case "cmd_superscript":
|
|
case "cmd_subscript":
|
|
case "cmd_strikethrough":
|
|
case "cmd_tt":
|
|
case "cmd_nobreak":
|
|
case "cmd_ul":
|
|
case "cmd_ol":
|
|
pokeStyleUI(command, params.getBooleanValue("state_all"));
|
|
break;
|
|
|
|
case "cmd_paragraphState":
|
|
case "cmd_align":
|
|
case "cmd_highlight":
|
|
case "cmd_backgroundColor":
|
|
case "cmd_fontColor":
|
|
case "cmd_fontFace":
|
|
case "cmd_fontSize":
|
|
case "cmd_absPos":
|
|
pokeMultiStateUI(command, params);
|
|
break;
|
|
|
|
case "cmd_decreaseZIndex":
|
|
case "cmd_increaseZIndex":
|
|
case "cmd_indent":
|
|
case "cmd_outdent":
|
|
case "cmd_increaseFont":
|
|
case "cmd_decreaseFont":
|
|
case "cmd_increaseFontStep":
|
|
case "cmd_decreaseFontStep":
|
|
case "cmd_removeStyles":
|
|
case "cmd_smiley":
|
|
break;
|
|
|
|
default:
|
|
dump("no update for command: " + command + "\n");
|
|
}
|
|
} catch (e) {
|
|
dump(
|
|
"An error occurred updating the " + command + " command: \n" + e + "\n"
|
|
);
|
|
}
|
|
}
|
|
/* eslint-enable complexity */
|
|
|
|
function goUpdateComposerMenuItems(commandset) {
|
|
// dump("Updating commands for " + commandset.id + "\n");
|
|
|
|
for (var i = 0; i < commandset.childNodes.length; i++) {
|
|
var commandNode = commandset.childNodes[i];
|
|
var commandID = commandNode.id;
|
|
if (commandID) {
|
|
goUpdateCommand(commandID); // enable or disable
|
|
if (commandNode.hasAttribute("state")) {
|
|
goUpdateCommandState(commandID);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function goDoCommandParams(command, params) {
|
|
try {
|
|
var controller = top.document.commandDispatcher.getControllerForCommand(
|
|
command
|
|
);
|
|
if (controller && controller.isCommandEnabled(command)) {
|
|
if (controller instanceof Ci.nsICommandController) {
|
|
controller.doCommandWithParams(command, params);
|
|
|
|
// the following two lines should be removed when we implement observers
|
|
if (params) {
|
|
controller.getCommandStateWithParams(command, params);
|
|
}
|
|
} else {
|
|
controller.doCommand(command);
|
|
}
|
|
ResetStructToolbar();
|
|
}
|
|
} catch (e) {
|
|
dump("An error occurred executing the " + command + " command\n");
|
|
}
|
|
}
|
|
|
|
function pokeStyleUI(uiID, aDesiredState) {
|
|
try {
|
|
var commandNode = top.document.getElementById(uiID);
|
|
if (!commandNode) {
|
|
return;
|
|
}
|
|
|
|
var uiState = "true" == commandNode.getAttribute("state");
|
|
if (aDesiredState != uiState) {
|
|
commandNode.setAttribute("state", aDesiredState ? "true" : "false");
|
|
}
|
|
} catch (e) {
|
|
dump("poking UI for " + uiID + " failed: " + e + "\n");
|
|
}
|
|
}
|
|
|
|
function doStyleUICommand(cmdStr) {
|
|
try {
|
|
var cmdParams = newCommandParams();
|
|
goDoCommandParams(cmdStr, cmdParams);
|
|
if (cmdParams) {
|
|
pokeStyleUI(cmdStr, cmdParams.getBooleanValue("state_all"));
|
|
}
|
|
|
|
ResetStructToolbar();
|
|
} catch (e) {}
|
|
}
|
|
|
|
// Copied from jsmime.js.
|
|
function stringToTypedArray(buffer) {
|
|
var typedarray = new Uint8Array(buffer.length);
|
|
for (var i = 0; i < buffer.length; i++) {
|
|
typedarray[i] = buffer.charCodeAt(i);
|
|
}
|
|
return typedarray;
|
|
}
|
|
|
|
function pokeMultiStateUI(uiID, cmdParams) {
|
|
try {
|
|
var commandNode = document.getElementById(uiID);
|
|
if (!commandNode) {
|
|
return;
|
|
}
|
|
|
|
var isMixed = cmdParams.getBooleanValue("state_mixed");
|
|
var desiredAttrib;
|
|
if (isMixed) {
|
|
desiredAttrib = "mixed";
|
|
} else {
|
|
var valuetype = cmdParams.getValueType("state_attribute");
|
|
if (valuetype == Ci.nsICommandParams.eStringType) {
|
|
desiredAttrib = cmdParams.getCStringValue("state_attribute");
|
|
// Decode UTF-8, for example for font names in Japanese.
|
|
desiredAttrib = new TextDecoder("UTF-8").decode(
|
|
stringToTypedArray(desiredAttrib)
|
|
);
|
|
} else {
|
|
desiredAttrib = cmdParams.getStringValue("state_attribute");
|
|
}
|
|
}
|
|
|
|
var uiState = commandNode.getAttribute("state");
|
|
if (desiredAttrib != uiState) {
|
|
commandNode.setAttribute("state", desiredAttrib);
|
|
}
|
|
} catch (e) {}
|
|
}
|
|
|
|
function doStatefulCommand(commandID, newState) {
|
|
var commandNode = document.getElementById(commandID);
|
|
if (commandNode) {
|
|
commandNode.setAttribute("state", newState);
|
|
}
|
|
gContentWindow.focus(); // needed for command dispatch to work
|
|
|
|
try {
|
|
var cmdParams = newCommandParams();
|
|
if (!cmdParams) {
|
|
return;
|
|
}
|
|
|
|
cmdParams.setStringValue("state_attribute", newState);
|
|
goDoCommandParams(commandID, cmdParams);
|
|
|
|
pokeMultiStateUI(commandID, cmdParams);
|
|
|
|
ResetStructToolbar();
|
|
} catch (e) {
|
|
dump("error thrown in doStatefulCommand: " + e + "\n");
|
|
}
|
|
}
|
|
|
|
function PrintObject(obj) {
|
|
dump("-----" + obj + "------\n");
|
|
var names = "";
|
|
for (var i in obj) {
|
|
if (i == "value") {
|
|
names += i + ": " + obj.value + "\n";
|
|
} else if (i == "id") {
|
|
names += i + ": " + obj.id + "\n";
|
|
} else {
|
|
names += i + "\n";
|
|
}
|
|
}
|
|
|
|
dump(names + "-----------\n");
|
|
}
|
|
|
|
function PrintNodeID(id) {
|
|
PrintObject(document.getElementById(id));
|
|
}
|
|
|
|
var nsDummyHTMLCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
// do nothing
|
|
dump("Hey, who's calling the dummy command?\n");
|
|
},
|
|
};
|
|
|
|
var nsOpenCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
// We can always do this.
|
|
return true;
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
var fileType = IsHTMLEditor() ? "html" : "text";
|
|
var title = GetString(IsHTMLEditor() ? "OpenHTMLFile" : "OpenTextFile");
|
|
|
|
var fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
|
|
fp.init(window, title, nsIFilePicker.modeOpen);
|
|
|
|
SetFilePickerDirectory(fp, fileType);
|
|
|
|
// Direct user to prefer HTML files and/or text files depending on whether
|
|
// loading into Composer or Text editor, so we call separately to control
|
|
// the order of the filter list.
|
|
if (fileType == "html") {
|
|
fp.appendFilters(nsIFilePicker.filterHTML);
|
|
}
|
|
fp.appendFilters(nsIFilePicker.filterText);
|
|
fp.appendFilters(nsIFilePicker.filterAll);
|
|
|
|
fp.open(rv => {
|
|
if (rv == nsIFilePicker.returnCancel) {
|
|
return;
|
|
}
|
|
// editPage checks for already open window and activates it.
|
|
if (fp.fileURL.spec) {
|
|
SaveFilePickerDirectory(fp, fileType);
|
|
editPage(fp.fileURL.spec, fileType);
|
|
}
|
|
});
|
|
},
|
|
};
|
|
|
|
// STRUCTURE TOOLBAR
|
|
//
|
|
var nsUpdateStructToolbarCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
UpdateStructToolbar();
|
|
return true;
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
doCommand(aCommand) {},
|
|
};
|
|
|
|
// ******* File output commands and utilities ******** //
|
|
var nsSaveCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
// Always allow saving when editing a remote document,
|
|
// otherwise the document modified state would prevent that
|
|
// when you first open a remote file.
|
|
try {
|
|
var docUrl = GetDocumentUrl();
|
|
return (
|
|
IsDocumentEditable() &&
|
|
(IsDocumentModified() ||
|
|
IsHTMLSourceChanged() ||
|
|
IsUrlAboutBlank(docUrl) ||
|
|
GetScheme(docUrl) != "file")
|
|
);
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
var editor = GetCurrentEditor();
|
|
if (editor) {
|
|
if (IsHTMLEditor()) {
|
|
SetEditMode(gPreviousNonSourceDisplayMode);
|
|
}
|
|
SaveDocument(
|
|
IsUrlAboutBlank(GetDocumentUrl()),
|
|
false,
|
|
editor.contentsMIMEType
|
|
);
|
|
}
|
|
},
|
|
};
|
|
|
|
var nsSaveAsCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
var editor = GetCurrentEditor();
|
|
if (editor) {
|
|
if (IsHTMLEditor()) {
|
|
SetEditMode(gPreviousNonSourceDisplayMode);
|
|
}
|
|
SaveDocument(true, false, editor.contentsMIMEType);
|
|
}
|
|
},
|
|
};
|
|
|
|
var nsExportToTextCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
if (GetCurrentEditor()) {
|
|
SetEditMode(gPreviousNonSourceDisplayMode);
|
|
SaveDocument(true, true, "text/plain");
|
|
}
|
|
},
|
|
};
|
|
|
|
var nsSaveAndChangeEncodingCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
SetEditMode(gPreviousNonSourceDisplayMode);
|
|
window.ok = false;
|
|
window.exportToText = false;
|
|
var oldTitle = GetDocumentTitle();
|
|
window.openDialog(
|
|
"chrome://editor/content/EditorSaveAsCharset.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal,resizable=yes"
|
|
);
|
|
|
|
if (GetDocumentTitle() != oldTitle) {
|
|
UpdateWindowTitle();
|
|
}
|
|
|
|
if (window.ok) {
|
|
if (window.exportToText) {
|
|
SaveDocument(true, true, "text/plain");
|
|
} else {
|
|
var editor = GetCurrentEditor();
|
|
SaveDocument(true, false, editor ? editor.contentsMIMEType : null);
|
|
}
|
|
}
|
|
},
|
|
};
|
|
|
|
var nsPublishCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
if (IsDocumentEditable()) {
|
|
// Always allow publishing when editing a local document,
|
|
// otherwise the document modified state would prevent that
|
|
// when you first open any local file.
|
|
try {
|
|
var docUrl = GetDocumentUrl();
|
|
return (
|
|
IsDocumentModified() ||
|
|
IsHTMLSourceChanged() ||
|
|
IsUrlAboutBlank(docUrl) ||
|
|
GetScheme(docUrl) == "file"
|
|
);
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
if (GetCurrentEditor()) {
|
|
let docUrl = GetDocumentUrl();
|
|
let filename = GetFilename(docUrl);
|
|
let publishData;
|
|
|
|
// First check pref to always show publish dialog
|
|
let showPublishDialog = Services.prefs.getBoolPref(
|
|
"editor.always_show_publish_dialog"
|
|
);
|
|
|
|
if (!showPublishDialog && filename) {
|
|
// Try to get publish data from the document url
|
|
publishData = CreatePublishDataFromUrl(docUrl);
|
|
|
|
// If none, use default publishing site? Need a pref for this
|
|
// if (!publishData)
|
|
// publishData = GetPublishDataFromSiteName(GetDefaultPublishSiteName(), filename);
|
|
}
|
|
|
|
if (showPublishDialog || !publishData) {
|
|
// Show the publish dialog
|
|
publishData = {};
|
|
window.ok = false;
|
|
let oldTitle = GetDocumentTitle();
|
|
window.openDialog(
|
|
"chrome://editor/content/EditorPublish.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal",
|
|
"",
|
|
"",
|
|
publishData
|
|
);
|
|
if (GetDocumentTitle() != oldTitle) {
|
|
UpdateWindowTitle();
|
|
}
|
|
|
|
if (!window.ok) {
|
|
return false;
|
|
}
|
|
}
|
|
if (publishData) {
|
|
SetEditMode(gPreviousNonSourceDisplayMode);
|
|
return Publish(publishData);
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
};
|
|
|
|
var nsPublishAsCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
if (GetCurrentEditor()) {
|
|
SetEditMode(gPreviousNonSourceDisplayMode);
|
|
|
|
window.ok = false;
|
|
var publishData = {};
|
|
var oldTitle = GetDocumentTitle();
|
|
window.openDialog(
|
|
"chrome://editor/content/EditorPublish.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal",
|
|
"",
|
|
"",
|
|
publishData
|
|
);
|
|
if (GetDocumentTitle() != oldTitle) {
|
|
UpdateWindowTitle();
|
|
}
|
|
|
|
if (window.ok) {
|
|
return Publish(publishData);
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
};
|
|
|
|
// ------- output utilities ----- //
|
|
|
|
// returns a fileExtension string
|
|
function GetExtensionBasedOnMimeType(aMIMEType) {
|
|
try {
|
|
var mimeService = null;
|
|
mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
|
|
|
|
var fileExtension = mimeService.getPrimaryExtension(aMIMEType, null);
|
|
|
|
// the MIME service likes to give back ".htm" for text/html files,
|
|
// so do a special-case fix here.
|
|
if (fileExtension == "htm") {
|
|
fileExtension = "html";
|
|
}
|
|
|
|
return fileExtension;
|
|
} catch (e) {}
|
|
return "";
|
|
}
|
|
|
|
function GetSuggestedFileName(aDocumentURLString, aMIMEType) {
|
|
var extension = GetExtensionBasedOnMimeType(aMIMEType);
|
|
if (extension) {
|
|
extension = "." + extension;
|
|
}
|
|
|
|
// check for existing file name we can use
|
|
if (aDocumentURLString && !IsUrlAboutBlank(aDocumentURLString)) {
|
|
try {
|
|
let docURI = Services.io.newURI(
|
|
aDocumentURLString,
|
|
GetCurrentEditor().documentCharacterSet
|
|
);
|
|
docURI = docURI.QueryInterface(Ci.nsIURL);
|
|
|
|
// grab the file name
|
|
let url = validateFileName(decodeURIComponent(docURI.fileBaseName));
|
|
if (url) {
|
|
return url + extension;
|
|
}
|
|
} catch (e) {}
|
|
}
|
|
|
|
// Check if there is a title we can use to generate a valid filename,
|
|
// if we can't, use the default filename.
|
|
var title =
|
|
validateFileName(GetDocumentTitle()) ||
|
|
GetString("untitledDefaultFilename");
|
|
return title + extension;
|
|
}
|
|
|
|
/**
|
|
* @return {Promise} dialogResult
|
|
*/
|
|
function PromptForSaveLocation(
|
|
aDoSaveAsText,
|
|
aEditorType,
|
|
aMIMEType,
|
|
aDocumentURLString
|
|
) {
|
|
var dialogResult = {};
|
|
dialogResult.filepickerClick = nsIFilePicker.returnCancel;
|
|
dialogResult.resultingURI = "";
|
|
dialogResult.resultingLocalFile = null;
|
|
|
|
var fp = null;
|
|
try {
|
|
fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
|
|
} catch (e) {}
|
|
if (!fp) {
|
|
return dialogResult;
|
|
}
|
|
|
|
// determine prompt string based on type of saving we'll do
|
|
var promptString;
|
|
if (aDoSaveAsText || aEditorType == "text") {
|
|
promptString = GetString("SaveTextAs");
|
|
} else {
|
|
promptString = GetString("SaveDocumentAs");
|
|
}
|
|
|
|
fp.init(window, promptString, nsIFilePicker.modeSave);
|
|
|
|
// Set filters according to the type of output
|
|
if (aDoSaveAsText) {
|
|
fp.appendFilters(nsIFilePicker.filterText);
|
|
} else {
|
|
fp.appendFilters(nsIFilePicker.filterHTML);
|
|
}
|
|
fp.appendFilters(nsIFilePicker.filterAll);
|
|
|
|
// now let's actually set the filepicker's suggested filename
|
|
var suggestedFileName = GetSuggestedFileName(aDocumentURLString, aMIMEType);
|
|
if (suggestedFileName) {
|
|
fp.defaultString = suggestedFileName;
|
|
}
|
|
|
|
// set the file picker's current directory
|
|
// assuming we have information needed (like prior saved location)
|
|
try {
|
|
var fileHandler = GetFileProtocolHandler();
|
|
|
|
var isLocalFile = true;
|
|
try {
|
|
let docURI = Services.io.newURI(
|
|
aDocumentURLString,
|
|
GetCurrentEditor().documentCharacterSet
|
|
);
|
|
isLocalFile = docURI.schemeIs("file");
|
|
} catch (e) {}
|
|
|
|
var parentLocation = null;
|
|
if (isLocalFile) {
|
|
var fileLocation = fileHandler.getFileFromURLSpec(aDocumentURLString); // this asserts if url is not local
|
|
parentLocation = fileLocation.parent;
|
|
}
|
|
if (parentLocation) {
|
|
// Save current filepicker's default location
|
|
if ("gFilePickerDirectory" in window) {
|
|
gFilePickerDirectory = fp.displayDirectory;
|
|
}
|
|
|
|
fp.displayDirectory = parentLocation;
|
|
} else {
|
|
// Initialize to the last-used directory for the particular type (saved in prefs)
|
|
SetFilePickerDirectory(fp, aEditorType);
|
|
}
|
|
} catch (e) {}
|
|
|
|
return new Promise(resolve => {
|
|
fp.open(rv => {
|
|
dialogResult.filepickerClick = rv;
|
|
if (rv != nsIFilePicker.returnCancel && fp.file) {
|
|
// Allow OK and replace.
|
|
// reset urlstring to new save location
|
|
dialogResult.resultingURIString = fileHandler.getURLSpecFromFile(
|
|
fp.file
|
|
);
|
|
dialogResult.resultingLocalFile = fp.file;
|
|
SaveFilePickerDirectory(fp, aEditorType);
|
|
resolve(dialogResult);
|
|
} else if ("gFilePickerDirectory" in window && gFilePickerDirectory) {
|
|
fp.displayDirectory = gFilePickerDirectory;
|
|
resolve(null);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* If needed, prompt for document title and set the document title to the
|
|
* preferred value.
|
|
* @return true if the title was set up successfully;
|
|
* false if the user cancelled the title prompt
|
|
*/
|
|
function PromptAndSetTitleIfNone() {
|
|
if (GetDocumentTitle()) {
|
|
// we have a title; no need to prompt!
|
|
return true;
|
|
}
|
|
|
|
let result = { value: null };
|
|
let captionStr = GetString("DocumentTitle");
|
|
let msgStr = GetString("NeedDocTitle") + "\n" + GetString("DocTitleHelp");
|
|
let confirmed = Services.prompt.prompt(
|
|
window,
|
|
captionStr,
|
|
msgStr,
|
|
result,
|
|
null,
|
|
{ value: 0 }
|
|
);
|
|
if (confirmed) {
|
|
SetDocumentTitle(TrimString(result.value));
|
|
}
|
|
|
|
return confirmed;
|
|
}
|
|
|
|
var gPersistObj;
|
|
|
|
// Don't forget to do these things after calling OutputFileWithPersistAPI:
|
|
// we need to update the uri before notifying listeners
|
|
// if (doUpdateURI)
|
|
// SetDocumentURI(docURI);
|
|
// UpdateWindowTitle();
|
|
// if (!aSaveCopy)
|
|
// editor.resetModificationCount();
|
|
// this should cause notification to listeners that document has changed
|
|
|
|
const webPersist = Ci.nsIWebBrowserPersist;
|
|
function OutputFileWithPersistAPI(
|
|
editorDoc,
|
|
aDestinationLocation,
|
|
aRelatedFilesParentDir,
|
|
aMimeType
|
|
) {
|
|
gPersistObj = null;
|
|
var editor = GetCurrentEditor();
|
|
try {
|
|
editor.forceCompositionEnd();
|
|
} catch (e) {}
|
|
|
|
var isLocalFile = false;
|
|
try {
|
|
aDestinationLocation.QueryInterface(Ci.nsIFile);
|
|
isLocalFile = true;
|
|
} catch (e) {
|
|
try {
|
|
var tmp = aDestinationLocation.QueryInterface(Ci.nsIURI);
|
|
isLocalFile = tmp.schemeIs("file");
|
|
} catch (e) {}
|
|
}
|
|
|
|
try {
|
|
// we should supply a parent directory if/when we turn on functionality to save related documents
|
|
var persistObj = Cc[
|
|
"@mozilla.org/embedding/browser/nsWebBrowserPersist;1"
|
|
].createInstance(webPersist);
|
|
persistObj.progressListener = gEditorOutputProgressListener;
|
|
|
|
var wrapColumn = GetWrapColumn();
|
|
var outputFlags = GetOutputFlags(aMimeType, wrapColumn);
|
|
|
|
// for 4.x parity as well as improving readability of file locally on server
|
|
// this will always send crlf for upload (http/ftp)
|
|
if (!isLocalFile) {
|
|
// if we aren't saving locally then send both cr and lf
|
|
outputFlags |=
|
|
webPersist.ENCODE_FLAGS_CR_LINEBREAKS |
|
|
webPersist.ENCODE_FLAGS_LF_LINEBREAKS;
|
|
|
|
// we want to serialize the output for all remote publishing
|
|
// some servers can handle only one connection at a time
|
|
// some day perhaps we can make this user-configurable per site?
|
|
persistObj.persistFlags =
|
|
persistObj.persistFlags | webPersist.PERSIST_FLAGS_SERIALIZE_OUTPUT;
|
|
}
|
|
|
|
// note: we always want to set the replace existing files flag since we have
|
|
// already given user the chance to not replace an existing file (file picker)
|
|
// or the user picked an option where the file is implicitly being replaced (save)
|
|
persistObj.persistFlags =
|
|
persistObj.persistFlags |
|
|
webPersist.PERSIST_FLAGS_NO_BASE_TAG_MODIFICATIONS |
|
|
webPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
|
|
webPersist.PERSIST_FLAGS_DONT_FIXUP_LINKS |
|
|
webPersist.PERSIST_FLAGS_DONT_CHANGE_FILENAMES |
|
|
webPersist.PERSIST_FLAGS_FIXUP_ORIGINAL_DOM;
|
|
persistObj.saveDocument(
|
|
editorDoc,
|
|
aDestinationLocation,
|
|
aRelatedFilesParentDir,
|
|
aMimeType,
|
|
outputFlags,
|
|
wrapColumn
|
|
);
|
|
gPersistObj = persistObj;
|
|
} catch (e) {
|
|
dump("caught an error, bail\n");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// returns output flags based on mimetype, wrapCol and prefs
|
|
function GetOutputFlags(aMimeType, aWrapColumn) {
|
|
var outputFlags = 0;
|
|
var editor = GetCurrentEditor();
|
|
var outputEntity =
|
|
editor && editor.documentCharacterSet == "ISO-8859-1"
|
|
? webPersist.ENCODE_FLAGS_ENCODE_LATIN1_ENTITIES
|
|
: webPersist.ENCODE_FLAGS_ENCODE_BASIC_ENTITIES;
|
|
if (aMimeType == "text/plain") {
|
|
// When saving in "text/plain" format, always do formatting
|
|
outputFlags |= webPersist.ENCODE_FLAGS_FORMATTED;
|
|
} else {
|
|
// Should we prettyprint? Check the pref
|
|
if (Services.prefs.getBoolPref("editor.prettyprint")) {
|
|
outputFlags |= webPersist.ENCODE_FLAGS_FORMATTED;
|
|
}
|
|
|
|
try {
|
|
// How much entity names should we output? Check the pref
|
|
switch (Services.prefs.getCharPref("editor.encode_entity")) {
|
|
case "basic":
|
|
outputEntity = webPersist.ENCODE_FLAGS_ENCODE_BASIC_ENTITIES;
|
|
break;
|
|
case "latin1":
|
|
outputEntity = webPersist.ENCODE_FLAGS_ENCODE_LATIN1_ENTITIES;
|
|
break;
|
|
case "html":
|
|
outputEntity = webPersist.ENCODE_FLAGS_ENCODE_HTML_ENTITIES;
|
|
break;
|
|
case "none":
|
|
outputEntity = 0;
|
|
break;
|
|
}
|
|
} catch (e) {}
|
|
}
|
|
outputFlags |= outputEntity;
|
|
|
|
if (aWrapColumn > 0) {
|
|
outputFlags |= webPersist.ENCODE_FLAGS_WRAP;
|
|
}
|
|
|
|
return outputFlags;
|
|
}
|
|
|
|
// returns number of column where to wrap
|
|
const nsIWebBrowserPersist = Ci.nsIWebBrowserPersist;
|
|
function GetWrapColumn() {
|
|
try {
|
|
return GetCurrentEditor().wrapWidth;
|
|
} catch (e) {}
|
|
return 0;
|
|
}
|
|
|
|
const gShowDebugOutputStateChange = false;
|
|
const gShowDebugOutputProgress = false;
|
|
const gShowDebugOutputStatusChange = false;
|
|
|
|
const gShowDebugOutputLocationChange = false;
|
|
const gShowDebugOutputSecurityChange = false;
|
|
|
|
const nsIWebProgressListener = Ci.nsIWebProgressListener;
|
|
const nsIChannel = Ci.nsIChannel;
|
|
|
|
const kErrorBindingAborted = 2152398850;
|
|
const kErrorBindingRedirected = 2152398851;
|
|
const kFileNotFound = 2152857618;
|
|
|
|
var gEditorOutputProgressListener = {
|
|
/* eslint-disable complexity */
|
|
onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
|
|
var editor = GetCurrentEditor();
|
|
|
|
// Use this to access onStateChange flags
|
|
var requestSpec;
|
|
try {
|
|
var channel = aRequest.QueryInterface(nsIChannel);
|
|
requestSpec = StripUsernamePasswordFromURI(channel.URI);
|
|
} catch (e) {
|
|
if (gShowDebugOutputStateChange) {
|
|
dump("***** onStateChange; NO REQUEST CHANNEL\n");
|
|
}
|
|
}
|
|
|
|
var pubSpec;
|
|
if (gPublishData) {
|
|
pubSpec =
|
|
gPublishData.publishUrl + gPublishData.docDir + gPublishData.filename;
|
|
}
|
|
|
|
if (gShowDebugOutputStateChange) {
|
|
dump("\n***** onStateChange request: " + requestSpec + "\n");
|
|
dump(" state flags: ");
|
|
|
|
if (aStateFlags & nsIWebProgressListener.STATE_START) {
|
|
dump(" STATE_START, ");
|
|
}
|
|
if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
|
|
dump(" STATE_STOP, ");
|
|
}
|
|
if (aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
|
|
dump(" STATE_IS_NETWORK ");
|
|
}
|
|
|
|
dump(
|
|
`\n * requestSpec=${requestSpec}, pubSpec=${pubSpec}, aStatus=${aStatus}\n`
|
|
);
|
|
|
|
DumpDebugStatus(aStatus);
|
|
}
|
|
// The rest only concerns publishing, so bail out if no dialog
|
|
if (!gProgressDialog) {
|
|
return;
|
|
}
|
|
|
|
// Detect start of file upload of any file:
|
|
// (We ignore any START messages after gPersistObj says publishing is finished
|
|
if (
|
|
aStateFlags & nsIWebProgressListener.STATE_START &&
|
|
gPersistObj &&
|
|
requestSpec &&
|
|
gPersistObj.currentState != gPersistObj.PERSIST_STATE_FINISHED
|
|
) {
|
|
try {
|
|
// Add url to progress dialog's list showing each file uploading
|
|
gProgressDialog.SetProgressStatus(GetFilename(requestSpec), "busy");
|
|
} catch (e) {}
|
|
}
|
|
|
|
// Detect end of file upload of any file:
|
|
if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
|
|
// ignore aStatus == kErrorBindingAborted; check http response for possible errors
|
|
try {
|
|
// check http channel for response: 200 range is ok; other ranges are not
|
|
var httpChannel = aRequest.QueryInterface(Ci.nsIHttpChannel);
|
|
var httpResponse = httpChannel.responseStatus;
|
|
if (httpResponse < 200 || httpResponse >= 300) {
|
|
// Not a real error but enough to pass check below.
|
|
aStatus = httpResponse;
|
|
} else if (aStatus == kErrorBindingAborted) {
|
|
aStatus = 0;
|
|
}
|
|
|
|
if (gShowDebugOutputStateChange) {
|
|
dump("http response is: " + httpResponse + "\n");
|
|
}
|
|
} catch (e) {
|
|
if (aStatus == kErrorBindingAborted) {
|
|
aStatus = 0;
|
|
}
|
|
}
|
|
|
|
// We abort publishing for all errors except if image src file is not found
|
|
var abortPublishing = aStatus != 0 && aStatus != kFileNotFound;
|
|
|
|
// Notify progress dialog when we receive the STOP
|
|
// notification for a file if there was an error
|
|
// or a successful finish
|
|
// (Check requestSpec to be sure message is for destination url)
|
|
if (
|
|
aStatus != 0 ||
|
|
(requestSpec &&
|
|
requestSpec.startsWith(GetScheme(gPublishData.publishUrl)))
|
|
) {
|
|
try {
|
|
gProgressDialog.SetProgressFinished(
|
|
GetFilename(requestSpec),
|
|
aStatus
|
|
);
|
|
} catch (e) {}
|
|
}
|
|
|
|
if (abortPublishing) {
|
|
// Cancel publishing
|
|
gPersistObj.cancelSave();
|
|
|
|
// Don't do any commands after failure
|
|
gCommandAfterPublishing = null;
|
|
|
|
// Restore original document to undo image src url adjustments
|
|
if (gRestoreDocumentSource) {
|
|
try {
|
|
editor.rebuildDocumentFromSource(gRestoreDocumentSource);
|
|
|
|
// Clear transaction cache since we just did a potentially
|
|
// very large insert and this will eat up memory
|
|
editor.transactionManager.clear();
|
|
} catch (e) {}
|
|
}
|
|
|
|
// Notify progress dialog that we're finished
|
|
// and keep open to show error
|
|
gProgressDialog.SetProgressFinished(null, 0);
|
|
|
|
// We don't want to change location or reset mod count, etc.
|
|
return;
|
|
}
|
|
|
|
// XXX HACK: "file://" protocol is not supported in network code
|
|
// (bug 151867 filed to add this support, bug 151869 filed
|
|
// to remove this and other code in nsIWebBrowserPersist)
|
|
// nsIWebBrowserPersist *does* copy the file(s), but we don't
|
|
// get normal onStateChange messages.
|
|
|
|
// Case 1: If images are included, we get fairly normal
|
|
// STATE_START/STATE_STOP & STATE_IS_NETWORK messages associated with the image files,
|
|
// thus we must finish HTML file progress below
|
|
|
|
// Case 2: If just HTML file is uploaded, we get STATE_START and STATE_STOP
|
|
// notification with a null "requestSpec", and
|
|
// the gPersistObj is destroyed before we get here!
|
|
// So create an new object so we can flow through normal processing below
|
|
if (
|
|
!requestSpec &&
|
|
GetScheme(gPublishData.publishUrl) == "file" &&
|
|
(!gPersistObj ||
|
|
gPersistObj.currentState ==
|
|
nsIWebBrowserPersist.PERSIST_STATE_FINISHED)
|
|
) {
|
|
aStateFlags |= nsIWebProgressListener.STATE_IS_NETWORK;
|
|
if (!gPersistObj) {
|
|
gPersistObj = {
|
|
result: aStatus,
|
|
currentState: nsIWebBrowserPersist.PERSIST_STATE_FINISHED,
|
|
};
|
|
}
|
|
}
|
|
|
|
// STATE_IS_NETWORK signals end of publishing, as does the gPersistObj.currentState
|
|
if (
|
|
aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK &&
|
|
gPersistObj.currentState == nsIWebBrowserPersist.PERSIST_STATE_FINISHED
|
|
) {
|
|
if (GetScheme(gPublishData.publishUrl) == "file") {
|
|
// XXX "file://" hack: We don't get notified about the HTML file, so end progress for it
|
|
// (This covers both "Case 1 and 2" described above)
|
|
gProgressDialog.SetProgressFinished(
|
|
gPublishData.filename,
|
|
gPersistObj.result
|
|
);
|
|
}
|
|
|
|
if (gPersistObj.result == 0) {
|
|
// All files are finished and publishing succeeded (some images may have failed)
|
|
try {
|
|
// Make a new docURI from the "browse location" in case "publish location" was FTP
|
|
// We need to set document uri before notifying listeners
|
|
var docUrl = GetDocUrlFromPublishData(gPublishData);
|
|
SetDocumentURI(
|
|
Services.io.newURI(docUrl, editor.documentCharacterSet)
|
|
);
|
|
|
|
UpdateWindowTitle();
|
|
|
|
// this should cause notification to listeners that doc has changed
|
|
editor.resetModificationCount();
|
|
|
|
// Set UI based on whether we're editing a remote or local url
|
|
// Why is urlstring undefined?
|
|
/* eslint-disable-next-line no-undef */
|
|
SetSaveAndPublishUI(urlstring);
|
|
} catch (e) {}
|
|
|
|
// Save publishData to prefs
|
|
if (gPublishData) {
|
|
if (gPublishData.savePublishData) {
|
|
// We published successfully, so we can safely
|
|
// save docDir and otherDir to prefs
|
|
gPublishData.saveDirs = true;
|
|
SavePublishDataToPrefs(gPublishData);
|
|
} else {
|
|
SavePassword(gPublishData);
|
|
}
|
|
}
|
|
|
|
// Ask progress dialog to close, but it may not
|
|
// if user checked checkbox to keep it open
|
|
gProgressDialog.RequestCloseDialog();
|
|
} else {
|
|
// We previously aborted publishing because of error:
|
|
// Calling gPersistObj.cancelSave() resulted in a non-zero gPersistObj.result,
|
|
// so notify progress dialog we're finished
|
|
gProgressDialog.SetProgressFinished(null, 0);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
/* eslint-enable complexity */
|
|
|
|
onProgressChange(
|
|
aWebProgress,
|
|
aRequest,
|
|
aCurSelfProgress,
|
|
aMaxSelfProgress,
|
|
aCurTotalProgress,
|
|
aMaxTotalProgress
|
|
) {
|
|
if (!gPersistObj) {
|
|
return;
|
|
}
|
|
|
|
if (gShowDebugOutputProgress) {
|
|
dump(
|
|
"\n onProgressChange: gPersistObj.result=" + gPersistObj.result + "\n"
|
|
);
|
|
try {
|
|
var channel = aRequest.QueryInterface(nsIChannel);
|
|
dump("***** onProgressChange request: " + channel.URI.spec + "\n");
|
|
} catch (e) {}
|
|
dump(
|
|
"***** self: " +
|
|
aCurSelfProgress +
|
|
" / " +
|
|
aMaxSelfProgress +
|
|
"\n"
|
|
);
|
|
dump(
|
|
"***** total: " +
|
|
aCurTotalProgress +
|
|
" / " +
|
|
aMaxTotalProgress +
|
|
"\n\n"
|
|
);
|
|
|
|
if (gPersistObj.currentState == gPersistObj.PERSIST_STATE_READY) {
|
|
dump(" Persister is ready to save data\n\n");
|
|
} else if (gPersistObj.currentState == gPersistObj.PERSIST_STATE_SAVING) {
|
|
dump(" Persister is saving data.\n\n");
|
|
} else if (
|
|
gPersistObj.currentState == gPersistObj.PERSIST_STATE_FINISHED
|
|
) {
|
|
dump(" PERSISTER HAS FINISHED SAVING DATA\n\n\n");
|
|
}
|
|
}
|
|
},
|
|
|
|
onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
|
|
if (gShowDebugOutputLocationChange) {
|
|
dump("***** onLocationChange: " + aLocation.spec + "\n");
|
|
try {
|
|
var channel = aRequest.QueryInterface(nsIChannel);
|
|
dump("***** request: " + channel.URI.spec + "\n");
|
|
} catch (e) {}
|
|
}
|
|
},
|
|
|
|
onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
|
|
if (gShowDebugOutputStatusChange) {
|
|
dump("***** onStatusChange: " + aMessage + "\n");
|
|
try {
|
|
var channel = aRequest.QueryInterface(nsIChannel);
|
|
dump("***** request: " + channel.URI.spec + "\n");
|
|
} catch (e) {
|
|
dump(" couldn't get request\n");
|
|
}
|
|
|
|
DumpDebugStatus(aStatus);
|
|
|
|
if (gPersistObj) {
|
|
if (gPersistObj.currentState == gPersistObj.PERSIST_STATE_READY) {
|
|
dump(" Persister is ready to save data\n\n");
|
|
} else if (
|
|
gPersistObj.currentState == gPersistObj.PERSIST_STATE_SAVING
|
|
) {
|
|
dump(" Persister is saving data.\n\n");
|
|
} else if (
|
|
gPersistObj.currentState == gPersistObj.PERSIST_STATE_FINISHED
|
|
) {
|
|
dump(" PERSISTER HAS FINISHED SAVING DATA\n\n\n");
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
onSecurityChange(aWebProgress, aRequest, state) {
|
|
if (gShowDebugOutputSecurityChange) {
|
|
try {
|
|
var channel = aRequest.QueryInterface(nsIChannel);
|
|
dump("***** onSecurityChange request: " + channel.URI.spec + "\n");
|
|
} catch (e) {}
|
|
}
|
|
},
|
|
|
|
onContentBlockingEvent(aWebProgress, aRequest, aEvent) {},
|
|
|
|
QueryInterface: ChromeUtils.generateQI([
|
|
"nsIWebProgressListener",
|
|
"nsISupportsWeakReference",
|
|
"nsIPrompt",
|
|
"nsIAuthPrompt",
|
|
]),
|
|
|
|
// nsIPrompt
|
|
alert(dlgTitle, text) {
|
|
Services.prompt.alert(
|
|
gProgressDialog ? gProgressDialog : window,
|
|
dlgTitle,
|
|
text
|
|
);
|
|
},
|
|
alertCheck(dialogTitle, text, checkBoxLabel, checkObj) {
|
|
Services.prompt.alert(window, dialogTitle, text);
|
|
},
|
|
confirm(dlgTitle, text) {
|
|
return ConfirmWithTitle(dlgTitle, text, null, null);
|
|
},
|
|
confirmCheck(dlgTitle, text, checkBoxLabel, checkObj) {
|
|
Services.prompt.confirmEx(
|
|
window,
|
|
dlgTitle,
|
|
text,
|
|
nsIPromptService.STD_OK_CANCEL_BUTTONS,
|
|
"",
|
|
"",
|
|
"",
|
|
checkBoxLabel,
|
|
checkObj
|
|
);
|
|
},
|
|
confirmEx(
|
|
dlgTitle,
|
|
text,
|
|
btnFlags,
|
|
btn0Title,
|
|
btn1Title,
|
|
btn2Title,
|
|
checkBoxLabel,
|
|
checkVal
|
|
) {
|
|
return Services.prompt.confirmEx(
|
|
window,
|
|
dlgTitle,
|
|
text,
|
|
btnFlags,
|
|
btn0Title,
|
|
btn1Title,
|
|
btn2Title,
|
|
checkBoxLabel,
|
|
checkVal
|
|
);
|
|
},
|
|
|
|
/** ***********************************************************************
|
|
* gEditorOutputProgressListener needs to implement both nsIPrompt *
|
|
* (providing alert) and nsIAuthPrompt (providing password saving). *
|
|
* Unfortunately, both interfaces specify prompt/promptPassword/ *
|
|
* promptUsernameAndPassword, albeit with conflicting method signatures. *
|
|
* Luckily, though, we only make use of their nsIAuthPrompt variants, *
|
|
* hence we can comment out the nsIPrompt ones here to avoid JavaScript *
|
|
* strict mode clutter. See bug 371174 for more information. *
|
|
*************************************************************************
|
|
prompt : function(dlgTitle, text, inoutText, checkBoxLabel, checkObj)
|
|
{
|
|
return Services.prompt.prompt(window, dlgTitle, text, inoutText, checkBoxLabel, checkObj);
|
|
},
|
|
promptPassword : function(dlgTitle, text, pwObj, checkBoxLabel, savePWObj)
|
|
{
|
|
var ret = false;
|
|
try {
|
|
// Note difference with nsIAuthPrompt::promptPassword, which has
|
|
// just "in" savePassword param, while nsIPrompt is "inout"
|
|
// Initialize with user's previous preference for this site
|
|
if (gPublishData)
|
|
savePWObj.value = gPublishData.savePassword;
|
|
|
|
ret = Services.prompt.promptPassword(gProgressDialog ? gProgressDialog : window,
|
|
dlgTitle, text, pwObj, checkBoxLabel, savePWObj);
|
|
|
|
if (!ret)
|
|
setTimeout(CancelPublishing, 0);
|
|
|
|
if (ret && gPublishData)
|
|
UpdateUsernamePasswordFromPrompt(gPublishData, gPublishData.username, pwObj.value, savePWObj.value);
|
|
} catch(e) {}
|
|
|
|
return ret;
|
|
},
|
|
promptUsernameAndPassword : function(dlgTitle, text, userObj, pwObj, checkBoxLabel, savePWObj)
|
|
{
|
|
var ret = PromptUsernameAndPassword(dlgTitle, text, savePWObj.value, userObj, pwObj);
|
|
if (!ret)
|
|
setTimeout(CancelPublishing, 0);
|
|
|
|
return ret;
|
|
},
|
|
*************************************************************************/
|
|
|
|
select(dlgTitle, text, selectList, outSelection) {
|
|
return Services.prompt.select(
|
|
window,
|
|
dlgTitle,
|
|
text,
|
|
selectList,
|
|
outSelection
|
|
);
|
|
},
|
|
|
|
// nsIAuthPrompt
|
|
prompt(dlgTitle, text, pwrealm, savePW, defaultText, result) {
|
|
var ret = Services.prompt.prompt(
|
|
gProgressDialog ? gProgressDialog : window,
|
|
dlgTitle,
|
|
text,
|
|
defaultText,
|
|
pwrealm,
|
|
savePWObj
|
|
);
|
|
if (!ret) {
|
|
setTimeout(CancelPublishing, 0);
|
|
}
|
|
return ret;
|
|
},
|
|
|
|
promptUsernameAndPassword(dlgTitle, text, pwrealm, savePW, userObj, pwObj) {
|
|
var ret = PromptUsernameAndPassword(dlgTitle, text, savePW, userObj, pwObj);
|
|
if (!ret) {
|
|
setTimeout(CancelPublishing, 0);
|
|
}
|
|
return ret;
|
|
},
|
|
|
|
promptPassword(dlgTitle, text, pwrealm, savePW, pwObj) {
|
|
var ret = false;
|
|
try {
|
|
// Note difference with nsIPrompt::promptPassword, which has
|
|
// "inout" savePassword param, while nsIAuthPrompt is just "in"
|
|
// Also nsIAuth doesn't supply "checkBoxLabel"
|
|
// Initialize with user's previous preference for this site
|
|
var savePWObj = { value: savePW };
|
|
// Initialize with user's previous preference for this site
|
|
if (gPublishData) {
|
|
savePWObj.value = gPublishData.savePassword;
|
|
}
|
|
|
|
ret = Services.prompt.promptPassword(
|
|
gProgressDialog ? gProgressDialog : window,
|
|
dlgTitle,
|
|
text,
|
|
pwObj,
|
|
GetString("SavePassword"),
|
|
savePWObj
|
|
);
|
|
|
|
if (!ret) {
|
|
setTimeout(CancelPublishing, 0);
|
|
}
|
|
|
|
if (ret && gPublishData) {
|
|
UpdateUsernamePasswordFromPrompt(
|
|
gPublishData,
|
|
gPublishData.username,
|
|
pwObj.value,
|
|
savePWObj.value
|
|
);
|
|
}
|
|
} catch (e) {}
|
|
|
|
return ret;
|
|
},
|
|
};
|
|
|
|
function PromptUsernameAndPassword(dlgTitle, text, savePW, userObj, pwObj) {
|
|
// HTTP prompts us twice even if user Cancels from 1st attempt!
|
|
// So never put up dialog if there's no publish data
|
|
if (!gPublishData) {
|
|
return false;
|
|
}
|
|
|
|
var ret = false;
|
|
try {
|
|
var savePWObj = { value: savePW };
|
|
|
|
// Initialize with user's previous preference for this site
|
|
if (gPublishData) {
|
|
// HTTP put uses this dialog if either username or password is bad,
|
|
// so prefill username input field with the previous value for modification
|
|
savePWObj.value = gPublishData.savePassword;
|
|
if (!userObj.value) {
|
|
userObj.value = gPublishData.username;
|
|
}
|
|
}
|
|
|
|
ret = Services.prompt.promptUsernameAndPassword(
|
|
gProgressDialog ? gProgressDialog : window,
|
|
dlgTitle,
|
|
text,
|
|
userObj,
|
|
pwObj,
|
|
GetString("SavePassword"),
|
|
savePWObj
|
|
);
|
|
if (ret && gPublishData) {
|
|
UpdateUsernamePasswordFromPrompt(
|
|
gPublishData,
|
|
userObj.value,
|
|
pwObj.value,
|
|
savePWObj.value
|
|
);
|
|
}
|
|
} catch (e) {}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* eslint-disable complexity */
|
|
function DumpDebugStatus(aStatus) {
|
|
// see nsError.h and netCore.h and ftpCore.h
|
|
|
|
if (aStatus == kErrorBindingAborted) {
|
|
dump("***** status is NS_BINDING_ABORTED\n");
|
|
} else if (aStatus == kErrorBindingRedirected) {
|
|
dump("***** status is NS_BINDING_REDIRECTED\n");
|
|
} else if (aStatus == 2152398859) {
|
|
// in netCore.h 11
|
|
dump("***** status is ALREADY_CONNECTED\n");
|
|
} else if (aStatus == 2152398860) {
|
|
// in netCore.h 12
|
|
dump("***** status is NOT_CONNECTED\n");
|
|
} else if (aStatus == 2152398861) {
|
|
// in nsISocketTransportService.idl 13
|
|
dump("***** status is CONNECTION_REFUSED\n");
|
|
} else if (aStatus == 2152398862) {
|
|
// in nsISocketTransportService.idl 14
|
|
dump("***** status is NET_TIMEOUT\n");
|
|
} else if (aStatus == 2152398863) {
|
|
// in netCore.h 15
|
|
dump("***** status is IN_PROGRESS\n");
|
|
} else if (aStatus == 2152398864) {
|
|
// 0x804b0010 in netCore.h 16
|
|
dump("***** status is OFFLINE\n");
|
|
} else if (aStatus == 2152398865) {
|
|
// in netCore.h 17
|
|
dump("***** status is NO_CONTENT\n");
|
|
} else if (aStatus == 2152398866) {
|
|
// in netCore.h 18
|
|
dump("***** status is UNKNOWN_PROTOCOL\n");
|
|
} else if (aStatus == 2152398867) {
|
|
// in netCore.h 19
|
|
dump("***** status is PORT_ACCESS_NOT_ALLOWED\n");
|
|
} else if (aStatus == 2152398868) {
|
|
// in nsISocketTransportService.idl 20
|
|
dump("***** status is NET_RESET\n");
|
|
} else if (aStatus == 2152398869) {
|
|
// in ftpCore.h 21
|
|
dump("***** status is FTP_LOGIN\n");
|
|
} else if (aStatus == 2152398870) {
|
|
// in ftpCore.h 22
|
|
dump("***** status is FTP_CWD\n");
|
|
} else if (aStatus == 2152398871) {
|
|
// in ftpCore.h 23
|
|
dump("***** status is FTP_PASV\n");
|
|
} else if (aStatus == 2152398872) {
|
|
// in ftpCore.h 24
|
|
dump("***** status is FTP_PWD\n");
|
|
} else if (aStatus == 2152857601) {
|
|
dump("***** status is UNRECOGNIZED_PATH\n");
|
|
} else if (aStatus == 2152857602) {
|
|
dump("***** status is UNRESOLABLE SYMLINK\n");
|
|
} else if (aStatus == 2152857604) {
|
|
dump("***** status is UNKNOWN_TYPE\n");
|
|
} else if (aStatus == 2152857605) {
|
|
dump("***** status is DESTINATION_NOT_DIR\n");
|
|
} else if (aStatus == 2152857606) {
|
|
dump("***** status is TARGET_DOES_NOT_EXIST\n");
|
|
} else if (aStatus == 2152857608) {
|
|
dump("***** status is ALREADY_EXISTS\n");
|
|
} else if (aStatus == 2152857609) {
|
|
dump("***** status is INVALID_PATH\n");
|
|
} else if (aStatus == 2152857610) {
|
|
dump("***** status is DISK_FULL\n");
|
|
} else if (aStatus == 2152857612) {
|
|
dump("***** status is NOT_DIRECTORY\n");
|
|
} else if (aStatus == 2152857613) {
|
|
dump("***** status is IS_DIRECTORY\n");
|
|
} else if (aStatus == 2152857614) {
|
|
dump("***** status is IS_LOCKED\n");
|
|
} else if (aStatus == 2152857615) {
|
|
dump("***** status is TOO_BIG\n");
|
|
} else if (aStatus == 2152857616) {
|
|
dump("***** status is NO_DEVICE_SPACE\n");
|
|
} else if (aStatus == 2152857617) {
|
|
dump("***** status is NAME_TOO_LONG\n");
|
|
} else if (aStatus == 2152857618) {
|
|
// 80520012
|
|
dump("***** status is FILE_NOT_FOUND\n");
|
|
} else if (aStatus == 2152857619) {
|
|
dump("***** status is READ_ONLY\n");
|
|
} else if (aStatus == 2152857620) {
|
|
dump("***** status is DIR_NOT_EMPTY\n");
|
|
} else if (aStatus == 2152857621) {
|
|
dump("***** status is ACCESS_DENIED\n");
|
|
} else if (aStatus == 2152398878) {
|
|
dump("***** status is ? (No connection or time out?)\n");
|
|
} else {
|
|
dump("***** status is " + aStatus + "\n");
|
|
}
|
|
}
|
|
/* eslint-enable complexity */
|
|
|
|
// Update any data that the user supplied in a prompt dialog
|
|
function UpdateUsernamePasswordFromPrompt(
|
|
publishData,
|
|
username,
|
|
password,
|
|
savePassword
|
|
) {
|
|
if (!publishData) {
|
|
return;
|
|
}
|
|
|
|
// Set flag to save publish data after publishing if it changed in dialog
|
|
// and the "SavePassword" checkbox was checked
|
|
// or we already had site data for this site
|
|
// (Thus we don't automatically create a site until user brings up Publish As dialog)
|
|
publishData.savePublishData =
|
|
(gPublishData.username != username || gPublishData.password != password) &&
|
|
(savePassword || !publishData.notInSiteData);
|
|
|
|
publishData.username = username;
|
|
publishData.password = password;
|
|
publishData.savePassword = savePassword;
|
|
}
|
|
|
|
const kSupportedTextMimeTypes = [
|
|
"text/plain",
|
|
"text/css",
|
|
"text/rdf",
|
|
"text/xsl",
|
|
"text/javascript",
|
|
"text/ecmascript",
|
|
"application/javascript",
|
|
"application/ecmascript",
|
|
"application/x-javascript",
|
|
"text/xul",
|
|
"application/vnd.mozilla.xul+xml",
|
|
];
|
|
|
|
function IsSupportedTextMimeType(aMimeType) {
|
|
for (var i = 0; i < kSupportedTextMimeTypes.length; i++) {
|
|
if (kSupportedTextMimeTypes[i] == aMimeType) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* eslint-disable complexity */
|
|
// throws an error or returns true if user attempted save; false if user canceled save
|
|
async function SaveDocument(aSaveAs, aSaveCopy, aMimeType) {
|
|
var editor = GetCurrentEditor();
|
|
if (!aMimeType || !editor) {
|
|
throw Cr.NS_ERROR_NOT_INITIALIZED;
|
|
}
|
|
|
|
var editorDoc = editor.document;
|
|
if (!editorDoc) {
|
|
throw Cr.NS_ERROR_NOT_INITIALIZED;
|
|
}
|
|
|
|
// if we don't have the right editor type bail (we handle text and html)
|
|
var editorType = GetCurrentEditorType();
|
|
if (!["text", "html", "htmlmail", "textmail"].includes(editorType)) {
|
|
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
var saveAsTextFile = IsSupportedTextMimeType(aMimeType);
|
|
|
|
// check if the file is to be saved is a format we don't understand; if so, bail
|
|
if (
|
|
aMimeType != kHTMLMimeType &&
|
|
aMimeType != kXHTMLMimeType &&
|
|
!saveAsTextFile
|
|
) {
|
|
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
if (saveAsTextFile) {
|
|
aMimeType = "text/plain";
|
|
}
|
|
|
|
var urlstring = GetDocumentUrl();
|
|
var mustShowFileDialog =
|
|
aSaveAs || IsUrlAboutBlank(urlstring) || urlstring == "";
|
|
|
|
// If editing a remote URL, force SaveAs dialog
|
|
if (!mustShowFileDialog && GetScheme(urlstring) != "file") {
|
|
mustShowFileDialog = true;
|
|
}
|
|
|
|
var doUpdateURI = false;
|
|
var tempLocalFile = null;
|
|
|
|
if (mustShowFileDialog) {
|
|
try {
|
|
// Prompt for title if we are saving to HTML
|
|
if (!saveAsTextFile && editorType == "html") {
|
|
var userContinuing = PromptAndSetTitleIfNone(); // not cancel
|
|
if (!userContinuing) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
var dialogResult = await PromptForSaveLocation(
|
|
saveAsTextFile,
|
|
editorType,
|
|
aMimeType,
|
|
urlstring
|
|
);
|
|
if (!dialogResult) {
|
|
return false;
|
|
}
|
|
|
|
// What is this unused 'replacing' var supposed to be doing?
|
|
/* eslint-disable-next-line no-unused-vars */
|
|
var replacing =
|
|
dialogResult.filepickerClick == nsIFilePicker.returnReplace;
|
|
|
|
urlstring = dialogResult.resultingURIString;
|
|
tempLocalFile = dialogResult.resultingLocalFile;
|
|
|
|
// update the new URL for the webshell unless we are saving a copy
|
|
if (!aSaveCopy) {
|
|
doUpdateURI = true;
|
|
}
|
|
} catch (e) {
|
|
Cu.reportError(e);
|
|
return false;
|
|
}
|
|
} // mustShowFileDialog
|
|
|
|
var success = true;
|
|
try {
|
|
// if somehow we didn't get a local file but we did get a uri,
|
|
// attempt to create the localfile if it's a "file" url
|
|
var docURI;
|
|
if (!tempLocalFile) {
|
|
docURI = Services.io.newURI(urlstring, editor.documentCharacterSet);
|
|
|
|
if (docURI.schemeIs("file")) {
|
|
var fileHandler = GetFileProtocolHandler();
|
|
tempLocalFile = fileHandler
|
|
.getFileFromURLSpec(urlstring)
|
|
.QueryInterface(Ci.nsIFile);
|
|
}
|
|
}
|
|
|
|
// this is the location where the related files will go
|
|
var relatedFilesDir = null;
|
|
|
|
// Only change links or move files if pref is set
|
|
// and we are saving to a new location
|
|
if (Services.prefs.getBoolPref("editor.save_associated_files") && aSaveAs) {
|
|
try {
|
|
if (tempLocalFile) {
|
|
// if we are saving to the same parent directory, don't set relatedFilesDir
|
|
// grab old location, chop off file
|
|
// grab new location, chop off file, compare
|
|
var oldLocation = GetDocumentUrl();
|
|
var oldLocationLastSlash = oldLocation.lastIndexOf("/");
|
|
if (oldLocationLastSlash != -1) {
|
|
oldLocation = oldLocation.slice(0, oldLocationLastSlash);
|
|
}
|
|
|
|
var relatedFilesDirStr = urlstring;
|
|
var newLocationLastSlash = relatedFilesDirStr.lastIndexOf("/");
|
|
if (newLocationLastSlash != -1) {
|
|
relatedFilesDirStr = relatedFilesDirStr.slice(
|
|
0,
|
|
newLocationLastSlash
|
|
);
|
|
}
|
|
if (
|
|
oldLocation == relatedFilesDirStr ||
|
|
IsUrlAboutBlank(oldLocation)
|
|
) {
|
|
relatedFilesDir = null;
|
|
} else {
|
|
relatedFilesDir = tempLocalFile.parent;
|
|
}
|
|
} else {
|
|
var lastSlash = urlstring.lastIndexOf("/");
|
|
if (lastSlash != -1) {
|
|
var relatedFilesDirString = urlstring.slice(0, lastSlash + 1); // include last slash
|
|
relatedFilesDir = Services.io.newURI(
|
|
relatedFilesDirString,
|
|
editor.documentCharacterSet
|
|
);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
relatedFilesDir = null;
|
|
}
|
|
}
|
|
|
|
let destinationLocation = tempLocalFile ? tempLocalFile : docURI;
|
|
|
|
success = OutputFileWithPersistAPI(
|
|
editorDoc,
|
|
destinationLocation,
|
|
relatedFilesDir,
|
|
aMimeType
|
|
);
|
|
} catch (e) {
|
|
success = false;
|
|
}
|
|
|
|
if (success) {
|
|
try {
|
|
if (doUpdateURI) {
|
|
// If a local file, we must create a new uri from nsIFile
|
|
if (tempLocalFile) {
|
|
docURI = GetFileProtocolHandler().newFileURI(tempLocalFile);
|
|
}
|
|
|
|
// We need to set new document uri before notifying listeners
|
|
SetDocumentURI(docURI);
|
|
}
|
|
|
|
// Update window title to show possibly different filename
|
|
// This also covers problem that after undoing a title change,
|
|
// window title loses the extra [filename] part that this adds
|
|
UpdateWindowTitle();
|
|
|
|
if (!aSaveCopy) {
|
|
editor.resetModificationCount();
|
|
}
|
|
// this should cause notification to listeners that document has changed
|
|
|
|
// Set UI based on whether we're editing a remote or local url
|
|
SetSaveAndPublishUI(urlstring);
|
|
} catch (e) {}
|
|
} else {
|
|
Services.prompt.alert(
|
|
window,
|
|
GetString("SaveDocument"),
|
|
GetString("SaveFileFailed")
|
|
);
|
|
}
|
|
return success;
|
|
}
|
|
/* eslint-enable complexity */
|
|
|
|
function SetDocumentURI(uri) {
|
|
try {
|
|
// XXX WE'LL NEED TO GET "CURRENT" CONTENT FRAME ONCE MULTIPLE EDITORS ARE ALLOWED
|
|
GetCurrentEditorElement().docShell.setCurrentURI(uri);
|
|
} catch (e) {
|
|
dump("SetDocumentURI:\n" + e + "\n");
|
|
}
|
|
}
|
|
|
|
// ------------------------------- Publishing
|
|
var gPublishData;
|
|
var gProgressDialog;
|
|
var gCommandAfterPublishing = null;
|
|
var gRestoreDocumentSource;
|
|
|
|
function Publish(publishData) {
|
|
if (!publishData) {
|
|
return false;
|
|
}
|
|
|
|
// Set data in global for username password requests
|
|
// and to do "post saving" actions after monitoring nsIWebProgressListener messages
|
|
// and we are sure file transfer was successful
|
|
gPublishData = publishData;
|
|
|
|
gPublishData.docURI = CreateURIFromPublishData(publishData, true);
|
|
if (!gPublishData.docURI) {
|
|
Services.prompt.alert(
|
|
window,
|
|
GetString("Publish"),
|
|
GetString("PublishFailed")
|
|
);
|
|
return false;
|
|
}
|
|
|
|
if (gPublishData.publishOtherFiles) {
|
|
gPublishData.otherFilesURI = CreateURIFromPublishData(publishData, false);
|
|
} else {
|
|
gPublishData.otherFilesURI = null;
|
|
}
|
|
|
|
if (gShowDebugOutputStateChange) {
|
|
dump(
|
|
"\n *** publishData: PublishUrl=" +
|
|
publishData.publishUrl +
|
|
", BrowseUrl=" +
|
|
publishData.browseUrl +
|
|
", Username=" +
|
|
publishData.username +
|
|
", Dir=" +
|
|
publishData.docDir +
|
|
", Filename=" +
|
|
publishData.filename +
|
|
"\n"
|
|
);
|
|
dump(
|
|
" * gPublishData.docURI.spec w/o pass=" +
|
|
StripPassword(gPublishData.docURI.spec) +
|
|
", PublishOtherFiles=" +
|
|
gPublishData.publishOtherFiles +
|
|
"\n"
|
|
);
|
|
}
|
|
|
|
// XXX Missing username will make FTP fail
|
|
// and it won't call us for prompt dialog (bug 132320)
|
|
// (It does prompt if just password is missing)
|
|
// So we should do the prompt ourselves before trying to publish
|
|
if (GetScheme(publishData.publishUrl) == "ftp" && !publishData.username) {
|
|
var message = GetString("PromptFTPUsernamePassword").replace(
|
|
/%host%/,
|
|
GetHost(publishData.publishUrl)
|
|
);
|
|
var savePWobj = { value: publishData.savePassword };
|
|
var userObj = { value: publishData.username };
|
|
var pwObj = { value: publishData.password };
|
|
if (
|
|
!PromptUsernameAndPassword(
|
|
GetString("Prompt"),
|
|
message,
|
|
savePWobj,
|
|
userObj,
|
|
pwObj
|
|
)
|
|
) {
|
|
// User canceled out of dialog.
|
|
return false;
|
|
}
|
|
|
|
// Reset data in URI objects
|
|
gPublishData.docURI.username = publishData.username;
|
|
gPublishData.docURI.password = publishData.password;
|
|
|
|
if (gPublishData.otherFilesURI) {
|
|
gPublishData.otherFilesURI.username = publishData.username;
|
|
gPublishData.otherFilesURI.password = publishData.password;
|
|
}
|
|
}
|
|
|
|
try {
|
|
// We launch dialog as a dependent
|
|
// Don't allow editing document!
|
|
SetDocumentEditable(false);
|
|
|
|
// Start progress monitoring
|
|
gProgressDialog = window.openDialog(
|
|
"chrome://editor/content/EditorPublishProgress.xhtml",
|
|
"_blank",
|
|
"chrome,dependent,titlebar",
|
|
gPublishData,
|
|
gPersistObj
|
|
);
|
|
} catch (e) {}
|
|
|
|
// Network transfer is often too quick for the progress dialog to be initialized
|
|
// and we can completely miss messages for quickly-terminated bad URLs,
|
|
// so we can't call OutputFileWithPersistAPI right away.
|
|
// StartPublishing() is called at the end of the dialog's onload method
|
|
return true;
|
|
}
|
|
|
|
function StartPublishing() {
|
|
var editor = GetCurrentEditor();
|
|
if (editor && gPublishData && gPublishData.docURI && gProgressDialog) {
|
|
gRestoreDocumentSource = null;
|
|
|
|
// Save backup document since nsIWebBrowserPersist changes image src urls
|
|
// but we only need to do this if publishing images and other related files
|
|
if (gPublishData.otherFilesURI) {
|
|
try {
|
|
gRestoreDocumentSource = editor.outputToString(
|
|
editor.contentsMIMEType,
|
|
kOutputEncodeW3CEntities
|
|
);
|
|
} catch (e) {}
|
|
}
|
|
|
|
OutputFileWithPersistAPI(
|
|
editor.document,
|
|
gPublishData.docURI,
|
|
gPublishData.otherFilesURI,
|
|
editor.contentsMIMEType
|
|
);
|
|
return gPersistObj;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function CancelPublishing() {
|
|
try {
|
|
gPersistObj.cancelSave();
|
|
gProgressDialog.SetProgressStatusCancel();
|
|
} catch (e) {}
|
|
|
|
// If canceling publishing do not do any commands after this
|
|
gCommandAfterPublishing = null;
|
|
|
|
if (gProgressDialog) {
|
|
// Close Progress dialog
|
|
// (this will call FinishPublishing())
|
|
gProgressDialog.CloseDialog();
|
|
} else {
|
|
FinishPublishing();
|
|
}
|
|
}
|
|
|
|
function FinishPublishing() {
|
|
SetDocumentEditable(true);
|
|
gProgressDialog = null;
|
|
gPublishData = null;
|
|
gRestoreDocumentSource = null;
|
|
|
|
if (gCommandAfterPublishing) {
|
|
// Be sure to null out the global now in case of trouble when executing command
|
|
var command = gCommandAfterPublishing;
|
|
gCommandAfterPublishing = null;
|
|
goDoCommand(command);
|
|
}
|
|
}
|
|
|
|
// Create a nsIURI object filled in with all required publishing info
|
|
function CreateURIFromPublishData(publishData, doDocUri) {
|
|
if (!publishData || !publishData.publishUrl) {
|
|
return null;
|
|
}
|
|
|
|
var URI;
|
|
try {
|
|
var spec = publishData.publishUrl;
|
|
if (doDocUri) {
|
|
spec += FormatDirForPublishing(publishData.docDir) + publishData.filename;
|
|
} else {
|
|
spec += FormatDirForPublishing(publishData.otherDir);
|
|
}
|
|
|
|
URI = Services.io.newURI(spec, GetCurrentEditor().documentCharacterSet);
|
|
|
|
if (publishData.username) {
|
|
URI.username = publishData.username;
|
|
}
|
|
if (publishData.password) {
|
|
URI.password = publishData.password;
|
|
}
|
|
} catch (e) {}
|
|
|
|
return URI;
|
|
}
|
|
|
|
// Resolve the correct "http:" document URL when publishing via ftp
|
|
function GetDocUrlFromPublishData(publishData) {
|
|
if (!publishData || !publishData.filename || !publishData.publishUrl) {
|
|
return "";
|
|
}
|
|
|
|
// If user was previously editing an "ftp" url, then keep that as the new scheme
|
|
var url;
|
|
|
|
// Always use the "HTTP" address if available
|
|
// XXX Should we do some more validation here for bad urls???
|
|
// Let's at least check for a scheme!
|
|
if (!GetScheme(publishData.browseUrl)) {
|
|
url = publishData.publishUrl;
|
|
} else {
|
|
url = publishData.browseUrl;
|
|
}
|
|
|
|
url += FormatDirForPublishing(publishData.docDir) + publishData.filename;
|
|
|
|
if (GetScheme(url) == "ftp") {
|
|
url = InsertUsernameIntoUrl(url, publishData.username);
|
|
}
|
|
|
|
return url;
|
|
}
|
|
|
|
function SetSaveAndPublishUI(urlstring) {
|
|
// Be sure enabled state of toolbar buttons are correct
|
|
goUpdateCommand("cmd_save");
|
|
goUpdateCommand("cmd_publish");
|
|
}
|
|
|
|
function SetDocumentEditable(isDocEditable) {
|
|
var editor = GetCurrentEditor();
|
|
if (editor && editor.document) {
|
|
try {
|
|
var flags = editor.flags;
|
|
editor.flags = isDocEditable
|
|
? (flags &= ~Ci.nsIEditor.eEditorReadonlyMask)
|
|
: flags | Ci.nsIEditor.eEditorReadonlyMask;
|
|
} catch (e) {}
|
|
|
|
// update all commands
|
|
window.updateCommands("create");
|
|
}
|
|
}
|
|
|
|
// ****** end of save / publish **********//
|
|
|
|
var nsPublishSettingsCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
if (GetCurrentEditor()) {
|
|
// Launch Publish Settings dialog
|
|
|
|
window.ok = window.openDialog(
|
|
"chrome://editor/content/EditorPublishSettings.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal",
|
|
""
|
|
);
|
|
return window.ok;
|
|
}
|
|
return false;
|
|
},
|
|
};
|
|
|
|
var nsRevertCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return (
|
|
IsDocumentEditable() &&
|
|
IsDocumentModified() &&
|
|
!IsUrlAboutBlank(GetDocumentUrl())
|
|
);
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
// Confirm with the user to abandon current changes
|
|
// Put the page title in the message string
|
|
let title = GetDocumentTitle();
|
|
let msg = GetString("AbandonChanges").replace(/%title%/, title);
|
|
|
|
let result = Services.prompt.confirmEx(
|
|
window,
|
|
GetString("RevertCaption"),
|
|
msg,
|
|
Services.prompt.BUTTON_TITLE_REVERT * Services.prompt.BUTTON_POS_0 +
|
|
Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1,
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
{ value: 0 }
|
|
);
|
|
|
|
// Reload page if first button (Revert) was pressed
|
|
if (result == 0) {
|
|
CancelHTMLSource();
|
|
EditorLoadUrl(GetDocumentUrl());
|
|
}
|
|
},
|
|
};
|
|
|
|
var nsCloseCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return GetCurrentEditor() != null;
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
CloseWindow();
|
|
},
|
|
};
|
|
|
|
async function CloseWindow() {
|
|
// Check to make sure document is saved. "true" means allow "Don't Save" button,
|
|
// so user can choose to close without saving
|
|
if (await CheckAndSaveDocument("cmd_close", true)) {
|
|
if (window.InsertCharWindow) {
|
|
SwitchInsertCharToAnotherEditorOrClose();
|
|
}
|
|
|
|
try {
|
|
var basewin = window
|
|
.getInterface(Ci.nsIWebNavigation)
|
|
.QueryInterface(Ci.nsIDocShellTreeItem)
|
|
.treeOwner.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIBaseWindow);
|
|
basewin.destroy();
|
|
} catch (e) {}
|
|
}
|
|
}
|
|
|
|
var nsOpenRemoteCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
// We can always do this.
|
|
return true;
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
var params = { action: "2", url: "" };
|
|
openDialog(
|
|
"chrome://communicator/content/openLocation.xhtml",
|
|
"_blank",
|
|
"chrome,modal,titlebar",
|
|
params
|
|
);
|
|
var win = getTopWin();
|
|
switch (params.action) {
|
|
case "0": // current window
|
|
win.focus();
|
|
win.loadURI(params.url, null, null, true);
|
|
break;
|
|
case "1": // new window
|
|
openDialog(
|
|
getBrowserURL(),
|
|
"_blank",
|
|
"all,dialog=no",
|
|
params.url,
|
|
null,
|
|
null,
|
|
null,
|
|
true
|
|
);
|
|
break;
|
|
case "2": // edit
|
|
editPage(params.url);
|
|
break;
|
|
case "3": // new tab
|
|
win.focus();
|
|
var browser = win.getBrowser();
|
|
browser.selectedTab = browser.addTab(params.url, {
|
|
allowThirdPartyFixup: true,
|
|
});
|
|
break;
|
|
case "4": // private
|
|
openNewPrivateWith(params.url);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
},
|
|
};
|
|
|
|
var nsPreviewCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return (
|
|
IsDocumentEditable() &&
|
|
IsHTMLEditor() &&
|
|
(DocumentHasBeenSaved() || IsDocumentModified())
|
|
);
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
async doCommand(aCommand) {
|
|
// Don't continue if user canceled during prompt for saving
|
|
// DocumentHasBeenSaved will test if we have a URL and suppress "Don't Save" button if not
|
|
if (!(await CheckAndSaveDocument("cmd_preview", DocumentHasBeenSaved()))) {
|
|
return;
|
|
}
|
|
|
|
// Check if we saved again just in case?
|
|
if (DocumentHasBeenSaved()) {
|
|
let browser;
|
|
try {
|
|
// Find a browser with this URL
|
|
let enumerator = Services.wm.getEnumerator("navigator:browser");
|
|
|
|
var documentURI = GetDocumentUrl();
|
|
while (enumerator.hasMoreElements()) {
|
|
browser = enumerator.getNext();
|
|
if (
|
|
browser &&
|
|
!browser.closed &&
|
|
documentURI == browser.getBrowser().currentURI.spec
|
|
) {
|
|
break;
|
|
}
|
|
|
|
browser = null;
|
|
}
|
|
} catch (ex) {}
|
|
|
|
// If none found, open a new browser
|
|
if (!browser) {
|
|
browser = window.openDialog(
|
|
getBrowserURL(),
|
|
"_blank",
|
|
"chrome,all,dialog=no",
|
|
documentURI
|
|
);
|
|
} else {
|
|
try {
|
|
browser.BrowserReloadSkipCache();
|
|
browser.focus();
|
|
} catch (ex) {}
|
|
}
|
|
}
|
|
},
|
|
};
|
|
|
|
var nsSendPageCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return (
|
|
IsDocumentEditable() && (DocumentHasBeenSaved() || IsDocumentModified())
|
|
);
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
async doCommand(aCommand) {
|
|
// Don't continue if user canceled during prompt for saving
|
|
// DocumentHasBeenSaved will test if we have a URL and suppress "Don't Save" button if not
|
|
if (
|
|
!(await CheckAndSaveDocument("cmd_editSendPage", DocumentHasBeenSaved()))
|
|
) {
|
|
return;
|
|
}
|
|
|
|
// Check if we saved again just in case?
|
|
if (DocumentHasBeenSaved()) {
|
|
// Launch Messenger Composer window with current page as contents
|
|
try {
|
|
openComposeWindow(GetDocumentUrl(), GetDocumentTitle());
|
|
} catch (ex) {
|
|
dump("Cannot Send Page: " + ex + "\n");
|
|
}
|
|
}
|
|
},
|
|
};
|
|
|
|
var nsPrintCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return true; // we can always do this
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
// In editor.js
|
|
SetEditMode(gPreviousNonSourceDisplayMode);
|
|
try {
|
|
let browser = GetCurrentEditorElement();
|
|
PrintUtils.printWindow(browser.outerWindowID, browser);
|
|
} catch (e) {}
|
|
},
|
|
};
|
|
|
|
var nsPrintPreviewCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
// We can always do this.
|
|
return true;
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
// In editor.js
|
|
SetEditMode(gPreviousNonSourceDisplayMode);
|
|
try {
|
|
PrintUtils.printPreview(PrintPreviewListener);
|
|
} catch (e) {}
|
|
},
|
|
};
|
|
|
|
var nsPrintSetupCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return true; // we can always do this
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
// In editor.js
|
|
SetEditMode(gPreviousNonSourceDisplayMode);
|
|
PrintUtils.showPageSetup();
|
|
},
|
|
};
|
|
|
|
var nsFindReplaceCommand = {
|
|
isCommandEnabled(aCommand, editorElement) {
|
|
return editorElement.getEditor(editorElement.contentWindow) != null;
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, editorElement) {},
|
|
doCommandParams(aCommand, aParams, editorElement) {},
|
|
|
|
doCommand(aCommand, editorElement) {
|
|
window.openDialog(
|
|
"chrome://editor/content/EdReplace.xhtml",
|
|
"_blank",
|
|
"chrome,modal,titlebar",
|
|
editorElement
|
|
);
|
|
},
|
|
};
|
|
|
|
var nsFindCommand = {
|
|
isCommandEnabled(aCommand, editorElement) {
|
|
return editorElement.getEditor(editorElement.contentWindow) != null;
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, editorElement) {},
|
|
doCommandParams(aCommand, aParams, editorElement) {},
|
|
|
|
doCommand(aCommand, editorElement) {
|
|
document.getElementById("FindToolbar").onFindCommand();
|
|
},
|
|
};
|
|
|
|
var nsFindAgainCommand = {
|
|
isCommandEnabled(aCommand, editorElement) {
|
|
// we can only do this if the search pattern is non-empty. Not sure how
|
|
// to get that from here
|
|
return editorElement.getEditor(editorElement.contentWindow) != null;
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, editorElement) {},
|
|
doCommandParams(aCommand, aParams, editorElement) {},
|
|
|
|
doCommand(aCommand, editorElement) {
|
|
let findPrev = aCommand == "cmd_findPrev";
|
|
document.getElementById("FindToolbar").onFindAgainCommand(findPrev);
|
|
},
|
|
};
|
|
|
|
var nsRewrapCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return (
|
|
IsDocumentEditable() &&
|
|
!IsInHTMLSourceMode() &&
|
|
GetCurrentEditor() instanceof Ci.nsIEditorMailSupport
|
|
);
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
GetCurrentEditor()
|
|
.QueryInterface(Ci.nsIEditorMailSupport)
|
|
.rewrap(false);
|
|
},
|
|
};
|
|
|
|
var nsSpellingCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return (
|
|
IsDocumentEditable() && !IsInHTMLSourceMode() && IsSpellCheckerInstalled()
|
|
);
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
window.cancelSendMessage = false;
|
|
try {
|
|
var skipBlockQuotes =
|
|
window.document.documentElement.getAttribute("windowtype") ==
|
|
"msgcompose";
|
|
window.openDialog(
|
|
"chrome://editor/content/EdSpellCheck.xhtml",
|
|
"_blank",
|
|
"dialog,close,titlebar,modal,resizable",
|
|
false,
|
|
skipBlockQuotes,
|
|
true
|
|
);
|
|
} catch (ex) {}
|
|
},
|
|
};
|
|
|
|
// Validate using http://validator.w3.org/file-upload.html
|
|
var URL2Validate;
|
|
var nsValidateCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return GetCurrentEditor() != null;
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
async doCommand(aCommand) {
|
|
// If the document hasn't been modified,
|
|
// then just validate the current url.
|
|
if (IsDocumentModified() || IsHTMLSourceChanged()) {
|
|
if (!(await CheckAndSaveDocument("cmd_validate", false))) {
|
|
return;
|
|
}
|
|
|
|
// Check if we saved again just in case?
|
|
if (!DocumentHasBeenSaved()) {
|
|
// user hit cancel?
|
|
return;
|
|
}
|
|
}
|
|
|
|
URL2Validate = GetDocumentUrl();
|
|
// See if it's a file:
|
|
var ifile;
|
|
try {
|
|
var fileHandler = GetFileProtocolHandler();
|
|
ifile = fileHandler.getFileFromURLSpec(URL2Validate);
|
|
// nsIFile throws an exception if it's not a file url
|
|
} catch (e) {
|
|
ifile = null;
|
|
}
|
|
if (ifile) {
|
|
URL2Validate = ifile.path;
|
|
var vwin = window.open(
|
|
"http://validator.w3.org/file-upload.html",
|
|
"EditorValidate"
|
|
);
|
|
// Window loads asynchronously, so pass control to the load listener:
|
|
vwin.addEventListener("load", this.validateFilePageLoaded);
|
|
} else {
|
|
window.open(
|
|
`http://validator.w3.org/check?uri=${URL2Validate}&doctype=Inline`,
|
|
"EditorValidate"
|
|
);
|
|
// This does the validation, no need to wait for page loaded.
|
|
}
|
|
},
|
|
validateFilePageLoaded(event) {
|
|
event.target.forms[0].uploaded_file.value = URL2Validate;
|
|
},
|
|
};
|
|
|
|
var nsFormCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
window.openDialog(
|
|
"chrome://editor/content/EdFormProps.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal"
|
|
);
|
|
},
|
|
};
|
|
|
|
var nsInputTagCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
window.openDialog(
|
|
"chrome://editor/content/EdInputProps.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal"
|
|
);
|
|
},
|
|
};
|
|
|
|
var nsInputImageCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
window.openDialog(
|
|
"chrome://editor/content/EdInputImage.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal"
|
|
);
|
|
},
|
|
};
|
|
|
|
var nsTextAreaCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
window.openDialog(
|
|
"chrome://editor/content/EdTextAreaProps.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal"
|
|
);
|
|
},
|
|
};
|
|
|
|
var nsSelectCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
window.openDialog(
|
|
"chrome://editor/content/EdSelectProps.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal"
|
|
);
|
|
},
|
|
};
|
|
|
|
var nsButtonCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
window.openDialog(
|
|
"chrome://editor/content/EdButtonProps.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal"
|
|
);
|
|
},
|
|
};
|
|
|
|
var nsLabelCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
var tagName = "label";
|
|
try {
|
|
var editor = GetCurrentEditor();
|
|
// Find selected label or if start/end of selection is in label
|
|
var labelElement = editor.getSelectedElement(tagName);
|
|
if (!labelElement) {
|
|
labelElement = editor.getElementOrParentByTagName(
|
|
tagName,
|
|
editor.selection.anchorNode
|
|
);
|
|
}
|
|
if (!labelElement) {
|
|
labelElement = editor.getElementOrParentByTagName(
|
|
tagName,
|
|
editor.selection.focusNode
|
|
);
|
|
}
|
|
if (labelElement) {
|
|
// We only open the dialog for an existing label
|
|
window.openDialog(
|
|
"chrome://editor/content/EdLabelProps.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal",
|
|
labelElement
|
|
);
|
|
} else {
|
|
EditorSetTextProperty(tagName, "", "");
|
|
}
|
|
} catch (e) {}
|
|
},
|
|
};
|
|
|
|
var nsFieldSetCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
window.openDialog(
|
|
"chrome://editor/content/EdFieldSetProps.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal"
|
|
);
|
|
},
|
|
};
|
|
|
|
var nsIsIndexCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
try {
|
|
var editor = GetCurrentEditor();
|
|
var isindexElement = editor.createElementWithDefaults("isindex");
|
|
isindexElement.setAttribute(
|
|
"prompt",
|
|
editor.outputToString("text/plain", kOutputSelectionOnly)
|
|
);
|
|
editor.insertElementAtSelection(isindexElement, true);
|
|
} catch (e) {}
|
|
},
|
|
};
|
|
|
|
var nsImageCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
window.openDialog(
|
|
"chrome://editor/content/EdImageProps.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal"
|
|
);
|
|
},
|
|
};
|
|
|
|
var nsHLineCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
// Inserting an HLine is different in that we don't use properties dialog
|
|
// unless we are editing an existing line's attributes
|
|
// We get the last-used attributes from the prefs and insert immediately
|
|
|
|
var tagName = "hr";
|
|
var editor = GetCurrentEditor();
|
|
|
|
var hLine;
|
|
try {
|
|
hLine = editor.getSelectedElement(tagName);
|
|
} catch (e) {
|
|
return;
|
|
}
|
|
|
|
if (hLine) {
|
|
// We only open the dialog for an existing HRule
|
|
window.openDialog(
|
|
"chrome://editor/content/EdHLineProps.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal"
|
|
);
|
|
} else {
|
|
try {
|
|
hLine = editor.createElementWithDefaults(tagName);
|
|
|
|
// We change the default attributes to those saved in the user prefs
|
|
let align = Services.prefs.getIntPref("editor.hrule.align");
|
|
if (align == 0) {
|
|
editor.setAttributeOrEquivalent(hLine, "align", "left", true);
|
|
} else if (align == 2) {
|
|
editor.setAttributeOrEquivalent(hLine, "align", "right", true);
|
|
}
|
|
|
|
// Note: Default is center (don't write attribute)
|
|
|
|
let width = Services.prefs.getIntPref("editor.hrule.width");
|
|
if (Services.prefs.getBoolPref("editor.hrule.width_percent")) {
|
|
width = width + "%";
|
|
}
|
|
|
|
editor.setAttributeOrEquivalent(hLine, "width", width, true);
|
|
|
|
let height = Services.prefs.getIntPref("editor.hrule.height");
|
|
editor.setAttributeOrEquivalent(hLine, "size", String(height), true);
|
|
|
|
if (Services.prefs.getBoolPref("editor.hrule.shading")) {
|
|
hLine.removeAttribute("noshade");
|
|
} else {
|
|
hLine.setAttribute("noshade", "noshade");
|
|
}
|
|
|
|
editor.insertElementAtSelection(hLine, true);
|
|
} catch (e) {}
|
|
}
|
|
},
|
|
};
|
|
|
|
var nsLinkCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
// If selected element is an image, launch that dialog instead
|
|
// since last tab panel handles link around an image
|
|
var element = GetObjectForProperties();
|
|
if (element && element.nodeName.toLowerCase() == "img") {
|
|
window.openDialog(
|
|
"chrome://editor/content/EdImageProps.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal",
|
|
null,
|
|
true
|
|
);
|
|
} else {
|
|
window.openDialog(
|
|
"chrome://editor/content/EdLinkProps.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal"
|
|
);
|
|
}
|
|
},
|
|
};
|
|
|
|
var nsAnchorCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
window.openDialog(
|
|
"chrome://editor/content/EdNamedAnchorProps.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal",
|
|
""
|
|
);
|
|
},
|
|
};
|
|
|
|
var nsInsertHTMLWithDialogCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
window.openDialog(
|
|
"chrome://editor/content/EdInsSrc.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal,resizable",
|
|
""
|
|
);
|
|
},
|
|
};
|
|
|
|
var nsInsertMathWithDialogCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
window.openDialog(
|
|
"chrome://editor/content/EdInsertMath.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal,resizable",
|
|
""
|
|
);
|
|
},
|
|
};
|
|
|
|
var nsInsertCharsCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
EditorFindOrCreateInsertCharWindow();
|
|
},
|
|
};
|
|
|
|
var nsInsertBreakCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
try {
|
|
GetCurrentEditor().insertHTML("<br>");
|
|
} catch (e) {}
|
|
},
|
|
};
|
|
|
|
var nsInsertBreakAllCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
try {
|
|
GetCurrentEditor().insertHTML("<br clear='all'>");
|
|
} catch (e) {}
|
|
},
|
|
};
|
|
|
|
var nsGridCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
window.openDialog(
|
|
"chrome://editor/content/EdSnapToGrid.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal"
|
|
);
|
|
},
|
|
};
|
|
|
|
var nsListPropertiesCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
window.openDialog(
|
|
"chrome://editor/content/EdListProps.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal"
|
|
);
|
|
},
|
|
};
|
|
|
|
var nsPagePropertiesCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
var oldTitle = GetDocumentTitle();
|
|
window.openDialog(
|
|
"chrome://editor/content/EdPageProps.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal",
|
|
""
|
|
);
|
|
|
|
// Update main window title and
|
|
// recent menu data in prefs if doc title changed
|
|
if (GetDocumentTitle() != oldTitle) {
|
|
UpdateWindowTitle();
|
|
}
|
|
},
|
|
};
|
|
|
|
var nsObjectPropertiesCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
var isEnabled = false;
|
|
if (IsDocumentEditable() && IsEditingRenderedHTML()) {
|
|
isEnabled =
|
|
GetObjectForProperties() != null ||
|
|
GetCurrentEditor().getSelectedElement("href") != null;
|
|
}
|
|
return isEnabled;
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
// Launch Object properties for appropriate selected element
|
|
var element = GetObjectForProperties();
|
|
if (element) {
|
|
var name = element.nodeName.toLowerCase();
|
|
switch (name) {
|
|
case "img":
|
|
goDoCommand("cmd_image");
|
|
break;
|
|
case "hr":
|
|
goDoCommand("cmd_hline");
|
|
break;
|
|
case "form":
|
|
goDoCommand("cmd_form");
|
|
break;
|
|
case "input":
|
|
var type = element.getAttribute("type");
|
|
if (type && type.toLowerCase() == "image") {
|
|
goDoCommand("cmd_inputimage");
|
|
} else {
|
|
goDoCommand("cmd_inputtag");
|
|
}
|
|
break;
|
|
case "textarea":
|
|
goDoCommand("cmd_textarea");
|
|
break;
|
|
case "select":
|
|
goDoCommand("cmd_select");
|
|
break;
|
|
case "button":
|
|
goDoCommand("cmd_button");
|
|
break;
|
|
case "label":
|
|
goDoCommand("cmd_label");
|
|
break;
|
|
case "fieldset":
|
|
goDoCommand("cmd_fieldset");
|
|
break;
|
|
case "table":
|
|
EditorInsertOrEditTable(false);
|
|
break;
|
|
case "td":
|
|
case "th":
|
|
EditorTableCellProperties();
|
|
break;
|
|
case "ol":
|
|
case "ul":
|
|
case "dl":
|
|
case "li":
|
|
goDoCommand("cmd_listProperties");
|
|
break;
|
|
case "a":
|
|
if (element.name) {
|
|
goDoCommand("cmd_anchor");
|
|
} else if (element.href) {
|
|
goDoCommand("cmd_link");
|
|
}
|
|
break;
|
|
case "math":
|
|
goDoCommand("cmd_insertMathWithDialog");
|
|
break;
|
|
default:
|
|
doAdvancedProperties(element);
|
|
break;
|
|
}
|
|
} else {
|
|
// We get a partially-selected link if asked for specifically
|
|
try {
|
|
element = GetCurrentEditor().getSelectedElement("href");
|
|
} catch (e) {}
|
|
if (element) {
|
|
goDoCommand("cmd_link");
|
|
}
|
|
}
|
|
},
|
|
};
|
|
|
|
var nsSetSmiley = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {
|
|
var smileyCode = aParams.getStringValue("state_attribute");
|
|
|
|
var strSml;
|
|
switch (smileyCode) {
|
|
case ":-)":
|
|
strSml = "s1";
|
|
break;
|
|
case ":-(":
|
|
strSml = "s2";
|
|
break;
|
|
case ";-)":
|
|
strSml = "s3";
|
|
break;
|
|
case ":-P":
|
|
case ":-p":
|
|
case ":-b":
|
|
strSml = "s4";
|
|
break;
|
|
case ":-D":
|
|
strSml = "s5";
|
|
break;
|
|
case ":-[":
|
|
strSml = "s6";
|
|
break;
|
|
case ":-/":
|
|
case ":/":
|
|
case ":-\\":
|
|
case ":\\":
|
|
strSml = "s7";
|
|
break;
|
|
case "=-O":
|
|
case "=-o":
|
|
strSml = "s8";
|
|
break;
|
|
case ":-*":
|
|
strSml = "s9";
|
|
break;
|
|
case ">:o":
|
|
case ">:-o":
|
|
strSml = "s10";
|
|
break;
|
|
case "8-)":
|
|
strSml = "s11";
|
|
break;
|
|
case ":-$":
|
|
strSml = "s12";
|
|
break;
|
|
case ":-!":
|
|
strSml = "s13";
|
|
break;
|
|
case "O:-)":
|
|
case "o:-)":
|
|
strSml = "s14";
|
|
break;
|
|
case ":'(":
|
|
strSml = "s15";
|
|
break;
|
|
case ":-X":
|
|
case ":-x":
|
|
strSml = "s16";
|
|
break;
|
|
default:
|
|
strSml = "";
|
|
break;
|
|
}
|
|
|
|
try {
|
|
var editor = GetCurrentEditor();
|
|
var extElement = editor.createElementWithDefaults("span");
|
|
extElement.setAttribute("class", "moz-smiley-" + strSml);
|
|
|
|
var intElement = editor.createElementWithDefaults("span");
|
|
if (!intElement) {
|
|
return;
|
|
}
|
|
|
|
var txtElement = editor.document.createTextNode(smileyCode);
|
|
if (!txtElement) {
|
|
return;
|
|
}
|
|
|
|
intElement.appendChild(txtElement);
|
|
extElement.appendChild(intElement);
|
|
|
|
editor.insertElementAtSelection(extElement, true);
|
|
window.content.focus();
|
|
} catch (e) {
|
|
dump("Exception occurred in smiley InsertElementAtSelection\n");
|
|
}
|
|
},
|
|
// This is now deprecated in favor of "doCommandParams"
|
|
doCommand(aCommand) {},
|
|
};
|
|
|
|
function doAdvancedProperties(element) {
|
|
if (element) {
|
|
window.openDialog(
|
|
"chrome://editor/content/EdAdvancedEdit.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal,resizable=yes",
|
|
"",
|
|
element
|
|
);
|
|
}
|
|
}
|
|
|
|
var nsAdvancedPropertiesCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
// Launch AdvancedEdit dialog for the selected element
|
|
try {
|
|
var element = GetCurrentEditor().getSelectedElement("");
|
|
doAdvancedProperties(element);
|
|
} catch (e) {}
|
|
},
|
|
};
|
|
|
|
var nsColorPropertiesCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
window.openDialog(
|
|
"chrome://editor/content/EdColorProps.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal",
|
|
""
|
|
);
|
|
UpdateDefaultColors();
|
|
},
|
|
};
|
|
|
|
var nsIncreaseFontCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
if (!(IsDocumentEditable() && IsEditingRenderedHTML())) {
|
|
return false;
|
|
}
|
|
var setIndex = getFontSizeIndex();
|
|
return setIndex >= 0 && setIndex < 5;
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
var setIndex = getFontSizeIndex();
|
|
if (setIndex < 0 || setIndex >= 5) {
|
|
return;
|
|
}
|
|
var sizes = ["x-small", "small", "medium", "large", "x-large", "xx-large"];
|
|
EditorSetFontSize(sizes[setIndex + 1]);
|
|
},
|
|
};
|
|
|
|
var nsDecreaseFontCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
if (!(IsDocumentEditable() && IsEditingRenderedHTML())) {
|
|
return false;
|
|
}
|
|
var setIndex = getFontSizeIndex();
|
|
return setIndex > 0;
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
var setIndex = getFontSizeIndex();
|
|
if (setIndex <= 0) {
|
|
return;
|
|
}
|
|
var sizes = ["x-small", "small", "medium", "large", "x-large", "xx-large"];
|
|
EditorSetFontSize(sizes[setIndex - 1]);
|
|
},
|
|
};
|
|
|
|
var nsRemoveNamedAnchorsCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
// We could see if there's any link in selection, but it doesn't seem worth the work!
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
EditorRemoveTextProperty("name", "");
|
|
window.content.focus();
|
|
},
|
|
};
|
|
|
|
var nsEditLinkCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
// Not really used -- this command is only in context menu, and we do enabling there
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
try {
|
|
var element = GetCurrentEditor().getSelectedElement("href");
|
|
if (element) {
|
|
editPage(element.href);
|
|
}
|
|
} catch (e) {}
|
|
window.content.focus();
|
|
},
|
|
};
|
|
|
|
var nsNormalModeCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsHTMLEditor() && IsDocumentEditable();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
SetEditMode(kDisplayModeNormal);
|
|
},
|
|
};
|
|
|
|
var nsAllTagsModeCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsHTMLEditor();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
SetEditMode(kDisplayModeAllTags);
|
|
},
|
|
};
|
|
|
|
var nsHTMLSourceModeCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsHTMLEditor();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
SetEditMode(kDisplayModeSource);
|
|
},
|
|
};
|
|
|
|
var nsPreviewModeCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsHTMLEditor();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
SetEditMode(kDisplayModePreview);
|
|
},
|
|
};
|
|
|
|
var nsInsertOrEditTableCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
if (IsInTableCell()) {
|
|
EditorTableCellProperties();
|
|
} else {
|
|
EditorInsertOrEditTable(true);
|
|
}
|
|
},
|
|
};
|
|
|
|
var nsEditTableCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsInTable();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
EditorInsertOrEditTable(false);
|
|
},
|
|
};
|
|
|
|
var nsSelectTableCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsInTable();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
try {
|
|
GetCurrentTableEditor().selectTable();
|
|
} catch (e) {}
|
|
window.content.focus();
|
|
},
|
|
};
|
|
|
|
var nsSelectTableRowCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsInTableCell();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
try {
|
|
GetCurrentTableEditor().selectTableRow();
|
|
} catch (e) {}
|
|
window.content.focus();
|
|
},
|
|
};
|
|
|
|
var nsSelectTableColumnCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsInTableCell();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
try {
|
|
GetCurrentTableEditor().selectTableColumn();
|
|
} catch (e) {}
|
|
window.content.focus();
|
|
},
|
|
};
|
|
|
|
var nsSelectTableCellCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsInTableCell();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
try {
|
|
GetCurrentTableEditor().selectTableCell();
|
|
} catch (e) {}
|
|
window.content.focus();
|
|
},
|
|
};
|
|
|
|
var nsSelectAllTableCellsCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsInTable();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
try {
|
|
GetCurrentTableEditor().selectAllTableCells();
|
|
} catch (e) {}
|
|
window.content.focus();
|
|
},
|
|
};
|
|
|
|
var nsInsertTableCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsDocumentEditable() && IsEditingRenderedHTML();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
EditorInsertTable();
|
|
},
|
|
};
|
|
|
|
var nsInsertTableRowAboveCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsInTableCell();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
try {
|
|
GetCurrentTableEditor().insertTableRow(1, false);
|
|
} catch (e) {}
|
|
window.content.focus();
|
|
},
|
|
};
|
|
|
|
var nsInsertTableRowBelowCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsInTableCell();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
try {
|
|
GetCurrentTableEditor().insertTableRow(1, true);
|
|
} catch (e) {}
|
|
window.content.focus();
|
|
},
|
|
};
|
|
|
|
var nsInsertTableColumnBeforeCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsInTableCell();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
try {
|
|
GetCurrentTableEditor().insertTableColumn(1, false);
|
|
} catch (e) {}
|
|
window.content.focus();
|
|
},
|
|
};
|
|
|
|
var nsInsertTableColumnAfterCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsInTableCell();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
try {
|
|
GetCurrentTableEditor().insertTableColumn(1, true);
|
|
} catch (e) {}
|
|
window.content.focus();
|
|
},
|
|
};
|
|
|
|
var nsInsertTableCellBeforeCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsInTableCell();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
try {
|
|
GetCurrentTableEditor().insertTableCell(1, false);
|
|
} catch (e) {}
|
|
window.content.focus();
|
|
},
|
|
};
|
|
|
|
var nsInsertTableCellAfterCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsInTableCell();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
try {
|
|
GetCurrentTableEditor().insertTableCell(1, true);
|
|
} catch (e) {}
|
|
window.content.focus();
|
|
},
|
|
};
|
|
|
|
var nsDeleteTableCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsInTable();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
try {
|
|
GetCurrentTableEditor().deleteTable();
|
|
} catch (e) {}
|
|
window.content.focus();
|
|
},
|
|
};
|
|
|
|
var nsDeleteTableRowCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsInTableCell();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
var rows = GetNumberOfContiguousSelectedRows();
|
|
// Delete at least one row
|
|
if (rows == 0) {
|
|
rows = 1;
|
|
}
|
|
|
|
try {
|
|
var editor = GetCurrentTableEditor();
|
|
editor.beginTransaction();
|
|
|
|
// Loop to delete all blocks of contiguous, selected rows
|
|
while (rows) {
|
|
editor.deleteTableRow(rows);
|
|
rows = GetNumberOfContiguousSelectedRows();
|
|
}
|
|
} finally {
|
|
editor.endTransaction();
|
|
}
|
|
window.content.focus();
|
|
},
|
|
};
|
|
|
|
var nsDeleteTableColumnCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsInTableCell();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
var columns = GetNumberOfContiguousSelectedColumns();
|
|
// Delete at least one column
|
|
if (columns == 0) {
|
|
columns = 1;
|
|
}
|
|
|
|
try {
|
|
var editor = GetCurrentTableEditor();
|
|
editor.beginTransaction();
|
|
|
|
// Loop to delete all blocks of contiguous, selected columns
|
|
while (columns) {
|
|
editor.deleteTableColumn(columns);
|
|
columns = GetNumberOfContiguousSelectedColumns();
|
|
}
|
|
} finally {
|
|
editor.endTransaction();
|
|
}
|
|
window.content.focus();
|
|
},
|
|
};
|
|
|
|
var nsDeleteTableCellCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsInTableCell();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
try {
|
|
GetCurrentTableEditor().deleteTableCell(1);
|
|
} catch (e) {}
|
|
window.content.focus();
|
|
},
|
|
};
|
|
|
|
var nsDeleteTableCellContentsCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsInTableCell();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
try {
|
|
GetCurrentTableEditor().deleteTableCellContents();
|
|
} catch (e) {}
|
|
window.content.focus();
|
|
},
|
|
};
|
|
|
|
var nsNormalizeTableCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsInTable();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
// Use nullptr to let editor find table enclosing current selection
|
|
try {
|
|
GetCurrentTableEditor().normalizeTable(null);
|
|
} catch (e) {}
|
|
window.content.focus();
|
|
},
|
|
};
|
|
|
|
var nsJoinTableCellsCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
if (IsDocumentEditable() && IsEditingRenderedHTML()) {
|
|
try {
|
|
var editor = GetCurrentTableEditor();
|
|
var tagNameObj = { value: "" };
|
|
var countObj = { value: 0 };
|
|
var cell = editor.getSelectedOrParentTableElement(tagNameObj, countObj);
|
|
|
|
// We need a cell and either > 1 selected cell or a cell to the right
|
|
// (this cell may originate in a row spanned from above current row)
|
|
// Note that editor returns "td" for "th" also.
|
|
// (this is a pain! Editor and gecko use lowercase tagNames, JS uses uppercase!)
|
|
if (cell && tagNameObj.value == "td") {
|
|
// Selected cells
|
|
if (countObj.value > 1) {
|
|
return true;
|
|
}
|
|
|
|
var colSpan = cell.getAttribute("colspan");
|
|
|
|
// getAttribute returns string, we need number
|
|
// no attribute means colspan = 1
|
|
if (!colSpan) {
|
|
colSpan = Number(1);
|
|
} else {
|
|
colSpan = Number(colSpan);
|
|
}
|
|
|
|
var rowObj = { value: 0 };
|
|
var colObj = { value: 0 };
|
|
editor.getCellIndexes(cell, rowObj, colObj);
|
|
|
|
// Test if cell exists to the right of current cell
|
|
// (cells with 0 span should never have cells to the right
|
|
// if there is, user can select the 2 cells to join them)
|
|
return (
|
|
colSpan &&
|
|
editor.getCellAt(null, rowObj.value, colObj.value + colSpan)
|
|
);
|
|
}
|
|
} catch (e) {}
|
|
}
|
|
return false;
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
// Param: Don't merge non-contiguous cells
|
|
try {
|
|
GetCurrentTableEditor().joinTableCells(false);
|
|
} catch (e) {}
|
|
window.content.focus();
|
|
},
|
|
};
|
|
|
|
var nsSplitTableCellCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
if (IsDocumentEditable() && IsEditingRenderedHTML()) {
|
|
var tagNameObj = { value: "" };
|
|
var countObj = { value: 0 };
|
|
var cell;
|
|
try {
|
|
cell = GetCurrentTableEditor().getSelectedOrParentTableElement(
|
|
tagNameObj,
|
|
countObj
|
|
);
|
|
} catch (e) {}
|
|
|
|
// We need a cell parent and there's just 1 selected cell
|
|
// or selection is entirely inside 1 cell
|
|
if (
|
|
cell &&
|
|
tagNameObj.value == "td" &&
|
|
countObj.value <= 1 &&
|
|
IsSelectionInOneCell()
|
|
) {
|
|
var colSpan = cell.getAttribute("colspan");
|
|
var rowSpan = cell.getAttribute("rowspan");
|
|
if (!colSpan) {
|
|
colSpan = 1;
|
|
}
|
|
if (!rowSpan) {
|
|
rowSpan = 1;
|
|
}
|
|
return colSpan > 1 || rowSpan > 1 || colSpan == 0 || rowSpan == 0;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
try {
|
|
GetCurrentTableEditor().splitTableCell();
|
|
} catch (e) {}
|
|
window.content.focus();
|
|
},
|
|
};
|
|
|
|
var nsTableOrCellColorCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return IsInTable();
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
EditorSelectColor("TableOrCell");
|
|
},
|
|
};
|
|
|
|
var nsPreferencesCommand = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return true;
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
goPreferences("composer_pane");
|
|
},
|
|
};
|
|
|
|
var nsFinishHTMLSource = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return true;
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
// In editor.js
|
|
SetEditMode(gPreviousNonSourceDisplayMode);
|
|
},
|
|
};
|
|
|
|
var nsCancelHTMLSource = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
return true;
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
// In editor.js
|
|
CancelHTMLSource();
|
|
},
|
|
};
|
|
|
|
var nsConvertToTable = {
|
|
isCommandEnabled(aCommand, dummy) {
|
|
if (IsDocumentEditable() && IsEditingRenderedHTML()) {
|
|
var selection;
|
|
try {
|
|
selection = GetCurrentEditor().selection;
|
|
} catch (e) {}
|
|
|
|
if (selection && !selection.isCollapsed) {
|
|
// Don't allow if table or cell is the selection
|
|
var element;
|
|
try {
|
|
element = GetCurrentEditor().getSelectedElement("");
|
|
} catch (e) {}
|
|
if (element) {
|
|
var name = element.nodeName.toLowerCase();
|
|
if (
|
|
name == "td" ||
|
|
name == "th" ||
|
|
name == "caption" ||
|
|
name == "table"
|
|
) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Selection start and end must be in the same cell
|
|
// in same cell or both are NOT in a cell
|
|
if (
|
|
GetParentTableCell(selection.focusNode) !=
|
|
GetParentTableCell(selection.anchorNode)
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
|
|
getCommandStateParams(aCommand, aParams, aRefCon) {},
|
|
doCommandParams(aCommand, aParams, aRefCon) {},
|
|
|
|
doCommand(aCommand) {
|
|
if (this.isCommandEnabled()) {
|
|
window.openDialog(
|
|
"chrome://editor/content/EdConvertToTable.xhtml",
|
|
"_blank",
|
|
"chrome,close,titlebar,modal"
|
|
);
|
|
}
|
|
},
|
|
};
|