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