/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998-1999 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * Sammy Ford * Dan Haddix (dan6992@hotmail.com) * John Ratke (jratke@owc.net) * Ryan Cassin (rcassin@supernova.org) */ /* Main Composer window UI control */ var editorShell; var documentModified; var prefAuthorString = ""; var NormalMode = 1; var PreviewMode = 2; // These must match enums in nsIEditorShell.idl: var DisplayModePreview = 0; var DisplayModeNormal = 1; var DisplayModeAllTags = 2; var DisplayModeSource = 3; var PreviousNonSourceDisplayMode = 1; var gEditorDisplayMode = 1; // Normal Editor mode var WebCompose = false; // Set true for Web Composer, leave false for Messenger Composer var docWasModified = false; // Check if clean document, if clean then unload when user "Opens" var gContentWindow = 0; var gSourceContentWindow = 0; var gContentWindowDeck; var gFormatToolbar; var gFormatToolbarHidden = false; var gFormatToolbarCollapsed; var gEditModeBar; // Bummer! Can't get at enums from nsIDocumentEncoder.h var gOutputSelectionOnly = 1; var gOutputFormatted = 2; //var gOutputNoDoctype = 4; // OutputNoDoctype has been obsoleted var gOutputBodyOnly = 8; var gOutputPreformatted = 16; var gOutputWrap = 32; var gOutputFormatFlowed = 64; var gOutputAbsoluteLinks = 128; var gOutputEncodeEntities = 256; var gEditModeLabel; var gNormalModeButton; var gTagModeButton; var gSourceModeButton; var gPreviewModeButton; var gIsWindows; var gIsMac; var gIsUNIX; var gIsHTMLEditor = false; var gColorObj = new Object(); var gPrefs; var gDefaultTextColor = ""; var gDefaultBackgroundColor = ""; // These must be kept in synch with the XUL lists var gFontSizeNames = new Array("xx-small","x-small","small","medium","large","x-large","xx-large"); const nsIFilePicker = Components.interfaces.nsIFilePicker; function EditorOnLoad() { // See if argument was passed. if ( window.arguments && window.arguments[0] ) { // Opened via window.openDialog with URL as argument. // Put argument where EditorStartup expects it. document.getElementById( "args" ).setAttribute( "value", window.arguments[0] ); } WebCompose = true; window.tryToClose = EditorCanClose; // Continue with normal startup. EditorStartup('html', document.getElementById("content-frame")); } function TextEditorOnLoad() { // See if argument was passed. if ( window.arguments && window.arguments[0] ) { // Opened via window.openDialog with URL as argument. // Put argument where EditorStartup expects it. document.getElementById( "args" ).setAttribute( "value", window.arguments[0] ); } // Continue with normal startup. EditorStartup('text', document.getElementById("content-frame")); } function PageIsEmptyAndUntouched() { return (editorShell != null) && (editorShell.documentIsEmpty == true) && (docWasModified == false); } // This is called when the real editor document is created, // before it's loaded. var DocumentStateListener = { NotifyDocumentCreated: function() { // Call EditorSetDefaultPrefs first so it gets the default author before initing toolbars EditorSetDefaultPrefs(); EditorInitToolbars(); BuildRecentMenu(true); // Build the recent files menu and save to prefs window._content.focus(); // udpate menu items now that we have an editor to play with // Note: This must be AFTER window._content.focus(); window.updateCommands("create"); }, NotifyDocumentWillBeDestroyed: function() {}, NotifyDocumentStateChanged:function( isNowDirty ) { /* Notify our dirty detector so this window won't be closed if another document is opened */ if (isNowDirty) docWasModified = true; // hack! Should not need this updateCommands, but there is some controller // bug that this works around. ?? // comment out the following line because it cause 41573 IME problem on Mac //window._content.focus(); window.updateCommands("create"); window.updateCommands("save"); } }; function EditorStartup(editorType, editorElement) { gIsHTMLEditor = (editorType == "html"); if (gIsHTMLEditor) { gSourceContentWindow = document.getElementById("content-source"); gEditModeBar = document.getElementById("EditModeToolbar"); gEditModeLabel = document.getElementById("EditModeLabel"); gNormalModeButton = document.getElementById("NormalModeButton"); gTagModeButton = document.getElementById("TagModeButton"); gSourceModeButton = document.getElementById("SourceModeButton"); gPreviewModeButton = document.getElementById("PreviewModeButton"); // mark first tab as selected document.getElementById("EditModeTabbox").selectedTab = gNormalModeButton; // XUL elements we use when switching from normal editor to edit source gContentWindowDeck = document.getElementById("ContentWindowDeck"); gFormatToolbar = document.getElementById("FormatToolbar"); } // store the editor shell in the window, so that child windows can get to it. editorShell = editorElement.editorShell; // this pattern exposes a JS/XBL bug that causes leaks editorShell.Init(); editorShell.editorType = editorType; editorShell.webShellWindow = window; editorShell.contentWindow = window._content; // add a listener to be called when document is really done loading editorShell.RegisterDocumentStateListener( DocumentStateListener ); // set up our global prefs object GetPrefsService(); // Startup also used by other editor users, such as Message Composer EditorSharedStartup(); // Commands specific to the Composer Application window, // (i.e., not embeded editors) // such as file-related commands, HTML Source editing, Edit Modes... SetupComposerWindowCommands(); // Get url for editor content and load it. // the editor gets instantiated by the editor shell when the URL has finished loading. var url = document.getElementById("args").getAttribute("value"); editorShell.LoadUrl(url); } // This is the only method also called by Message Composer function EditorSharedStartup() { // set up JS-implemented commands for HTML editing SetupHTMLEditorCommands(); // Just for convenience gContentWindow = window._content; gIsWindows = navigator.appVersion.indexOf("Win") != -1; gIsUNIX = (navigator.appVersion.indexOf("X11") || navigator.appVersion.indexOf("nux")) != -1; gIsMac = !gIsWindows && !gIsUNIX; //dump("IsWin="+gIsWindows+", IsUNIX="+gIsUNIX+", IsMac="+gIsMac+"\n"); // Set platform-specific hints for how to select cells // Mac uses "Cmd", all others use "Ctrl" var tableKey = GetString(gIsMac ? "XulKeyMac" : "TableSelectKey"); var dragStr = tableKey+GetString("Drag"); var clickStr = tableKey+GetString("Click"); var delStr = GetString(gIsMac ? "Clear" : "Del"); SafeSetAttribute("menu_SelectCell", "acceltext", clickStr); SafeSetAttribute("menu_SelectRow", "acceltext", dragStr); SafeSetAttribute("menu_SelectColumn", "acceltext", dragStr); SafeSetAttribute("menu_SelectAllCells", "acceltext", dragStr); // And add "Del" or "Clear" SafeSetAttribute("menu_DeleteCellContents", "acceltext", delStr); // Remove a Privacy menu that causes problems // (method is in tasksOverlay.js) HideImage(); // hide UI that we don't have components for RemoveInapplicableUIElements(); // Use global prefs if EditorStartup already run, // else get service for other editor users if (!gPrefs) GetPrefsService(); // Use browser colors as initial values for editor's default colors var BrowserColors = GetDefaultBrowserColors(); if (BrowserColors) { gDefaultTextColor = BrowserColors.TextColor; gDefaultBackgroundColor = BrowserColors.BackgroundColor; } //dump(" *** EditorShared startup: gDefaultTextColor="+gDefaultTextColor+", gDefaultBackgroundColor="+gDefaultBackgroundColor+"\n"); } // Get these to use for initial default text and background, // and also pass to prefs and color dialogs function GetDefaultBrowserColors() { var colors = new Object(); var useWinColors = false; if (gIsWindows) { // In Windows only, there's a pref to use system colors instead of pref colors try { useWinColors = gPrefs.GetBoolPref("browser.display.wfe.use_windows_colors"); } catch (e) {} } if (!useWinColors) { try { colors.TextColor = gPrefs.CopyCharPref("browser.display.foreground_color"); } catch (e) {} try { colors.BackgroundColor = gPrefs.CopyCharPref("browser.display.background_color"); } catch (e) {} } // Use OS colors for text and background if explicitly asked or pref is not set if (!colors.TextColor) colors.TextColor = "windowtext"; if (!colors.BackgroundColor) colors.BackgroundColor = "window"; colors.LinkColor = gPrefs.CopyCharPref("browser.anchor_color"); colors.VisitedLinkColor = gPrefs.CopyCharPref("browser.visited_color"); return colors; } function _EditorNotImplemented() { dump("Function not implemented\n"); } function EditorShutdown() { dump("In EditorShutdown..\n"); return editorShell.Shutdown(); } function SafeSetAttribute(nodeID, attributeName, attributeValue) { var theNode = document.getElementById(nodeID); if (theNode) theNode.setAttribute(attributeName, attributeValue); } // We use this alot! function GetString(id) { return editorShell.GetString(id); } function FindAndSelectEditorWindowWithURL(urlToMatch) { if (!urlToMatch || urlToMatch.length == 0) return false; // returns true if found; false if an error or not found var windowManager = Components.classes["@mozilla.org/rdf/datasource;1?name=window-mediator"].getService(); if ( !windowManager ) return false; var windowManagerInterface = windowManager.QueryInterface(Components.interfaces.nsIWindowMediator); if ( !windowManagerInterface ) return false; var enumerator = windowManagerInterface.getEnumerator( "composer:html" ); if ( !enumerator ) return false; while ( enumerator.hasMoreElements() ) { var window = windowManagerInterface.convertISupportsToDOMWindow( enumerator.getNext() ); if ( window ) { var didFindWindow = editorShell.checkOpenWindowForURLMatch(urlToMatch, window) if (didFindWindow) { window.focus(); return true; } } } // not found return false; } function editorSendPage() { var docModified = editorShell.documentModified; var pageUrl = window.editorShell.editorDocument.location; if (pageUrl != "about:blank" && !docModified) { var pageTitle = window.editorShell.editorDocument.title; window.openDialog("chrome://messenger/content/messengercompose/messengercompose.xul", "_blank", "chrome,all,dialog=no", "attachment='" + pageUrl + "',body='" + pageUrl + "',subject='" + pageTitle + "',bodyislink=true"); } else if (CheckAndSaveDocument(GetString("SendPageReason")), DocumentHasBeenSaved()) editorSendPage(); window._content.focus(); } function DocumentHasBeenSaved() { var fileurl = ""; try { fileurl = window._content.location; } catch (e) { return false; } if (fileurl == "" || fileurl == "about:blank") return false; // We have a file URL already return true; } function CheckAndSaveDocument(reasonToSave, allowDontSave) { var document = editorShell.editorDocument; if (!editorShell.documentModified) return true; var title = window.editorShell.editorDocument.title; if (!title) title = GetString("untitled"); var dialogTitle = window.editorShell.GetString("SaveDocument"); var dialogMsg = window.editorShell.GetString("SaveFilePrompt"); dialogMsg = (dialogMsg.replace(/%title%/,title)).replace(/%reason%/,reasonToSave); var result = {value:0}; commonDialogsService.UniversalDialog( window, null, dialogTitle, dialogMsg, null, window.editorShell.GetString("Save"), // Save Button window.editorShell.GetString("Cancel"), // Cancel Button (allowDontSave ? window.editorShell.GetString("DontSave") : null), // Don't Save Button null, null, null, {value:0}, {value:0}, "chrome://global/skin/question-icon.gif", {value:"false"}, (allowDontSave ? 3 : 2), 0, 0, result ); if (result.value == 0) { // Save var success = window.editorShell.saveDocument(false, false); return success; } if (result.value == 2) // "Don't Save" return true; // Default or result.value == 1 (Cancel) return false; } // --------------------------- File menu --------------------------- // used by openLocation. see openLocation.js for additional notes. function delayedOpenWindow(chrome, flags, url) { if (PageIsEmptyAndUntouched()) editorShell.LoadUrl(url); else setTimeout("window.openDialog('"+chrome+"','_blank','"+flags+"','"+url+"')", 10); } function EditorNewPlaintext() { window.openDialog( "chrome://editor/content/TextEditorAppShell.xul", "_blank", "chrome,dialog=no,all", "about:blank"); } // Check for changes to document and allow saving before closing // This is hooked up to the OS's window close widget (e.g., "X" for Windows) function EditorCanClose() { // Returns FALSE only if user cancels save action // "true" means allow "Don't Save" button var canClose = CheckAndSaveDocument(GetString("BeforeClosing"), true); // This is our only hook into closing via the "X" in the caption // or "Quit" (or other paths?) // so we must shift association to another // editor or close any non-modal windows now if (canClose && window.InsertCharWindow) SwitchInsertCharToAnotherEditorOrClose(); return canClose; } // --------------------------- View menu --------------------------- function EditorSetDocumentCharacterSet(aCharset) { if(editorShell) { editorShell.SetDocumentCharacterSet(aCharset); if( editorShell.editorDocument.location != "about:blank") { editorShell.LoadUrl(editorShell.editorDocument.location); } } } // ------------------------------------------------------------------ function updateCharsetPopupMenu(menuPopup) { if(editorShell.documentModified) { for (var i = 0; i < menuPopup.childNodes.length; i++) { var menuItem = menuPopup.childNodes[i]; menuItem.setAttribute('disabled', 'true'); } } } // --------------------------- Text style --------------------------- function EditorSetTextProperty(property, attribute, value) { editorShell.SetTextProperty(property, attribute, value); gContentWindow.focus(); } function onParagraphFormatChange(paraMenuList, commandID) { var commandNode = document.getElementById(commandID); var state = commandNode.getAttribute("state"); var menuList = document.getElementById("ParagraphSelect"); if (!menuList) return; // force match with "normal" if (state == "body") state = ""; if (state == "mixed") { //Selection is the "mixed" ( > 1 style) state paraMenuList.selectedItem = null; paraMenuList.setAttribute("value",GetString('Mixed')); } else { var menuPopup = document.getElementById("ParagraphPopup"); var menuItems = menuPopup.childNodes; for (var i=0; i < menuItems.length; i++) { var menuItem = menuItems.item(i); if (menuItem.data == state) { paraMenuList.selectedItem = menuItem; break; } } } } function doStatefulCommand(commandID, newState) { var commandNode = document.getElementById(commandID); if (commandNode) commandNode.setAttribute("state", newState); window._content.focus(); // needed for command dispatch to work goDoCommand(commandID); } function onFontFaceChange(fontFaceMenuList, commandID) { var commandNode = document.getElementById(commandID); var state = commandNode.getAttribute("state"); if (state == "mixed") { //Selection is the "mixed" ( > 1 style) state fontFaceMenuList.selectedItem = null; fontFaceMenuList.setAttribute("value",GetString('Mixed')); } else { var menuPopup = document.getElementById("FontFacePopup"); var menuItems = menuPopup.childNodes; for (var i=0; i < menuItems.length; i++) { var menuItem = menuItems.item(i); if (menuItem.getAttribute("value") && (menuItem.data.toLowerCase() == state.toLowerCase())) { fontFaceMenuList.selectedItem = menuItem; break; } } } } function EditorSelectFontSize() { var select = document.getElementById("FontSizeSelect"); if (select) { if (select.selectedIndex == -1) return; EditorSetFontSize(gFontSizeNames[select.selectedIndex]); } } function onFontSizeChange(fontSizeMenulist, commandID) { // If we don't match anything, set to "0 (normal)" var newIndex = 2; var size = fontSizeMenulist.getAttribute("size"); if ( size == "mixed") { // No single type selected newIndex = -1; } else { for (var i = 0; i < gFontSizeNames.length; i++) { if( gFontSizeNames[i] == size ) { newIndex = i; break; } } } if (fontSizeMenulist.selectedIndex != newIndex) fontSizeMenulist.selectedIndex = newIndex; } function EditorSetFontSize(size) { if( size == "0" || size == "normal" || size == "medium" ) { editorShell.RemoveTextProperty("font", "size"); } else { // Temp: convert from new CSS size strings to old HTML size strings switch (size) { case "xx-small": case "x-small": size = "-2"; break; case "small": size = "-1"; break; case "large": size = "+1"; break; case "x-large": size = "+2"; break; case "xx-large": size = "+3"; break; } editorShell.SetTextProperty("font", "size", size); } gContentWindow.focus(); } function onFontColorChange() { var commandNode = document.getElementById("cmd_fontColor"); if (commandNode) { var color = commandNode.getAttribute("state"); var button = document.getElementById("TextColorButton"); if (button) { // No color set - get color set on page or other defaults if (!color) color = gDefaultTextColor; button.setAttribute("style", "background-color:"+color); } } } function onBackgroundColorChange() { var commandNode = document.getElementById("cmd_backgroundColor"); if (commandNode) { var color = commandNode.getAttribute("state"); var button = document.getElementById("BackgroundColorButton"); if (button) { if (!color) color = gDefaultBackgroundColor; button.setAttribute("style", "background-color:"+color); } } } function GetBackgroundElementWithColor() { gColorObj.Type = ""; gColorObj.PageColor = ""; gColorObj.TableColor = ""; gColorObj.CellColor = ""; gColorObj.BackgroundColor = ""; var tagNameObj = new Object; var countObj = new Object; var element = window.editorShell.GetSelectedOrParentTableElement(tagNameObj, countObj); if (element && tagNameObj && tagNameObj.value) { gColorObj.BackgroundColor = element.getAttribute("bgcolor"); if (tagNameObj.value.toLowerCase() == "td") { gColorObj.Type = "Cell"; gColorObj.CellColor = gColorObj.BackgroundColor; // Get any color that might be on parent table var table = GetParentTable(element); gColorObj.TableColor = table.getAttribute("bgcolor"); } else { gColorObj.Type = "Table"; gColorObj.TableColor = gColorObj.BackgroundColor; } } else { element = GetBodyElement(); if (element) { gColorObj.Type = "Page"; gColorObj.BackgroundColor = element.getAttribute("bgcolor"); gColorObj.PageColor = gColorObj.BackgroundColor; } } return element; } function EditorSelectColor(colorType) { if (!gColorObj) gColorObj = new Object; var element; var table; var currentColor = ""; var commandNode; if (!colorType) colorType = ""; if (colorType == "Text") { gColorObj.Type = colorType; // Get color from command node state commandNode = document.getElementById("cmd_fontColor"); currentColor = commandNode.getAttribute("state"); gColorObj.TextColor = currentColor; } else { element = GetBackgroundElementWithColor(); if (!element) return; // Get the table if we found a cell if (gColorObj.Type == "Table") table = element; else if (gColorObj.Type == "Cell") table = GetParentTable(element); // Save to avoid resetting if not necessary currentColor = gColorObj.BackgroundColor; if (colorType == "TableOrCell" || colorType == "Cell") { if (gColorObj.Type == "Cell") gColorObj.Type = colorType; else if (gColorObj.Type != "Table") return; } else if (colorType == "Table" && gColorObj.Type == "Page") return; if (colorType == "" && gColorObj.Type == "Cell") { // Using empty string for requested type means // we can let user select cell or table gColorObj.Type = "TableOrCell"; } } // Save the type we are really requesting colorType = gColorObj.Type; // Launch the ColorPicker dialog // TODO: Figure out how to position this under the color buttons on the toolbar window.openDialog("chrome://editor/content/EdColorPicker.xul", "_blank", "chrome,close,titlebar,modal", "", gColorObj); // User canceled the dialog if (gColorObj.Cancel) return; if (colorType == "Text") { if (currentColor != gColorObj.TextColor) window.editorShell.SetTextProperty("font", "color", gColorObj.TextColor); // Update the command state (this will trigger color button update) goUpdateCommand("cmd_fontColor"); } else if (element) { if (gColorObj.Type == "Table") { // Set background on a table // Note that we shouldn't trust "currentColor" because of "TableOrCell" behavior if (table) { var bgcolor = table.getAttribute("bgcolor"); if (bgcolor != gColorObj.BackgroundColor) { if (gColorObj.BackgroundColor) window.editorShell.SetAttribute(table, "bgcolor", gColorObj.BackgroundColor); else window.editorShell.RemoveAttribute(table, "bgcolor"); } } } else if (currentColor != gColorObj.BackgroundColor) window.editorShell.SetBackgroundColor(gColorObj.BackgroundColor); goUpdateCommand("cmd_backgroundColor"); } window._content.focus(); } function GetParentTable(element) { var node = element; while (node) { if (node.tagName.toLowerCase() == "table") return node; node = node.parentNode; } return node; } /*TODO: We need an oncreate hook to do enabling/disabling for the Format menu. There should be code like this for the object-specific "Properties" item */ // For property dialogs, we want the selected element, // but will accept a parent link, list, or table cell if inside one function GetObjectForProperties() { var element = editorShell.GetSelectedElement(""); if (element) return element; // Find nearest parent of selection anchor node // that is a link, list, table cell, or table var anchorNode = editorShell.editorSelection.anchorNode; if (!anchorNode) return null; var node; if (anchorNode.firstChild) { // Start at actual selected node var offset = editorShell.editorSelection.anchorOffset; // Note: If collapsed, offset points to element AFTER caret, // thus node may be null node = anchorNode.childNodes.item(offset); } if (!node) node = anchorNode; while (node) { if (node.tagName) { var tagName = node.tagName.toLowerCase(); // Done when we hit the body if (tagName == "body") break; if ((tagName == "a" && node.href) || tagName == "ol" || tagName == "ul" || tagName == "dl" || tagName == "td" || tagName == "th" || tagName == "table") { return node; } } node = node.parentNode; } return null; } function SetEditMode(mode) { if (gIsHTMLEditor) { var bodyNode = editorShell.editorDocument.getElementsByTagName("body").item(0); if (!bodyNode) { dump("SetEditMode: We don't have a body node!\n"); return; } // Switch the UI mode before inserting contents // so user can't type in source window while new window is being filled var previousMode = gEditorDisplayMode; if (!SetDisplayMode(mode)) return; var source; if (mode == DisplayModeSource) { // Get the entire document's source string var flags = gOutputEncodeEntities; // var prettyPrint = gPrefs.GetBoolPref("editor.prettyprint"); if (prettyPrint) flags |= gOutputFormatted; source = editorShell.GetContentsAs("text/html", flags); var start = source.search(/ here to make sure new value is updated // everwhere (also covers bug in RebuildDocumentFromSource that strips it out!) var titleStart = source.search(//i); if (titleStart != -1) { // Skip over tag name titleStart += 7; var titleEnd = source.indexOf("<",titleStart); if (titleEnd > titleStart) { var title = source.slice(titleStart, titleEnd); editorShell.SetDocumentTitle(title); } } // Clear out the string buffers source = null; gSourceContentWindow.value = null; // reset selection to top of doc (wish we could preserve it!) if (bodyNode) editorShell.editorSelection.collapse(bodyNode, 0); window._content.focus(); } } } function CancelHTMLSource() { // Don't convert source text back into the DOM document gSourceContentWindow.value = ""; SetDisplayMode(PreviousNonSourceDisplayMode); } function FinishHTMLSource() { // Switch edit modes -- converts source back into DOM document SetEditMode(PreviousNonSourceDisplayMode); } function CollapseItem(id, collapse) { var item = document.getElementById(id); if (item) { if(collapse != (item.getAttribute("collapsed") == "true")) item.setAttribute("collapsed", collapse ? "true" : ""); } } function DisableItem(id, disable) { var item = document.getElementById(id); if (item) { if(disable != (item.getAttribute("disabled") == "true")) item.setAttribute("disabled", disable ? "true" : ""); } } function SetDisplayMode(mode) { if (gIsHTMLEditor) { // Already in requested mode: // return false to indicate we didn't switch if (mode == gEditorDisplayMode) return false; gEditorDisplayMode = mode; // Save the last non-source mode so we can cancel source editing easily if (mode != DisplayModeSource) PreviousNonSourceDisplayMode = mode; // Editorshell does the style sheet loading/unloading editorShell.SetDisplayMode(mode); // Set the UI states var selectedTab = null; if (mode == DisplayModePreview) selectedTab = gPreviewModeButton; if (mode == DisplayModeNormal) selectedTab = gNormalModeButton; if (mode == DisplayModeAllTags) selectedTab = gTagModeButton; if (mode == DisplayModeSource) selectedTab = gSourceModeButton; if (selectedTab) { document.getElementById("EditModeTabbox").selectedTab = selectedTab; } if (mode == DisplayModeSource) { // Switch to the sourceWindow (second in the deck) gContentWindowDeck.setAttribute("index","1"); DisableMenusForHTMLSource(true); //Hide the formating toolbar if not already hidden gFormatToolbarHidden = gFormatToolbar.getAttribute("hidden"); if (gFormatToolbarHidden != "true") { gFormatToolbar.setAttribute("hidden", "true"); } gSourceContentWindow.focus(); } else { // Switch to the normal editor (first in the deck) gContentWindowDeck.setAttribute("index","0"); // Restore menus and toolbars DisableMenusForHTMLSource(false); if (gFormatToolbarHidden != "true") { gFormatToolbar.setAttribute("hidden", gFormatToolbarHidden); } window._content.focus(); } // We must set check on menu item since toolbar may have been used document.getElementById("viewPreviewMode").setAttribute("checked","false"); document.getElementById("viewNormalMode").setAttribute("checked","false"); document.getElementById("viewAllTagsMode").setAttribute("checked","false"); document.getElementById("viewSourceMode").setAttribute("checked","true"); var menuID; switch(mode) { case DisplayModePreview: menuID = "viewPreviewMode"; break; case DisplayModeNormal: menuID = "viewNormalMode"; break; case DisplayModeAllTags: menuID = "viewAllTagsMode"; break; case DisplayModeSource: menuID = "viewSourceMode"; break; } if (menuID) document.getElementById(menuID).setAttribute("checked","true"); return true; } return false; } // We disable all items in View menu except Edit mode and sidebar items function DisableMenusForHTMLSource(disable) { // Disable toolbar buttons DisableItem("spellingButton", disable); DisableItem("imageButton", disable); DisableItem("hlineButton", disable); DisableItem("tableButton", disable); DisableItem("linkButton", disable); DisableItem("namedAnchorButton", disable); // Any toolbar can be toggled on/off except the format toolbar DisableItem("viewFormatToolbar", disable); // Top-level menus that we completely hide CollapseItem("insertMenu", disable); CollapseItem("formatMenu", disable); CollapseItem("tableMenu", disable); // Edit menu items DisableItem("menu_find", disable); DisableItem("menu_findnext", disable); DisableItem("menu_checkspelling", disable); // Disable all items in the view menu except mode switch items var viewMenu = document.getElementById("viewMenu"); // menuitems are children of the menupopup child var children = viewMenu.firstChild.childNodes; for (var i = 0; i < children.length; i++) { var item = children.item(i); if (item.id != "viewToolbar" && item.id != "viewNormalMode" && item.id != "viewAllTagsMode" && item.id != "viewSourceMode" && item.id != "viewPreviewMode" && item.id != "sidebar-menu") { DisableItem(item.id, disable); } } } function EditorToggleParagraphMarks() { var menuItem = document.getElementById("viewParagraphMarks"); if (menuItem) { // Note that the 'type="checbox"' mechanism automatically // toggles the "checked" state before the oncommand is called, // so if "checked" is true now, it was just switched to that mode var checked = menuItem.getAttribute("checked"); try { editorShell.DisplayParagraphMarks(checked == "true"); } catch(e) { return; } } } function EditorInitEditMenu() { /* // Fake the keybinding hint for the "Clear" item // (Don't bother for Mac?) if (!gIsMac) document.getElementById("menu_clear").setAttribute("acceltext", GetString("Del")); */ //TODO: We should modify the Paste menuitem to build a submenu // with multiple paste format types } function EditorOpenUrl(url) { if (!url) return; // if the existing window is untouched, just load there if (!FindAndSelectEditorWindowWithURL(url)) { if (PageIsEmptyAndUntouched()) { window.editorShell.LoadUrl(url); } else { // open new window window.openDialog("chrome://editor/content", "_blank", "chrome,dialog=no,all", url); } } } function BuildRecentMenu(savePrefs) { // Can't do anything if no prefs if (!gPrefs) return; var popup = document.getElementById("menupopup_RecentFiles"); if (!popup || !window.editorShell || !window.editorShell.editorDocument) return; // Delete existing menu while (popup.firstChild) popup.removeChild(popup.firstChild); // Current page is the "0" item in the list we save in prefs, // but we don't include it in the menu. var curTitle = window.editorShell.editorDocument.title; var curUrl = window.editorShell.editorDocument.location; var newDoc = (curUrl == "about:blank"); var historyCount = 10; try { historyCount = gPrefs.CopyUnicharPref("editor.history.url_maximum"); } catch(e) {} var titleArray = new Array(historyCount); var urlArray = new Array(historyCount); var menuIndex = 1; var arrayIndex = 0; var i; if(!newDoc) { // Always put latest-opened URL at start of array titleArray[0] = curTitle; urlArray[0] = curUrl; arrayIndex = 1; } for (i = 0; i < historyCount; i++) { var title = getUnicharPref("editor.history_title_"+i); var url = getUnicharPref("editor.history_url_"+i); if (!url) break; // Skip over current URL if (url != curUrl) { // Build the menu AppendRecentMenuitem(popup, title, url, menuIndex); menuIndex++; // Save in array for prefs if (savePrefs && arrayIndex < historyCount) { titleArray[arrayIndex] = title; urlArray[arrayIndex] = url; arrayIndex++; } } } // Now resave the list back to prefs in the new order if (savePrefs) { savePrefs = false; for (i = 0; i < historyCount; i++) { if (!urlArray[i]) break; setUnicharPref("editor.history_title_"+i, titleArray[i]); setUnicharPref("editor.history_url_"+i, urlArray[i]); savePrefs = true; } } // Force saving to file so next file opened finds these values if (savePrefs) gPrefs.SavePrefFile(); } function AppendRecentMenuitem(menupopup, title, url, menuIndex) { if (menupopup) { var menuItem = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "menuitem"); if (menuItem) { var accessKey; if (menuIndex <= 9) accessKey = String(menuIndex); else if (menuIndex == 10) accessKey = "0"; else accessKey = " "; var itemString = accessKey+" "; if (title) itemString += title; else itemString += url; menuItem.setAttribute("value", itemString); menuItem.setAttribute("data", url); if (accessKey != " ") menuItem.setAttribute("accesskey", accessKey); menuItem.setAttribute("oncommand", "EditorOpenUrl(getAttribute('data'))"); menupopup.appendChild(menuItem); } } } function setUnicharPref(aPrefName, aPrefValue) { if (!gPrefs) return; try { gPrefs.SetUnicharPref(aPrefName, aPrefValue); } catch(e) { } } function getUnicharPref(aPrefName, aDefVal) { if (!gPrefs) return ""; try { return gPrefs.CopyUnicharPref(aPrefName); } catch(e) { } return ""; } function EditorInitFormatMenu() { InitObjectPropertiesMenuitem("objectProperties"); InitRemoveStylesMenuitems("removeStylesMenuitem", "removeLinksMenuitem"); // Set alignment check } function InitObjectPropertiesMenuitem(id) { // Set strings and enable for the [Object] Properties item // Note that we directly do the enabling instead of // using goSetCommandEnabled since we already have the menuitem var menuItem = document.getElementById(id); if (!menuItem) return null; var element = GetObjectForProperties(); var menuStr = GetString("ObjectProperties"); var name; if (element && element.nodeName) { var objStr = ""; menuItem.setAttribute("disabled", ""); name = element.nodeName.toLowerCase(); switch (name) { case "img": objStr = GetString("Image"); break; case "hr": objStr = GetString("HLine"); break; case "table": objStr = GetString("Table"); break; case "th": name = "td"; case "td": objStr = GetString("TableCell"); break; case "ol": case "ul": case "dl": objStr = GetString("List"); break; case "a": if (element.name) { objStr = GetString("NamedAnchor"); name = "anchor"; } else if(element.href) { objStr = GetString("Link"); name = "href"; } break; } menuStr = menuStr.replace(/%obj%/,objStr); } else { // We show generic "Properties" string, but disable menu item menuItem.setAttribute("disabled","true"); // Replace placeholder with "", then remaining space on left side menuStr = menuStr.replace(/%obj%/,"").replace(/^\s+/, ""); } menuItem.setAttribute("value", menuStr); menuItem.setAttribute("accesskey",GetString("ObjectPropertiesAccessKey")); return name; } function InitParagraphMenu() { var mixedObj = new Object(); var state = editorShell.GetParagraphState(mixedObj); var IDSuffix; // PROBLEM: When we get blockquote, it masks other styles contained by it // We need a separate method to get blockquote state // We use "x" as uninitialized paragraph state if (!state || state == "x") IDSuffix = "bodyText" // No paragraph container else IDSuffix = state; // Set "radio" check on one item, but... var menuItem = document.getElementById("menu_"+IDSuffix); menuItem.setAttribute("checked", "true"); // ..."bodyText" is returned if mixed selection, so remove checkmark if (mixedObj.value) menuItem.setAttribute("checked", "false"); } function InitListMenu() { var mixedObj = new Object(); var state = editorShell.GetListState(mixedObj); var IDSuffix = "noList"; if (state) { if (state == "dl") state = editorShell.GetListItemState(mixedObj); if (state) IDSuffix = state; } // Set "radio" check on one item, but... var menuItem = document.getElementById("menu_"+IDSuffix); menuItem.setAttribute("checked", "true"); // ..."noList" is returned if mixed selection, so remove checkmark if (mixedObj.value) menuItem.setAttribute("checked", "false"); } function InitAlignMenu() { var mixedObj = new Object(); // Note: GetAlignment DOESN'T set the "mixed" flag, // but always returns "left" in mixed state // We'll pay attention to it here so it works when fixed var state = editorShell.GetAlignment(mixedObj); var IDSuffix; if (!state) IDSuffix = "left" else IDSuffix = state; var menuItem = document.getElementById("menu_"+IDSuffix); menuItem.setAttribute("checked", "true"); if (mixedObj.value) menuItem.setAttribute("checked", "false"); } function EditorInitToolbars() { // Nothing to do now, but we might want some state updating here } function EditorSetDefaultPrefs() { /* only set defaults for new documents */ var url = document.getElementById("args").getAttribute("value"); if ( url != "about:blank" ) return; var element; var domdoc; try { domdoc = window.editorShell.editorDocument; } catch (e) { dump( e + "\n"); } if ( !domdoc ) { dump("EditorSetDefaultPrefs: EDITOR DOCUMENT NOT FOUND\n"); return; } // doctype var newdoctype = domdoc.implementation.createDocumentType("html", "-//W3C//DTD HTML 4.01 Transitional//EN",""); if (!domdoc.doctype) { domdoc.insertBefore(newdoctype, domdoc.firstChild); } else { domdoc.replaceChild(newdoctype, domdoc.doctype); } // search for head; we'll need this for meta tag additions var headelement = 0; var headnodelist = domdoc.getElementsByTagName("head"); if (headnodelist) { var sz = headnodelist.length; if ( sz >= 1 ) { headelement = headnodelist.item(0); } } // search for author meta tag. // if one is found, don't do anything. // if not, create one and make it a child of the head tag // and set its content attribute to the value of the editor.author preference. var nodelist = domdoc.getElementsByTagName("meta"); if ( nodelist ) { // we should do charset first since we need to have charset before // hitting other 8-bit char in other meta tags // grab charset pref and make it the default charset var prefCharsetString = 0; try { prefCharsetString = gPrefs.getLocalizedUnicharPref("intl.charset.default"); } catch (ex) {} if ( prefCharsetString && prefCharsetString != 0) { element = domdoc.createElement("meta"); if ( element ) { AddAttrToElem(domdoc, "http-equiv", "content-type", element); AddAttrToElem(domdoc, "content", "text/html; charset=" + prefCharsetString, element); headelement.appendChild( element ); } } var node = 0; var listlength = nodelist.length; // let's start by assuming we have an author in case we don't have the pref var authorFound = false; for (var i = 0; i < listlength && !authorFound; i++) { node = nodelist.item(i); if ( node ) { var value = node.getAttribute("name").toLowerCase(); if (value == "author") { authorFound = true; } } } var prefAuthorString = 0; try { prefAuthorString = gPrefs.CopyUnicharPref("editor.author"); } catch (ex) {} if ( prefAuthorString && prefAuthorString != 0) { if ( !authorFound && headelement) { /* create meta tag with 2 attributes */ element = domdoc.createElement("meta"); if ( element ) { AddAttrToElem(domdoc, "name", "author", element); AddAttrToElem(domdoc, "content", prefAuthorString, element); headelement.appendChild( element ); } } } } // Get editor color prefs var use_custom_colors = false; try { use_custom_colors = gPrefs.GetBoolPref("editor.use_custom_colors"); } catch (ex) {} // XXX BUG: this is always comming up TRUE even when pref panel is used to set false dump(" *** editor.use_custom_colors="+use_custom_colors+"\n"); if ( use_custom_colors ) { // find body node var bodyelement = GetBodyElement(); // try to get the default color values. ignore them if we don't have them. var text_color; var link_color; var active_link_color; var followed_link_color; var background_color; try { text_color = gPrefs.CopyCharPref("editor.text_color"); } catch (e) {} try { link_color = gPrefs.CopyCharPref("editor.link_color"); } catch (e) {} try { active_link_color = gPrefs.CopyCharPref("editor.active_link_color"); } catch (e) {} try { followed_link_color = gPrefs.CopyCharPref("editor.followed_link_color"); } catch (e) {} try { background_color = gPrefs.CopyCharPref("editor.background_color"); } catch(e) {} // add the color attributes to the body tag. // and use them for the default text and background colors if not empty if (text_color) { AddAttrToElem(domdoc, "text", text_color, bodyelement); gDefaultTextColor = text_color; } if (background_color) { AddAttrToElem(domdoc, "bgcolor", background_color, bodyelement); gDefaultBackgroundColor = background_color } if (link_color) AddAttrToElem(domdoc, "link", link_color, bodyelement); if (active_link_color) AddAttrToElem(domdoc, "alink", active_link_color, bodyelement); if (followed_link_color) AddAttrToElem(domdoc, "vlink", followed_link_color, bodyelement); } // auto-save??? } function GetBodyElement() { try { var bodyNodelist = window.editorShell.editorDocument.getElementsByTagName("body"); if (bodyNodelist) return bodyNodelist.item(0); } catch (ex) { dump("no body tag found?!\n"); // better have one, how can we blow things up here? } return null; } function AddAttrToElem(dom, attr_name, attr_value, elem) { var a = dom.createAttribute(attr_name); if ( a ) { a.value = attr_value; elem.setAttributeNode(a); } } function UpdateSaveButton(modified) { var saveButton = document.getElementById("saveButton"); if (saveButton) { if (modified) { saveButton.setAttribute("src", "chrome://editor/skin/images/ED_SaveMod.gif"); } else { saveButton.setAttribute("src", "chrome://editor/skin/images/ED_SaveFile.gif"); } } } function EditorReflectDocState() { var docState = window.editorShell.documentStatus; var stateString; if (docState == 0) { stateString = "unmodified"; } else { stateString = "modified"; } var oldModified = documentModified; documentModified = (window.editorShell.documentStatus != 0); if (oldModified != documentModified) UpdateSaveButton(documentModified); return true; } // --------------------------- Logging stuff --------------------------- function EditorGetNodeFromOffsets(offsets) { var node = null; node = editorShell.editorDocument; for (var i = 0; i < offsets.length; i++) { node = node.childNodes[offsets[i]]; } return node; } function EditorSetSelectionFromOffsets(selRanges) { var rangeArr, start, end, node, offset; var selection = editorShell.editorSelection; selection.clearSelection(); for (var i = 0; i < selRanges.length; i++) { rangeArr = selRanges[i]; start = rangeArr[0]; end = rangeArr[1]; var range = editorShell.editorDocument.createRange(); node = EditorGetNodeFromOffsets(start[0]); offset = start[1]; range.setStart(node, offset); node = EditorGetNodeFromOffsets(end[0]); offset = end[1]; range.setEnd(node, offset); selection.addRange(range); } } //-------------------------------------------------------------------- function initFontStyleMenu(menuPopup) { for (var i = 0; i < menuPopup.childNodes.length; i++) { var menuItem = menuPopup.childNodes[i]; var theStyle = menuItem.getAttribute("state"); if (theStyle) { menuItem.setAttribute("checked", theStyle); } } } //-------------------------------------------------------------------- function onButtonUpdate(button, commmandID) { var commandNode = document.getElementById(commmandID); var state = commandNode.getAttribute("state"); button.setAttribute("toggled", state); } //-------------------------------------------------------------------- function onStateButtonUpdate(button, commmandID, onState) { var commandNode = document.getElementById(commmandID); var state = commandNode.getAttribute("state"); button.setAttribute("toggled", (state == onState) ? "true" : "false"); } // --------------------------- Status calls --------------------------- function onStyleChange(theStyle) { //dump("in onStyleChange with " + theStyle + "\n"); var broadcaster = document.getElementById("cmd_" + theStyle); var isOn = broadcaster.getAttribute("state"); // PrintObject(broadcaster); var theButton = document.getElementById(theStyle + "Button"); if (theButton) { theButton.setAttribute("toggled", (isOn == "true") ? 1 : 0); } var theMenuItem = document.getElementById(theStyle + "MenuItem"); if (theMenuItem) { theMenuItem.setAttribute("checked", isOn); } } /* onDirtyChange() is not called */ function onDirtyChange() { // this should happen through style, but that doesn't seem to work. var theButton = document.getElementById("saveButton"); if (theButton) { var isDirty = theButton.getAttribute("dirty"); if (isDirty == "true") { theButton.setAttribute("src", "chrome://editor/skin/images/savemod.gif"); } else { theButton.setAttribute("src", "chrome://editor/skin/images/savefile.gif"); } } } function getColorAndSetColorWell(ColorPickerID, ColorWellID) { var colorWell; if (ColorWellID) colorWell = document.getElementById(ColorWellID); var colorPicker = document.getElementById(ColorPickerID); if (colorPicker) { // Extract color from colorPicker and assign to colorWell. var color = colorPicker.getAttribute("color"); if (colorWell && color) { // Use setAttribute so colorwell can be a XUL element, such as titledbutton colorWell.setAttribute("style", "background-color: " + color); } } //TODO: Trigger UI update to get color from the current caret/selection return color; } var gHaveSpellChecker = false; var gSoughtSpellChecker = false; //----------------------------------------------------------------------------------- function IsSpellCheckerInstalled() { if (gSoughtSpellChecker) return gHaveSpellChecker; var spellcheckerClass = Components.classes["@mozilla.org/spellchecker;1"]; gHaveSpellChecker = (spellcheckerClass != null); gSoughtSpellChecker = true; dump("Have SpellChecker = "+gHaveSpellChecker+"\n"); return gHaveSpellChecker; } var gHaveFind = false; var gSoughtFind = false; //----------------------------------------------------------------------------------- function IsFindInstalled() { if (gSoughtFind) return gHaveFind; var findClass = Components.classes["@mozilla.org/appshell/component/find;1"]; gHaveFind = (findClass != null); gSoughtFind = true; dump("Have Find = "+gHaveFind+"\n"); return gHaveFind; } //----------------------------------------------------------------------------------- function RemoveInapplicableUIElements() { // For items that are in their own menu block, remove associated separator // (we can't use "hidden" since class="hide-in-IM" CSS rule interferes) // if no find, remove find ui if (!IsFindInstalled()) { var findMenuItem = document.getElementById("menu_find"); if (findMenuItem) findMenuItem.setAttribute("hidden", "true"); findMenuItem = document.getElementById("menu_findnext"); if (findMenuItem) findMenuItem.setAttribute("hidden", "true"); var findSepItem = document.getElementById("sep_find"); if (findSepItem) findSepItem.parentNode.removeChild(findSepItem); } // if no spell checker, remove spell checker ui if (!IsSpellCheckerInstalled()) { var spellingButton = document.getElementById("spellingButton"); if (spellingButton) spellingButton.setAttribute("hidden", "true"); var spellingMenuItem = document.getElementById("menu_checkspelling"); if (spellingMenuItem) spellingMenuItem.setAttribute("hidden", "true"); var spellingSepItem = document.getElementById("sep_checkspelling"); if (spellingSepItem) spellingSepItem.parentNode.removeChild(spellingSepItem); } } function GetPrefsService() { // Store the prefs object try { var prefsService = Components.classes["@mozilla.org/preferences;1"]; if (prefsService) prefsService = prefsService.getService(); if (prefsService) gPrefs = prefsService.QueryInterface(Components.interfaces.nsIPref); if (!gPrefs) dump("failed to get prefs service!\n"); } catch(ex) { dump("failed to get prefs service!\n"); } } // Command Updating Strategy: // Don't update on on selection change, only when menu is displayed, // with this "oncreate" hander: function EditorInitTableMenu() { InitJoinCellMenuitem("menu_JoinTableCells"); // Set enable states for all table commands goUpdateTableMenuItems(document.getElementById("composerTableMenuItems")); } function InitJoinCellMenuitem(id) { // Change text on the "Join..." item depending if we // are joining selected cells or just cell to right // TODO: What to do about normal selection that crosses // table border? Try to figure out all cells // included in the selection? var menuText; var menuItem = document.getElementById(id); if (!menuItem) return; // Use "Join selected cells if there's more than 1 cell selected var tagNameObj = new Object; var countObj = new Object; if (window.editorShell.GetSelectedOrParentTableElement(tagNameObj, countObj) && countObj.value > 1) menuText = GetString("JoinSelectedCells"); else menuText = GetString("JoinCellToRight"); menuItem.setAttribute("value",menuText); menuItem.setAttribute("accesskey",GetString("JoinCellAccesskey")); } function InitRemoveStylesMenuitems(removeStylesId, removeLinksId) { // Change wording of menuitems depending on selection var stylesItem = document.getElementById(removeStylesId); var linkItem = document.getElementById(removeLinksId); var isCollapsed = editorShell.editorSelection.isCollapsed; if (stylesItem) { stylesItem.setAttribute("value", isCollapsed ? GetString("StopTextStyles") : GetString("RemoveTextStyles")); stylesItem.setAttribute("accesskey", GetString("RemoveTextStylesAccesskey")); } if (linkItem) { linkItem.setAttribute("value", isCollapsed ? GetString("StopLinks") : GetString("RemoveLinks")); linkItem.setAttribute("accesskey", GetString("RemoveLinksAccesskey")); // Note: disabling text style is a pain since there are so many - forget it! // Disable if not in a link, but always allow "Remove" // if selection isn't collapsed since we only look at anchor node DisableItem(removeLinksId, isCollapsed && !window.editorShell.GetElementOrParentByTagName("href", null)); } } function goUpdateTableMenuItems(commandset) { var enabled = false; var enabledIfTable = false; if (window.editorShell && window.editorShell.documentEditable) { var selectedCountObj = new Object(); var tagNameObj = new Object(); var element = editorShell.GetSelectedOrParentTableElement(tagNameObj, selectedCountObj); if (element) { // Value when we need to have a selected table or inside a table enabledIfTable = true; // All others require being inside a cell or selected cell enabled = (tagNameObj.value == "td"); } } // Loop through command nodes for (var i = 0; i < commandset.childNodes.length; i++) { var commandID = commandset.childNodes[i].getAttribute("id"); if (commandID) { if (commandID == "cmd_InsertTable" || commandID == "cmd_JoinTableCells" || commandID == "cmd_SplitTableCell") { // Call the update method in the command class goUpdateCommand(commandID); } // Directly set with the values calculated here else if (commandID == "cmd_DeleteTable" || commandID == "cmd_NormalizeTable" || commandID == "cmd_TableOrCellColor") { goSetCommandEnabled(commandID, enabledIfTable); } else { goSetCommandEnabled(commandID, enabled); } } } } //----------------------------------------------------------------------------------- // Helpers for inserting and editing tables: function IsInTable() { return (window.editorShell && window.editorShell.documentEditable && null != window.editorShell.GetElementOrParentByTagName("table", null)); } function IsInTableCell() { return (window.editorShell && window.editorShell.documentEditable && null != window.editorShell.GetElementOrParentByTagName("td", null)); } function IsSelectionInOneCell() { var selection = window.editorShell.editorSelection; if (selection && selection.rangeCount == 1) { // We have a "normal" single-range selection if (!selection.isCollapsed && selection.anchorNode != selection.focusNode) { // Check if both nodes are within the same cell var anchorCell = window.editorShell.GetElementOrParentByTagName("td", selection.anchorNode); var focusCell = window.editorShell.GetElementOrParentByTagName("td", selection.focusNode); return (focusCell != null && anchorCell != null && (focusCell == anchorCell)); } // Collapsed selection or anchor == focus (thus must be in 1 cell) return true; } return false; } // Call this with insertAllowed = true to allow inserting if not in existing table, // else use false to do nothing if not in a table function EditorInsertOrEditTable(insertAllowed) { if (IsInTable()) { // Edit properties of existing table window.openDialog("chrome://editor/content/EdTableProps.xul", "_blank", "chrome,close,titlebar,modal", "","TablePanel"); window._content.focus(); } else if(insertAllowed) { EditorInsertTable(); } } function EditorInsertTable() { // Insert a new table window.openDialog("chrome://editor/content/EdInsertTable.xul", "_blank", "chrome,close,titlebar,modal", ""); window._content.focus(); } function EditorTableCellProperties() { var cell = editorShell.GetElementOrParentByTagName("td", null); if (cell) { // Start Table Properties dialog on the "Cell" panel window.openDialog("chrome://editor/content/EdTableProps.xul", "_blank", "chrome,close,titlebar,modal", "", "CellPanel"); window._content.focus(); } } function EditorOnFocus() { if (window.InsertCharWindow) return; // Find window with an InsertCharsWindow and switch association to this one var windowWithDialog = FindEditorWithInsertCharDialog(); if (windowWithDialog) { // Switch the dialog to current window // this sets focus to dialog, so bring focus back to editor window if (SwitchInsertCharToThisWindow(windowWithDialog)) window.focus(); } } function SwitchInsertCharToThisWindow(windowWithDialog) { if (windowWithDialog && windowWithDialog.InsertCharWindow) { // Move dialog association to the current window window.InsertCharWindow = windowWithDialog.InsertCharWindow; windowWithDialog.InsertCharWindow = null; // Switch the dialog's editorShell and opener to current window's window.InsertCharWindow.editorShell = window.editorShell; window.InsertCharWindow.opener = window; // Bring dialog to the forground window.InsertCharWindow.focus(); return true; } return false; } function FindEditorWithInsertCharDialog() { // Find window with an InsertCharsWindow and switch association to this one var windowManager = Components.classes['@mozilla.org/rdf/datasource;1?name=window-mediator'].getService(); var windowManagerInterface = windowManager.QueryInterface( Components.interfaces.nsIWindowMediator); var enumerator = windowManagerInterface.getEnumerator( null ); while ( enumerator.hasMoreElements() ) { var tempWindow = enumerator.getNext(); if (tempWindow != window && tempWindow.InsertCharWindow) { return tempWindow; } } return null; } function EditorFindOrCreateInsertCharWindow() { if (window.InsertCharWindow) window.InsertCharWindow.focus(); else { // Since we switch the dialog during EditorOnFocus(), // this should really never be found, but it's good to be sure var windowWithDialog = FindEditorWithInsertCharDialog(); if (windowWithDialog) { SwitchInsertCharToThisWindow(windowWithDialog); } else { // The dialog will set window.InsertCharWindow to itself window.openDialog("chrome://editor/content/EdInsertChars.xul", "_blank", "chrome,close,titlebar", ""); } } } // Find another HTML editor window to associate with the InsertChar dialog // or close it if none found (May be a mail composer) function SwitchInsertCharToAnotherEditorOrClose() { if (window.InsertCharWindow) { var windowManager = Components.classes['@mozilla.org/rdf/datasource;1?name=window-mediator'].getService(); var windowManagerInterface = windowManager.QueryInterface( Components.interfaces.nsIWindowMediator); var enumerator = windowManagerInterface.getEnumerator( null ); // TODO: Fix this to search for command controllers and look for "cmd_InsertChars" // For now, detect just Web Composer and HTML Mail Composer while ( enumerator.hasMoreElements() ) { var tempWindow = enumerator.getNext(); if (tempWindow != window && tempWindow.editorShell && tempWindow.gIsHTMLEditor || tempWindow.editorShell.editorType == "htmlmail") // tempWindow.editorShell.editorDocument.commandDispatcher.getControllerForCommand("cmd_InsertChars")) { tempWindow.InsertCharWindow = window.InsertCharWindow; window.InsertCharWindow = null; tempWindow.InsertCharWindow.editorShell = tempWindow.editorShell; tempWindow.InsertCharWindow.opener = tempWindow; return; } } // Didn't find another editor - close the dialog window.InsertCharWindow.close(); } }