From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001
From: Daniel Baumann
Date: Sun, 7 Apr 2024 19:32:43 +0200
Subject: Adding upstream version 1:115.7.0.
Signed-off-by: Daniel Baumann
---
.../components/dialogs/content/EdAEAttributes.js | 973 +++++++++++++
.../dialogs/content/EdAECSSAttributes.js | 146 ++
.../dialogs/content/EdAEHTMLAttributes.js | 367 +++++
.../dialogs/content/EdAEJSEAttributes.js | 200 +++
.../components/dialogs/content/EdAdvancedEdit.js | 342 +++++
.../dialogs/content/EdAdvancedEdit.xhtml | 182 +++
.../components/dialogs/content/EdButtonProps.js | 146 ++
.../components/dialogs/content/EdButtonProps.xhtml | 92 ++
.../components/dialogs/content/EdColorPicker.js | 297 ++++
.../components/dialogs/content/EdColorPicker.xhtml | 56 +
.../components/dialogs/content/EdColorProps.js | 476 +++++++
.../components/dialogs/content/EdColorProps.xhtml | 134 ++
.../components/dialogs/content/EdConvertToTable.js | 326 +++++
.../dialogs/content/EdConvertToTable.xhtml | 43 +
.../components/dialogs/content/EdDialogCommon.js | 1038 ++++++++++++++
.../components/dialogs/content/EdDialogTemplate.js | 45 +
.../dialogs/content/EdDialogTemplate.xhtml | 23 +
.../components/dialogs/content/EdDictionary.js | 164 +++
.../components/dialogs/content/EdDictionary.xhtml | 59 +
.../components/dialogs/content/EdFieldSetProps.js | 196 +++
.../dialogs/content/EdFieldSetProps.xhtml | 67 +
.../components/dialogs/content/EdFormProps.js | 136 ++
.../components/dialogs/content/EdFormProps.xhtml | 98 ++
.../components/dialogs/content/EdHLineProps.js | 227 +++
.../components/dialogs/content/EdHLineProps.xhtml | 80 ++
.../components/dialogs/content/EdImageDialog.js | 661 +++++++++
.../dialogs/content/EdImageLinkLoader.js | 145 ++
.../components/dialogs/content/EdImageProps.js | 293 ++++
.../components/dialogs/content/EdImageProps.xhtml | 116 ++
.../components/dialogs/content/EdInputImage.js | 189 +++
.../components/dialogs/content/EdInputImage.xhtml | 104 ++
.../components/dialogs/content/EdInputProps.js | 345 +++++
.../components/dialogs/content/EdInputProps.xhtml | 135 ++
.../editor/components/dialogs/content/EdInsSrc.js | 160 +++
.../components/dialogs/content/EdInsSrc.xhtml | 42 +
.../components/dialogs/content/EdInsertChars.js | 409 ++++++
.../components/dialogs/content/EdInsertChars.xhtml | 55 +
.../components/dialogs/content/EdInsertMath.js | 330 +++++
.../components/dialogs/content/EdInsertMath.xhtml | 60 +
.../components/dialogs/content/EdInsertTOC.js | 378 +++++
.../components/dialogs/content/EdInsertTOC.xhtml | 225 +++
.../components/dialogs/content/EdInsertTable.js | 254 ++++
.../components/dialogs/content/EdInsertTable.xhtml | 82 ++
.../components/dialogs/content/EdLabelProps.js | 118 ++
.../components/dialogs/content/EdLabelProps.xhtml | 66 +
.../components/dialogs/content/EdLinkProps.js | 331 +++++
.../components/dialogs/content/EdLinkProps.xhtml | 79 ++
.../components/dialogs/content/EdListProps.js | 455 +++++++
.../components/dialogs/content/EdListProps.xhtml | 73 +
.../dialogs/content/EdNamedAnchorProps.js | 159 +++
.../dialogs/content/EdNamedAnchorProps.xhtml | 43 +
.../components/dialogs/content/EdPageProps.js | 159 +++
.../components/dialogs/content/EdPageProps.xhtml | 50 +
.../editor/components/dialogs/content/EdReplace.js | 382 ++++++
.../components/dialogs/content/EdReplace.xhtml | 65 +
.../components/dialogs/content/EdSelectProps.js | 770 +++++++++++
.../components/dialogs/content/EdSelectProps.xhtml | 143 ++
.../components/dialogs/content/EdSnapToGrid.js | 62 +
.../components/dialogs/content/EdSnapToGrid.xhtml | 47 +
.../components/dialogs/content/EdSpellCheck.js | 495 +++++++
.../components/dialogs/content/EdSpellCheck.xhtml | 113 ++
.../components/dialogs/content/EdTableProps.js | 1439 ++++++++++++++++++++
.../components/dialogs/content/EdTableProps.xhtml | 287 ++++
.../components/dialogs/content/EdTextAreaProps.js | 171 +++
.../dialogs/content/EdTextAreaProps.xhtml | 115 ++
.../components/dialogs/content/EditConflict.js | 42 +
.../components/dialogs/content/EditConflict.xhtml | 40 +
.../components/dialogs/content/EditorPublish.js | 558 ++++++++
.../components/dialogs/content/EditorPublish.xhtml | 132 ++
.../dialogs/content/EditorPublishOverlay.xhtml | 66 +
.../dialogs/content/EditorPublishProgress.js | 391 ++++++
.../dialogs/content/EditorPublishProgress.xhtml | 66 +
.../dialogs/content/EditorPublishSettings.js | 343 +++++
.../dialogs/content/EditorPublishSettings.xhtml | 50 +
.../dialogs/content/EditorSaveAsCharset.js | 155 +++
.../dialogs/content/EditorSaveAsCharset.xhtml | 46 +
.../components/dialogs/content/edImage.inc.xhtml | 248 ++++
comm/suite/editor/components/dialogs/jar.mn | 82 ++
comm/suite/editor/components/dialogs/moz.build | 6 +
comm/suite/editor/components/moz.build | 10 +
.../prefs/content/editorPrefsOverlay.xhtml | 50 +
.../components/prefs/content/pref-composer.xhtml | 84 ++
.../components/prefs/content/pref-editing.js | 187 +++
.../components/prefs/content/pref-editing.xhtml | 181 +++
comm/suite/editor/components/prefs/jar.mn | 11 +
comm/suite/editor/components/prefs/moz.build | 6 +
.../editor/components/texzilla/content/TeXZilla.js | 339 +++++
comm/suite/editor/components/texzilla/jar.mn | 6 +
comm/suite/editor/components/texzilla/moz.build | 6 +
89 files changed, 18623 insertions(+)
create mode 100644 comm/suite/editor/components/dialogs/content/EdAEAttributes.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdAECSSAttributes.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdAEHTMLAttributes.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdAEJSEAttributes.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdAdvancedEdit.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdAdvancedEdit.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdButtonProps.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdButtonProps.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdColorPicker.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdColorPicker.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdColorProps.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdColorProps.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdConvertToTable.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdConvertToTable.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdDialogCommon.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdDialogTemplate.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdDialogTemplate.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdDictionary.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdDictionary.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdFieldSetProps.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdFieldSetProps.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdFormProps.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdFormProps.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdHLineProps.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdHLineProps.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdImageDialog.js
create mode 100755 comm/suite/editor/components/dialogs/content/EdImageLinkLoader.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdImageProps.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdImageProps.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdInputImage.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdInputImage.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdInputProps.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdInputProps.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdInsSrc.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdInsSrc.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdInsertChars.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdInsertChars.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdInsertMath.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdInsertMath.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdInsertTOC.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdInsertTOC.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdInsertTable.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdInsertTable.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdLabelProps.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdLabelProps.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdLinkProps.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdLinkProps.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdListProps.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdListProps.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdNamedAnchorProps.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdNamedAnchorProps.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdPageProps.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdPageProps.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdReplace.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdReplace.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdSelectProps.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdSelectProps.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdSnapToGrid.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdSnapToGrid.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdSpellCheck.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdSpellCheck.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdTableProps.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdTableProps.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EdTextAreaProps.js
create mode 100644 comm/suite/editor/components/dialogs/content/EdTextAreaProps.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EditConflict.js
create mode 100644 comm/suite/editor/components/dialogs/content/EditConflict.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EditorPublish.js
create mode 100644 comm/suite/editor/components/dialogs/content/EditorPublish.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EditorPublishOverlay.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EditorPublishProgress.js
create mode 100644 comm/suite/editor/components/dialogs/content/EditorPublishProgress.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EditorPublishSettings.js
create mode 100644 comm/suite/editor/components/dialogs/content/EditorPublishSettings.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/EditorSaveAsCharset.js
create mode 100644 comm/suite/editor/components/dialogs/content/EditorSaveAsCharset.xhtml
create mode 100644 comm/suite/editor/components/dialogs/content/edImage.inc.xhtml
create mode 100644 comm/suite/editor/components/dialogs/jar.mn
create mode 100644 comm/suite/editor/components/dialogs/moz.build
create mode 100644 comm/suite/editor/components/moz.build
create mode 100644 comm/suite/editor/components/prefs/content/editorPrefsOverlay.xhtml
create mode 100644 comm/suite/editor/components/prefs/content/pref-composer.xhtml
create mode 100644 comm/suite/editor/components/prefs/content/pref-editing.js
create mode 100644 comm/suite/editor/components/prefs/content/pref-editing.xhtml
create mode 100644 comm/suite/editor/components/prefs/jar.mn
create mode 100644 comm/suite/editor/components/prefs/moz.build
create mode 100644 comm/suite/editor/components/texzilla/content/TeXZilla.js
create mode 100644 comm/suite/editor/components/texzilla/jar.mn
create mode 100644 comm/suite/editor/components/texzilla/moz.build
(limited to 'comm/suite/editor/components')
diff --git a/comm/suite/editor/components/dialogs/content/EdAEAttributes.js b/comm/suite/editor/components/dialogs/content/EdAEAttributes.js
new file mode 100644
index 0000000000..52b7e30fac
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdAEAttributes.js
@@ -0,0 +1,973 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// HTML Attributes object for "Name" menulist
+var gHTMLAttr = {};
+
+// JS Events Attributes object for "Name" menulist
+var gJSAttr = {};
+
+// Core HTML attribute values //
+// This is appended to Name menulist when "_core" is attribute name
+var gCoreHTMLAttr = ["^id", "class", "title"];
+
+// Core event attribute values //
+// This is appended to all JS menulists
+// except those elements having "noJSEvents"
+// as a value in their gJSAttr array.
+var gCoreJSEvents = [
+ "onclick",
+ "ondblclick",
+ "onmousedown",
+ "onmouseup",
+ "onmouseover",
+ "onmousemove",
+ "onmouseout",
+ "-",
+ "onkeypress",
+ "onkeydown",
+ "onkeyup",
+];
+
+// Following are commonly-used strings
+
+// Also accept: sRGB: #RRGGBB //
+var gHTMLColors = [
+ "Aqua",
+ "Black",
+ "Blue",
+ "Fuchsia",
+ "Gray",
+ "Green",
+ "Lime",
+ "Maroon",
+ "Navy",
+ "Olive",
+ "Purple",
+ "Red",
+ "Silver",
+ "Teal",
+ "White",
+ "Yellow",
+];
+
+var gHAlign = ["left", "center", "right"];
+
+var gHAlignJustify = ["left", "center", "right", "justify"];
+
+var gHAlignTableContent = ["left", "center", "right", "justify", "char"];
+
+var gVAlignTable = ["top", "middle", "bottom", "baseline"];
+
+var gTarget = ["_blank", "_self", "_parent", "_top"];
+
+// ================ HTML Attributes ================ //
+/* For each element, there is an array of attributes,
+ whose name is the element name,
+ used to fill the "Attribute Name" menulist.
+ For each of those attributes, if they have a specific
+ set of values, those are listed in an array named:
+ "elementName_attName".
+
+ In each values string, the following characters
+ are signal to do input filtering:
+ "#" Allow only integer values
+ "%" Allow integer values or a number ending in "%"
+ "+" Allow integer values and allow "+" or "-" as first character
+ "!" Allow only one character
+ "^" The first character can be only be A-Z, a-z, hyphen, underscore, colon or period
+ "$" is an attribute required by HTML DTD
+*/
+
+/*
+ Most elements have the "dir" attribute,
+ so we use this value array
+ for all elements instead of specifying
+ separately for each element
+*/
+gHTMLAttr.all_dir = ["ltr", "rtl"];
+
+gHTMLAttr.a = [
+ "charset",
+ "type",
+ "name",
+ "href",
+ "^hreflang",
+ "target",
+ "rel",
+ "rev",
+ "!accesskey",
+ "shape", // with imagemap //
+ "coords", // with imagemap //
+ "#tabindex",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.a_target = gTarget;
+
+gHTMLAttr.a_rel = [
+ "alternate",
+ "stylesheet",
+ "start",
+ "next",
+ "prev",
+ "contents",
+ "index",
+ "glossary",
+ "copyright",
+ "chapter",
+ "section",
+ "subsection",
+ "appendix",
+ "help",
+ "bookmark",
+];
+
+gHTMLAttr.a_rev = [
+ "alternate",
+ "stylesheet",
+ "start",
+ "next",
+ "prev",
+ "contents",
+ "index",
+ "glossary",
+ "copyright",
+ "chapter",
+ "section",
+ "subsection",
+ "appendix",
+ "help",
+ "bookmark",
+];
+
+gHTMLAttr.a_shape = ["rect", "circle", "poly", "default"];
+
+gHTMLAttr.abbr = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.acronym = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.address = ["_core", "-", "^lang", "dir"];
+
+// this is deprecated //
+gHTMLAttr.applet = [
+ "codebase",
+ "archive",
+ "code",
+ "object",
+ "alt",
+ "name",
+ "%$width",
+ "%$height",
+ "align",
+ "#hspace",
+ "#vspace",
+ "-",
+ "_core",
+];
+
+gHTMLAttr.applet_align = ["top", "middle", "bottom", "left", "right"];
+
+gHTMLAttr.area = [
+ "shape",
+ "coords",
+ "href",
+ "nohref",
+ "target",
+ "$alt",
+ "#tabindex",
+ "!accesskey",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.area_target = gTarget;
+
+gHTMLAttr.area_shape = ["rect", "circle", "poly", "default"];
+
+gHTMLAttr.area_nohref = ["nohref"];
+
+gHTMLAttr.b = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.base = ["href", "target"];
+
+gHTMLAttr.base_target = gTarget;
+
+// this is deprecated //
+gHTMLAttr.basefont = ["^id", "$size", "color", "face"];
+
+gHTMLAttr.basefont_color = gHTMLColors;
+
+gHTMLAttr.bdo = ["_core", "-", "^lang", "$dir"];
+
+gHTMLAttr.bdo_dir = ["ltr", "rtl"];
+
+gHTMLAttr.big = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.blockquote = ["cite", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.body = [
+ "background",
+ "bgcolor",
+ "text",
+ "link",
+ "vlink",
+ "alink",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.body_bgcolor = gHTMLColors;
+
+gHTMLAttr.body_text = gHTMLColors;
+
+gHTMLAttr.body_link = gHTMLColors;
+
+gHTMLAttr.body_vlink = gHTMLColors;
+
+gHTMLAttr.body_alink = gHTMLColors;
+
+gHTMLAttr.br = ["clear", "-", "_core"];
+
+gHTMLAttr.br_clear = ["none", "left", "all", "right"];
+
+gHTMLAttr.button = [
+ "name",
+ "value",
+ "$type",
+ "disabled",
+ "#tabindex",
+ "!accesskey",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.button_type = ["submit", "button", "reset"];
+
+gHTMLAttr.button_disabled = ["disabled"];
+
+gHTMLAttr.caption = ["align", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.caption_align = ["top", "bottom", "left", "right"];
+
+// this is deprecated //
+gHTMLAttr.center = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.cite = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.code = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.col = [
+ "#$span",
+ "%width",
+ "align",
+ "!char",
+ "#charoff",
+ "valign",
+ "char",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.col_span = [
+ "1", // default
+];
+
+gHTMLAttr.col_align = gHAlignTableContent;
+
+gHTMLAttr.col_valign = ["top", "middle", "bottom", "baseline"];
+
+gHTMLAttr.colgroup = [
+ "#$span",
+ "%width",
+ "align",
+ "!char",
+ "#charoff",
+ "valign",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.colgroup_span = [
+ "1", // default
+];
+
+gHTMLAttr.colgroup_align = gHAlignTableContent;
+
+gHTMLAttr.colgroup_valign = ["top", "middle", "bottom", "baseline"];
+
+gHTMLAttr.dd = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.del = ["cite", "datetime", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.dfn = ["_core", "-", "^lang", "dir"];
+
+// this is deprecated //
+gHTMLAttr.dir = ["compact", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.dir_compact = ["compact"];
+
+gHTMLAttr.div = ["align", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.div_align = gHAlignJustify;
+
+gHTMLAttr.dl = ["compact", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.dl_compact = ["compact"];
+
+gHTMLAttr.dt = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.em = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.fieldset = ["_core", "-", "^lang", "dir"];
+
+// this is deprecated //
+gHTMLAttr.font = ["+size", "color", "face", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.font_color = gHTMLColors;
+
+gHTMLAttr.form = [
+ "$action",
+ "$method",
+ "enctype",
+ "accept",
+ "name",
+ "accept-charset",
+ "target",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.form_method = ["get", "post"];
+
+gHTMLAttr.form_enctype = ["application/x-www-form-urlencoded"];
+
+gHTMLAttr.form_target = gTarget;
+
+gHTMLAttr.frame = [
+ "longdesc",
+ "name",
+ "src",
+ "#frameborder",
+ "#marginwidth",
+ "#marginheight",
+ "noresize",
+ "$scrolling",
+];
+
+gHTMLAttr.frame_frameborder = ["1", "0"];
+
+gHTMLAttr.frame_noresize = ["noresize"];
+
+gHTMLAttr.frame_scrolling = ["auto", "yes", "no"];
+
+gHTMLAttr.frameset = ["rows", "cols", "-", "_core"];
+
+gHTMLAttr.h1 = ["align", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.h1_align = gHAlignJustify;
+
+gHTMLAttr.h2 = ["align", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.h2_align = gHAlignJustify;
+
+gHTMLAttr.h3 = ["align", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.h3_align = gHAlignJustify;
+
+gHTMLAttr.h4 = ["align", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.h4_align = gHAlignJustify;
+
+gHTMLAttr.h5 = ["align", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.h5_align = gHAlignJustify;
+
+gHTMLAttr.h6 = ["align", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.h6_align = gHAlignJustify;
+
+gHTMLAttr.head = ["profile", "-", "^lang", "dir"];
+
+gHTMLAttr.hr = [
+ "align",
+ "noshade",
+ "#size",
+ "%width",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.hr_align = gHAlign;
+
+gHTMLAttr.hr_noshade = ["noshade"];
+
+gHTMLAttr.html = ["version", "-", "^lang", "dir"];
+
+gHTMLAttr.i = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.iframe = [
+ "longdesc",
+ "name",
+ "src",
+ "$frameborder",
+ "marginwidth",
+ "marginheight",
+ "$scrolling",
+ "align",
+ "%height",
+ "%width",
+ "-",
+ "_core",
+];
+
+gHTMLAttr.iframe_frameborder = ["1", "0"];
+
+gHTMLAttr.iframe_scrolling = ["auto", "yes", "no"];
+
+gHTMLAttr.iframe_align = ["top", "middle", "bottom", "left", "right"];
+
+gHTMLAttr.img = [
+ "$src",
+ "$alt",
+ "longdesc",
+ "name",
+ "%height",
+ "%width",
+ "usemap",
+ "ismap",
+ "align",
+ "#border",
+ "#hspace",
+ "#vspace",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.img_ismap = ["ismap"];
+
+gHTMLAttr.img_align = ["top", "middle", "bottom", "left", "right"];
+
+gHTMLAttr.input = [
+ "$type",
+ "name",
+ "value",
+ "checked",
+ "disabled",
+ "readonly",
+ "#size",
+ "#maxlength",
+ "src",
+ "alt",
+ "usemap",
+ "ismap",
+ "#tabindex",
+ "!accesskey",
+ "accept",
+ "align",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.input_type = [
+ "text",
+ "password",
+ "checkbox",
+ "radio",
+ "submit",
+ "reset",
+ "file",
+ "hidden",
+ "image",
+ "button",
+];
+
+gHTMLAttr.input_checked = ["checked"];
+
+gHTMLAttr.input_disabled = ["disabled"];
+
+gHTMLAttr.input_readonly = ["readonly"];
+
+gHTMLAttr.input_ismap = ["ismap"];
+
+gHTMLAttr.input_align = ["top", "middle", "bottom", "left", "right"];
+
+gHTMLAttr.ins = ["cite", "datetime", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.isindex = ["prompt", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.kbd = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.label = ["for", "!accesskey", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.legend = ["!accesskey", "align", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.legend_align = ["top", "bottom", "left", "right"];
+
+gHTMLAttr.li = ["type", "#value", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.li_type = ["disc", "square", "circle", "-", "1", "a", "A", "i", "I"];
+
+gHTMLAttr.link = [
+ "charset",
+ "href",
+ "^hreflang",
+ "type",
+ "rel",
+ "rev",
+ "media",
+ "target",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.link_target = gTarget;
+
+gHTMLAttr.link_rel = [
+ "alternate",
+ "stylesheet",
+ "start",
+ "next",
+ "prev",
+ "contents",
+ "index",
+ "glossary",
+ "copyright",
+ "chapter",
+ "section",
+ "subsection",
+ "appendix",
+ "help",
+ "bookmark",
+];
+
+gHTMLAttr.link_rev = [
+ "alternate",
+ "stylesheet",
+ "start",
+ "next",
+ "prev",
+ "contents",
+ "index",
+ "glossary",
+ "copyright",
+ "chapter",
+ "section",
+ "subsection",
+ "appendix",
+ "help",
+ "bookmark",
+];
+
+gHTMLAttr.map = ["$name", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.menu = ["compact", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.menu_compact = ["compact"];
+
+gHTMLAttr.meta = [
+ "http-equiv",
+ "name",
+ "$content",
+ "scheme",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.noframes = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.noscript = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.object = [
+ "declare",
+ "classid",
+ "codebase",
+ "data",
+ "type",
+ "codetype",
+ "archive",
+ "standby",
+ "%height",
+ "%width",
+ "usemap",
+ "name",
+ "#tabindex",
+ "align",
+ "#border",
+ "#hspace",
+ "#vspace",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.object_declare = ["declare"];
+
+gHTMLAttr.object_align = ["top", "middle", "bottom", "left", "right"];
+
+gHTMLAttr.ol = ["type", "compact", "#start", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.ol_type = ["1", "a", "A", "i", "I"];
+
+gHTMLAttr.ol_compact = ["compact"];
+
+gHTMLAttr.optgroup = ["disabled", "$label", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.optgroup_disabled = ["disabled"];
+
+gHTMLAttr.option = [
+ "selected",
+ "disabled",
+ "label",
+ "value",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.option_selected = ["selected"];
+
+gHTMLAttr.option_disabled = ["disabled"];
+
+gHTMLAttr.p = ["align", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.p_align = gHAlignJustify;
+
+gHTMLAttr.param = ["^id", "$name", "value", "$valuetype", "type"];
+
+gHTMLAttr.param_valuetype = ["data", "ref", "object"];
+
+gHTMLAttr.pre = ["%width", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.q = ["cite", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.s = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.samp = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.script = ["charset", "$type", "language", "src", "defer"];
+
+gHTMLAttr.script_defer = ["defer"];
+
+gHTMLAttr.select = [
+ "name",
+ "#size",
+ "multiple",
+ "disabled",
+ "#tabindex",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.select_multiple = ["multiple"];
+
+gHTMLAttr.select_disabled = ["disabled"];
+
+gHTMLAttr.small = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.span = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.strike = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.strong = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.style = ["$type", "media", "title", "-", "^lang", "dir"];
+
+gHTMLAttr.sub = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.sup = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.table = [
+ "summary",
+ "%width",
+ "#border",
+ "frame",
+ "rules",
+ "#cellspacing",
+ "#cellpadding",
+ "align",
+ "bgcolor",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.table_frame = [
+ "void",
+ "above",
+ "below",
+ "hsides",
+ "lhs",
+ "rhs",
+ "vsides",
+ "box",
+ "border",
+];
+
+gHTMLAttr.table_rules = ["none", "groups", "rows", "cols", "all"];
+
+// Note; This is alignment of the table,
+// not table contents, like all other table child elements
+gHTMLAttr.table_align = gHAlign;
+
+gHTMLAttr.table_bgcolor = gHTMLColors;
+
+gHTMLAttr.tbody = [
+ "align",
+ "!char",
+ "#charoff",
+ "valign",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.tbody_align = gHAlignTableContent;
+
+gHTMLAttr.tbody_valign = gVAlignTable;
+
+gHTMLAttr.td = [
+ "abbr",
+ "axis",
+ "headers",
+ "scope",
+ "$#rowspan",
+ "$#colspan",
+ "align",
+ "!char",
+ "#charoff",
+ "valign",
+ "nowrap",
+ "bgcolor",
+ "%width",
+ "%height",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.td_scope = ["row", "col", "rowgroup", "colgroup"];
+
+gHTMLAttr.td_rowspan = [
+ "1", // default
+];
+
+gHTMLAttr.td_colspan = [
+ "1", // default
+];
+
+gHTMLAttr.td_align = gHAlignTableContent;
+
+gHTMLAttr.td_valign = gVAlignTable;
+
+gHTMLAttr.td_nowrap = ["nowrap"];
+
+gHTMLAttr.td_bgcolor = gHTMLColors;
+
+gHTMLAttr.textarea = [
+ "name",
+ "$#rows",
+ "$#cols",
+ "disabled",
+ "readonly",
+ "#tabindex",
+ "!accesskey",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.textarea_disabled = ["disabled"];
+
+gHTMLAttr.textarea_readonly = ["readonly"];
+
+gHTMLAttr.tfoot = [
+ "align",
+ "!char",
+ "#charoff",
+ "valign",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.tfoot_align = gHAlignTableContent;
+
+gHTMLAttr.tfoot_valign = gVAlignTable;
+
+gHTMLAttr.th = [
+ "abbr",
+ "axis",
+ "headers",
+ "scope",
+ "$#rowspan",
+ "$#colspan",
+ "align",
+ "!char",
+ "#charoff",
+ "valign",
+ "nowrap",
+ "bgcolor",
+ "%width",
+ "%height",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.th_scope = ["row", "col", "rowgroup", "colgroup"];
+
+gHTMLAttr.th_rowspan = [
+ "1", // default
+];
+
+gHTMLAttr.th_colspan = [
+ "1", // default
+];
+
+gHTMLAttr.th_align = gHAlignTableContent;
+
+gHTMLAttr.th_valign = gVAlignTable;
+
+gHTMLAttr.th_nowrap = ["nowrap"];
+
+gHTMLAttr.th_bgcolor = gHTMLColors;
+
+gHTMLAttr.thead = [
+ "align",
+ "!char",
+ "#charoff",
+ "valign",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.thead_align = gHAlignTableContent;
+
+gHTMLAttr.thead_valign = gVAlignTable;
+
+gHTMLAttr.title = ["^lang", "dir"];
+
+gHTMLAttr.tr = [
+ "align",
+ "!char",
+ "#charoff",
+ "valign",
+ "bgcolor",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.tr_align = gHAlignTableContent;
+
+gHTMLAttr.tr_valign = gVAlignTable;
+
+gHTMLAttr.tr_bgcolor = gHTMLColors;
+
+gHTMLAttr.tt = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.u = ["_core", "-", "^lang", "dir"];
+gHTMLAttr.ul = ["type", "compact", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.ul_type = ["disc", "square", "circle"];
+
+gHTMLAttr.ul_compact = ["compact"];
+
+// Prefix with "_" since this is reserved (it's stripped out)
+gHTMLAttr._var = ["_core", "-", "^lang", "dir"];
+
+// ================ JS Attributes ================ //
+// These are element specific even handlers.
+/* Most all elements use gCoreJSEvents, so those
+ are assumed except for those listed here with "noEvents"
+*/
+
+gJSAttr.a = ["onfocus", "onblur"];
+
+gJSAttr.area = ["onfocus", "onblur"];
+
+gJSAttr.body = ["onload", "onupload"];
+
+gJSAttr.button = ["onfocus", "onblur"];
+
+gJSAttr.form = ["onsubmit", "onreset"];
+
+gJSAttr.frameset = ["onload", "onunload"];
+
+gJSAttr.input = ["onfocus", "onblur", "onselect", "onchange"];
+
+gJSAttr.label = ["onfocus", "onblur"];
+
+gJSAttr.select = ["onfocus", "onblur", "onchange"];
+
+gJSAttr.textarea = ["onfocus", "onblur", "onselect", "onchange"];
+
+// Elements that don't have JSEvents:
+gJSAttr.font = ["noJSEvents"];
+
+gJSAttr.applet = ["noJSEvents"];
+
+gJSAttr.isindex = ["noJSEvents"];
+
+gJSAttr.iframe = ["noJSEvents"];
diff --git a/comm/suite/editor/components/dialogs/content/EdAECSSAttributes.js b/comm/suite/editor/components/dialogs/content/EdAECSSAttributes.js
new file mode 100644
index 0000000000..977068bd70
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdAECSSAttributes.js
@@ -0,0 +1,146 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdAdvancedEdit.js */
+/* import-globals-from EdDialogCommon.js */
+
+// build attribute list in tree form from element attributes
+function BuildCSSAttributeTable() {
+ var style = gElement.style;
+ if (style == undefined) {
+ dump("Inline styles undefined\n");
+ return;
+ }
+
+ var declLength = style.length;
+
+ if (declLength == undefined || declLength == 0) {
+ if (declLength == undefined) {
+ dump("Failed to query the number of inline style declarations\n");
+ }
+
+ return;
+ }
+
+ if (declLength > 0) {
+ for (var i = 0; i < declLength; ++i) {
+ var name = style.item(i);
+ var value = style.getPropertyValue(name);
+ AddTreeItem(name, value, "CSSAList", CSSAttrs);
+ }
+ }
+
+ ClearCSSInputWidgets();
+}
+
+function onChangeCSSAttribute() {
+ var name = TrimString(gDialog.AddCSSAttributeNameInput.value);
+ if (!name) {
+ return;
+ }
+
+ var value = TrimString(gDialog.AddCSSAttributeValueInput.value);
+
+ // First try to update existing attribute
+ // If not found, add new attribute
+ if (!UpdateExistingAttribute(name, value, "CSSAList") && value) {
+ AddTreeItem(name, value, "CSSAList", CSSAttrs);
+ }
+}
+
+function ClearCSSInputWidgets() {
+ gDialog.AddCSSAttributeTree.view.selection.clearSelection();
+ gDialog.AddCSSAttributeNameInput.value = "";
+ gDialog.AddCSSAttributeValueInput.value = "";
+ SetTextboxFocus(gDialog.AddCSSAttributeNameInput);
+}
+
+function onSelectCSSTreeItem() {
+ if (!gDoOnSelectTree) {
+ return;
+ }
+
+ var tree = gDialog.AddCSSAttributeTree;
+ if (tree && tree.view.selection.count) {
+ gDialog.AddCSSAttributeNameInput.value = GetTreeItemAttributeStr(
+ getSelectedItem(tree)
+ );
+ gDialog.AddCSSAttributeValueInput.value = GetTreeItemValueStr(
+ getSelectedItem(tree)
+ );
+ }
+}
+
+function onInputCSSAttributeName() {
+ var attName = TrimString(
+ gDialog.AddCSSAttributeNameInput.value
+ ).toLowerCase();
+ var newValue = "";
+
+ var existingValue = GetAndSelectExistingAttributeValue(attName, "CSSAList");
+ if (existingValue) {
+ newValue = existingValue;
+ }
+
+ gDialog.AddCSSAttributeValueInput.value = newValue;
+}
+
+function editCSSAttributeValue(targetCell) {
+ if (IsNotTreeHeader(targetCell)) {
+ gDialog.AddCSSAttributeValueInput.inputField.select();
+ }
+}
+
+function UpdateCSSAttributes() {
+ var CSSAList = document.getElementById("CSSAList");
+ var styleString = "";
+ for (var i = 0; i < CSSAList.childNodes.length; i++) {
+ var item = CSSAList.childNodes[i];
+ var name = GetTreeItemAttributeStr(item);
+ var value = GetTreeItemValueStr(item);
+ // this code allows users to be sloppy in typing in values, and enter
+ // things like "foo: " and "bar;". This will trim off everything after the
+ // respective character.
+ if (name.includes(":")) {
+ name = name.substring(0, name.lastIndexOf(":"));
+ }
+ if (value.includes(";")) {
+ value = value.substring(0, value.lastIndexOf(";"));
+ }
+ if (i == CSSAList.childNodes.length - 1) {
+ // Last property.
+ styleString += name + ": " + value + ";";
+ } else {
+ styleString += name + ": " + value + "; ";
+ }
+ }
+ if (styleString) {
+ // Use editor transactions if modifying the element directly in the document
+ doRemoveAttribute("style");
+ doSetAttribute("style", styleString); // NOTE BUG 18894!!!
+ } else if (gElement.getAttribute("style")) {
+ doRemoveAttribute("style");
+ }
+}
+
+function RemoveCSSAttribute() {
+ // We only allow 1 selected item
+ if (gDialog.AddCSSAttributeTree.view.selection.count) {
+ // Remove the item from the tree
+ // We always rebuild complete "style" string,
+ // so no list of "removed" items
+ getSelectedItem(gDialog.AddCSSAttributeTree).remove();
+
+ ClearCSSInputWidgets();
+ }
+}
+
+function SelectCSSTree(index) {
+ gDoOnSelectTree = false;
+ try {
+ gDialog.AddCSSAttributeTree.selectedIndex = index;
+ } catch (e) {}
+ gDoOnSelectTree = true;
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdAEHTMLAttributes.js b/comm/suite/editor/components/dialogs/content/EdAEHTMLAttributes.js
new file mode 100644
index 0000000000..1f96762754
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdAEHTMLAttributes.js
@@ -0,0 +1,367 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdAdvancedEdit.js */
+/* import-globals-from EdDialogCommon.js */
+
+function BuildHTMLAttributeNameList() {
+ gDialog.AddHTMLAttributeNameInput.removeAllItems();
+
+ var elementName = gElement.localName;
+ var attNames = gHTMLAttr[elementName];
+
+ if (attNames && attNames.length) {
+ var menuitem;
+
+ for (var i = 0; i < attNames.length; i++) {
+ var name = attNames[i];
+
+ if (name == "_core") {
+ // Signal to append the common 'core' attributes.
+ for (var j = 0; j < gCoreHTMLAttr.length; j++) {
+ name = gCoreHTMLAttr[j];
+
+ // only filtering rule used for core attributes as of 8-20-01
+ // Add more rules if necessary.
+ if (name.includes("^")) {
+ name = name.replace(/\^/g, "");
+ menuitem = gDialog.AddHTMLAttributeNameInput.appendItem(name, name);
+ menuitem.setAttribute("limitFirstChar", "true");
+ } else {
+ gDialog.AddHTMLAttributeNameInput.appendItem(name, name);
+ }
+ }
+ } else if (name == "-") {
+ // Signal for separator
+ var popup = gDialog.AddHTMLAttributeNameInput.menupopup;
+ if (popup) {
+ var sep = document.createXULElement("menuseparator");
+ if (sep) {
+ popup.appendChild(sep);
+ }
+ }
+ } else {
+ // Get information about value filtering
+ let forceOneChar = name.includes("!");
+ let forceInteger = name.includes("#");
+ let forceSignedInteger = name.includes("+");
+ let forceIntOrPercent = name.includes("%");
+ let limitFirstChar = name.includes("^");
+ // let required = name.includes("$");
+
+ // Strip flag characters
+ name = name.replace(/[!^#%$+]/g, "");
+
+ menuitem = gDialog.AddHTMLAttributeNameInput.appendItem(name, name);
+ if (menuitem) {
+ // Signify "required" attributes by special style
+ // TODO: Don't do this until next version, when we add
+ // explanatory text and an 'Autofill Required Attributes' button
+ // if (required)
+ // menuitem.setAttribute("class", "menuitem-highlight-1");
+
+ // Set flags to filter value input
+ if (forceOneChar) {
+ menuitem.setAttribute("forceOneChar", "true");
+ }
+ if (limitFirstChar) {
+ menuitem.setAttribute("limitFirstChar", "true");
+ }
+ if (forceInteger) {
+ menuitem.setAttribute("forceInteger", "true");
+ }
+ if (forceSignedInteger) {
+ menuitem.setAttribute("forceSignedInteger", "true");
+ }
+ if (forceIntOrPercent) {
+ menuitem.setAttribute("forceIntOrPercent", "true");
+ }
+ }
+ }
+ }
+ }
+}
+
+// build attribute list in tree form from element attributes
+function BuildHTMLAttributeTable() {
+ var nodeMap = gElement.attributes;
+ var i;
+ if (nodeMap.length > 0) {
+ var added = false;
+ for (i = 0; i < nodeMap.length; i++) {
+ let name = nodeMap[i].name.trim().toLowerCase();
+ if (
+ CheckAttributeNameSimilarity(nodeMap[i].nodeName, HTMLAttrs) ||
+ name.startsWith("on") ||
+ name == "style"
+ ) {
+ continue; // repeated or non-HTML attribute, ignore this one and go to next
+ }
+ if (
+ !name.startsWith("_moz") &&
+ AddTreeItem(name, nodeMap[i].value, "HTMLAList", HTMLAttrs)
+ ) {
+ added = true;
+ }
+ }
+
+ if (added) {
+ SelectHTMLTree(0);
+ }
+ }
+}
+
+function ClearHTMLInputWidgets() {
+ gDialog.AddHTMLAttributeTree.view.selection.clearSelection();
+ gDialog.AddHTMLAttributeNameInput.value = "";
+ gDialog.AddHTMLAttributeValueInput.value = "";
+ SetTextboxFocus(gDialog.AddHTMLAttributeNameInput);
+}
+
+function onSelectHTMLTreeItem() {
+ if (!gDoOnSelectTree) {
+ return;
+ }
+
+ var tree = gDialog.AddHTMLAttributeTree;
+ if (tree && tree.view.selection.count) {
+ var inputName = TrimString(
+ gDialog.AddHTMLAttributeNameInput.value
+ ).toLowerCase();
+ var selectedItem = getSelectedItem(tree);
+ var selectedName = selectedItem.firstChild.firstChild.getAttribute("label");
+
+ if (inputName == selectedName) {
+ // Already editing selected name - just update the value input
+ gDialog.AddHTMLAttributeValueInput.value = GetTreeItemValueStr(
+ selectedItem
+ );
+ } else {
+ gDialog.AddHTMLAttributeNameInput.value = selectedName;
+
+ // Change value input based on new selected name
+ onInputHTMLAttributeName();
+ }
+ }
+}
+
+function onInputHTMLAttributeName() {
+ let attName = gDialog.AddHTMLAttributeNameInput.value.toLowerCase().trim();
+
+ // Clear value widget, but prevent triggering update in tree
+ gUpdateTreeValue = false;
+ gDialog.AddHTMLAttributeValueInput.value = "";
+ gUpdateTreeValue = true;
+
+ if (attName) {
+ // Get value list for current attribute name
+ var valueListName;
+
+ // Most elements have the "dir" attribute,
+ // so we have just one array for the allowed values instead
+ // requiring duplicate entries for each element in EdAEAttributes.js
+ if (attName == "dir") {
+ valueListName = "all_dir";
+ } else {
+ valueListName = gElement.localName + "_" + attName;
+ }
+
+ // Strip off leading "_" we sometimes use (when element name is reserved word)
+ if (valueListName.startsWith("_")) {
+ valueListName = valueListName.slice(1);
+ }
+
+ var newValue = "";
+ var listLen = 0;
+
+ // Index to which widget we were using to edit the value
+ var deckIndex = gDialog.AddHTMLAttributeValueDeck.getAttribute(
+ "selectedIndex"
+ );
+
+ if (valueListName in gHTMLAttr) {
+ var valueList = gHTMLAttr[valueListName];
+
+ listLen = valueList.length;
+ if (listLen == 1) {
+ newValue = valueList[0];
+ }
+
+ // Note: For case where "value list" is actually just
+ // one (default) item, don't use menulist for that
+ if (listLen > 1) {
+ gDialog.AddHTMLAttributeValueMenulist.removeAllItems();
+
+ if (deckIndex != "1") {
+ // Switch to using editable menulist
+ gDialog.AddHTMLAttributeValueInput =
+ gDialog.AddHTMLAttributeValueMenulist;
+ gDialog.AddHTMLAttributeValueDeck.setAttribute("selectedIndex", "1");
+ }
+ // Rebuild the list
+ for (var i = 0; i < listLen; i++) {
+ if (valueList[i] == "-") {
+ // Signal for separator
+ var popup = gDialog.AddHTMLAttributeValueInput.menupopup;
+ if (popup) {
+ var sep = document.createXULElement("menuseparator");
+ if (sep) {
+ popup.appendChild(sep);
+ }
+ }
+ } else {
+ gDialog.AddHTMLAttributeValueMenulist.appendItem(
+ valueList[i],
+ valueList[i]
+ );
+ }
+ }
+ }
+ }
+
+ if (listLen <= 1 && deckIndex != "0") {
+ // No list: Use textbox for input instead
+ gDialog.AddHTMLAttributeValueInput = gDialog.AddHTMLAttributeValueTextbox;
+ gDialog.AddHTMLAttributeValueDeck.setAttribute("selectedIndex", "0");
+ }
+
+ // If attribute already exists in tree, use associated value,
+ // else use default found above
+ var existingValue = GetAndSelectExistingAttributeValue(
+ attName,
+ "HTMLAList"
+ );
+ if (existingValue) {
+ newValue = existingValue;
+ }
+
+ gDialog.AddHTMLAttributeValueInput.value = newValue;
+
+ if (!existingValue) {
+ onInputHTMLAttributeValue();
+ }
+ }
+}
+
+function onInputHTMLAttributeValue() {
+ if (!gUpdateTreeValue) {
+ return;
+ }
+
+ var name = TrimString(gDialog.AddHTMLAttributeNameInput.value);
+ if (!name) {
+ return;
+ }
+
+ // Trim spaces only from left since we must allow spaces within the string
+ // (we always reset the input field's value below)
+ var value = TrimStringLeft(gDialog.AddHTMLAttributeValueInput.value);
+ if (value) {
+ // Do value filtering based on type of attribute
+ // (Do not use "forceInteger()" to avoid multiple
+ // resetting of input's value and flickering)
+ var selectedItem = gDialog.AddHTMLAttributeNameInput.selectedItem;
+
+ if (selectedItem) {
+ if (
+ selectedItem.getAttribute("forceOneChar") == "true" &&
+ value.length > 1
+ ) {
+ value = value.slice(0, 1);
+ }
+
+ if (selectedItem.getAttribute("forceIntOrPercent") == "true") {
+ // Allow integer with optional "%" as last character
+ var percent = TrimStringRight(value).slice(-1);
+ value = value.replace(/\D+/g, "");
+ if (percent == "%") {
+ value += percent;
+ }
+ } else if (selectedItem.getAttribute("forceInteger") == "true") {
+ value = value.replace(/\D+/g, "");
+ } else if (selectedItem.getAttribute("forceSignedInteger") == "true") {
+ // Allow integer with optional "+" or "-" as first character
+ var sign = value[0];
+ value = value.replace(/\D+/g, "");
+ if (sign == "+" || sign == "-") {
+ value = sign + value;
+ }
+ }
+
+ // Special case attributes
+ if (selectedItem.getAttribute("limitFirstChar") == "true") {
+ // Limit first character to letter, and all others to
+ // letters, numbers, and a few others
+ value = value
+ .replace(/^[^a-zA-Z\u0080-\uFFFF]/, "")
+ .replace(/[^a-zA-Z0-9_\.\-\:\u0080-\uFFFF]+/g, "");
+ }
+
+ // Update once only if it changed
+ if (value != gDialog.AddHTMLAttributeValueInput.value) {
+ gDialog.AddHTMLAttributeValueInput.value = value;
+ }
+ }
+ }
+
+ // Update value in the tree list
+ // If not found, add new attribute
+ if (!UpdateExistingAttribute(name, value, "HTMLAList") && value) {
+ AddTreeItem(name, value, "HTMLAList", HTMLAttrs);
+ }
+}
+
+function editHTMLAttributeValue(targetCell) {
+ if (IsNotTreeHeader(targetCell)) {
+ gDialog.AddHTMLAttributeValueInput.select();
+ }
+}
+
+// update the object with added and removed attributes
+function UpdateHTMLAttributes() {
+ var HTMLAList = document.getElementById("HTMLAList");
+ var i;
+
+ // remove removed attributes
+ for (i = 0; i < HTMLRAttrs.length; i++) {
+ var name = HTMLRAttrs[i];
+
+ if (gElement.hasAttribute(name)) {
+ doRemoveAttribute(name);
+ }
+ }
+
+ // Set added or changed attributes
+ for (i = 0; i < HTMLAList.childNodes.length; i++) {
+ var item = HTMLAList.childNodes[i];
+ doSetAttribute(GetTreeItemAttributeStr(item), GetTreeItemValueStr(item));
+ }
+}
+
+function RemoveHTMLAttribute() {
+ // We only allow 1 selected item
+ if (gDialog.AddHTMLAttributeTree.view.selection.count) {
+ var item = getSelectedItem(gDialog.AddHTMLAttributeTree);
+ var attr = GetTreeItemAttributeStr(item);
+
+ // remove the item from the attribute array
+ HTMLRAttrs[HTMLRAttrs.length] = attr;
+ RemoveNameFromAttArray(attr, HTMLAttrs);
+
+ // Remove the item from the tree
+ item.remove();
+
+ // Clear inputs and selected item in tree
+ ClearHTMLInputWidgets();
+ }
+}
+
+function SelectHTMLTree(index) {
+ gDoOnSelectTree = false;
+ try {
+ gDialog.AddHTMLAttributeTree.selectedIndex = index;
+ } catch (e) {}
+ gDoOnSelectTree = true;
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdAEJSEAttributes.js b/comm/suite/editor/components/dialogs/content/EdAEJSEAttributes.js
new file mode 100644
index 0000000000..c15c938b3e
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdAEJSEAttributes.js
@@ -0,0 +1,200 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdAdvancedEdit.js */
+/* import-globals-from EdDialogCommon.js */
+
+function BuildJSEAttributeNameList() {
+ gDialog.AddJSEAttributeNameList.removeAllItems();
+
+ // Get events specific to current element
+ var elementName = gElement.localName;
+ if (elementName in gJSAttr) {
+ var attNames = gJSAttr[elementName];
+ var i;
+ var popup;
+ var sep;
+
+ if (attNames && attNames.length) {
+ // Since we don't allow user-editable JS events yet (but we will soon)
+ // simply remove the JS tab to not allow adding JS events
+ if (attNames[0] == "noJSEvents") {
+ var tab = document.getElementById("tabJSE");
+ if (tab) {
+ tab.remove();
+ }
+
+ return;
+ }
+
+ for (i = 0; i < attNames.length; i++) {
+ gDialog.AddJSEAttributeNameList.appendItem(attNames[i], attNames[i]);
+ }
+
+ popup = gDialog.AddJSEAttributeNameList.firstChild;
+ if (popup) {
+ sep = document.createXULElement("menuseparator");
+ if (sep) {
+ popup.appendChild(sep);
+ }
+ }
+ }
+ }
+
+ // Always add core JS events unless we aborted above
+ for (i = 0; i < gCoreJSEvents.length; i++) {
+ if (gCoreJSEvents[i] == "-") {
+ if (!popup) {
+ popup = gDialog.AddJSEAttributeNameList.firstChild;
+ }
+
+ sep = document.createXULElement("menuseparator");
+
+ if (popup && sep) {
+ popup.appendChild(sep);
+ }
+ } else {
+ gDialog.AddJSEAttributeNameList.appendItem(
+ gCoreJSEvents[i],
+ gCoreJSEvents[i]
+ );
+ }
+ }
+
+ gDialog.AddJSEAttributeNameList.selectedIndex = 0;
+
+ // Use current name and value of first tree item if it exists
+ onSelectJSETreeItem();
+}
+
+// build attribute list in tree form from element attributes
+function BuildJSEAttributeTable() {
+ var nodeMap = gElement.attributes;
+ if (nodeMap.length > 0) {
+ var added = false;
+ for (var i = 0; i < nodeMap.length; i++) {
+ let name = nodeMap[i].nodeName.toLowerCase();
+ if (CheckAttributeNameSimilarity(nodeMap[i].nodeName, JSEAttrs)) {
+ // Repeated or non-JS handler, ignore this one and go to next.
+ continue;
+ }
+ if (!name.startsWith("on")) {
+ // Attribute isn't an event handler.
+ continue;
+ }
+ var value = gElement.getAttribute(nodeMap[i].nodeName);
+ if (AddTreeItem(name, value, "JSEAList", JSEAttrs)) {
+ // add item to tree
+ added = true;
+ }
+ }
+
+ // Select first item
+ if (added) {
+ gDialog.AddJSEAttributeTree.selectedIndex = 0;
+ }
+ }
+}
+
+function onSelectJSEAttribute() {
+ if (!gDoOnSelectTree) {
+ return;
+ }
+
+ gDialog.AddJSEAttributeValueInput.value = GetAndSelectExistingAttributeValue(
+ gDialog.AddJSEAttributeNameList.label,
+ "JSEAList"
+ );
+}
+
+function onSelectJSETreeItem() {
+ var tree = gDialog.AddJSEAttributeTree;
+ if (tree && tree.view.selection.count) {
+ // Select attribute name in list
+ gDialog.AddJSEAttributeNameList.value = GetTreeItemAttributeStr(
+ getSelectedItem(tree)
+ );
+
+ // Set value input to that in tree (no need to update this in the tree)
+ gUpdateTreeValue = false;
+ gDialog.AddJSEAttributeValueInput.value = GetTreeItemValueStr(
+ getSelectedItem(tree)
+ );
+ gUpdateTreeValue = true;
+ }
+}
+
+function onInputJSEAttributeValue() {
+ if (gUpdateTreeValue) {
+ var name = TrimString(gDialog.AddJSEAttributeNameList.label);
+ var value = TrimString(gDialog.AddJSEAttributeValueInput.value);
+
+ // Update value in the tree list
+ // Since we have a non-editable menulist,
+ // we MUST automatically add the event attribute if it doesn't exist
+ if (!UpdateExistingAttribute(name, value, "JSEAList") && value) {
+ AddTreeItem(name, value, "JSEAList", JSEAttrs);
+ }
+ }
+}
+
+function editJSEAttributeValue(targetCell) {
+ if (IsNotTreeHeader(targetCell)) {
+ gDialog.AddJSEAttributeValueInput.inputField.select();
+ }
+}
+
+function UpdateJSEAttributes() {
+ var JSEAList = document.getElementById("JSEAList");
+ var i;
+
+ // remove removed attributes
+ for (i = 0; i < JSERAttrs.length; i++) {
+ var name = JSERAttrs[i];
+
+ if (gElement.hasAttribute(name)) {
+ doRemoveAttribute(name);
+ }
+ }
+
+ // Add events
+ for (i = 0; i < JSEAList.childNodes.length; i++) {
+ var item = JSEAList.childNodes[i];
+
+ // set the event handler
+ doSetAttribute(GetTreeItemAttributeStr(item), GetTreeItemValueStr(item));
+ }
+}
+
+function RemoveJSEAttribute() {
+ // This differs from HTML and CSS panels:
+ // We reselect after removing, because there is not
+ // editable attribute name input, so we can't clear that
+ // like we do in other panels
+ var newIndex = gDialog.AddJSEAttributeTree.selectedIndex;
+
+ // We only allow 1 selected item
+ if (gDialog.AddJSEAttributeTree.view.selection.count) {
+ var item = getSelectedItem(gDialog.AddJSEAttributeTree);
+
+ // Name is the text of the treecell
+ var attr = GetTreeItemAttributeStr(item);
+
+ // remove the item from the attribute array
+ if (newIndex >= JSEAttrs.length - 1) {
+ newIndex--;
+ }
+
+ // remove the item from the attribute array
+ JSERAttrs[JSERAttrs.length] = attr;
+ RemoveNameFromAttArray(attr, JSEAttrs);
+
+ // Remove the item from the tree
+ item.remove();
+
+ // Reselect an item
+ gDialog.AddJSEAttributeTree.selectedIndex = newIndex;
+ }
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdAdvancedEdit.js b/comm/suite/editor/components/dialogs/content/EdAdvancedEdit.js
new file mode 100644
index 0000000000..60e9009905
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdAdvancedEdit.js
@@ -0,0 +1,342 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdAEAttributes.js */
+/* import-globals-from EdAECSSAttributes.js */
+/* import-globals-from EdAEHTMLAttributes.js */
+/* import-globals-from EdAEJSEAttributes.js */
+/* import-globals-from EdDialogCommon.js */
+
+/** ************ GLOBALS **************/
+var gElement = null; // handle to actual element edited
+
+var HTMLAttrs = []; // html attributes
+var CSSAttrs = []; // css attributes
+var JSEAttrs = []; // js events
+
+var HTMLRAttrs = []; // removed html attributes
+var JSERAttrs = []; // removed js events
+
+/* Set false to allow changing selection in tree
+ without doing "onselect" handler actions
+*/
+var gDoOnSelectTree = true;
+var gUpdateTreeValue = true;
+
+/** ************ INITIALISATION && SETUP **************/
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+/**
+ * function : void Startup();
+ * parameters : none
+ * returns : none
+ * desc. : startup and initialisation, prepares dialog.
+ **/
+function Startup() {
+ var editor = GetCurrentEditor();
+
+ // Element to edit is passed in
+ if (!editor || !window.arguments[1]) {
+ dump("Advanced Edit: No editor or element to edit not supplied\n");
+ window.close();
+ return;
+ }
+ // This is the return value for the parent,
+ // who only needs to know if OK was clicked
+ window.opener.AdvancedEditOK = false;
+
+ // The actual element edited (not a copy!)
+ gElement = window.arguments[1];
+
+ // place the tag name in the header
+ var tagLabel = document.getElementById("tagLabel");
+ tagLabel.setAttribute("value", "<" + gElement.localName + ">");
+
+ // Create dialog object to store controls for easy access
+ gDialog.AddHTMLAttributeNameInput = document.getElementById(
+ "AddHTMLAttributeNameInput"
+ );
+
+ // We use a to switch between editable menulist and textbox
+ gDialog.AddHTMLAttributeValueDeck = document.getElementById(
+ "AddHTMLAttributeValueDeck"
+ );
+ gDialog.AddHTMLAttributeValueMenulist = document.getElementById(
+ "AddHTMLAttributeValueMenulist"
+ );
+ gDialog.AddHTMLAttributeValueTextbox = document.getElementById(
+ "AddHTMLAttributeValueTextbox"
+ );
+ gDialog.AddHTMLAttributeValueInput = gDialog.AddHTMLAttributeValueTextbox;
+
+ gDialog.AddHTMLAttributeTree = document.getElementById("HTMLATree");
+ gDialog.AddCSSAttributeNameInput = document.getElementById(
+ "AddCSSAttributeNameInput"
+ );
+ gDialog.AddCSSAttributeValueInput = document.getElementById(
+ "AddCSSAttributeValueInput"
+ );
+ gDialog.AddCSSAttributeTree = document.getElementById("CSSATree");
+ gDialog.AddJSEAttributeNameList = document.getElementById(
+ "AddJSEAttributeNameList"
+ );
+ gDialog.AddJSEAttributeValueInput = document.getElementById(
+ "AddJSEAttributeValueInput"
+ );
+ gDialog.AddJSEAttributeTree = document.getElementById("JSEATree");
+ gDialog.okButton = document.documentElement.getButton("accept");
+
+ // build the attribute trees
+ BuildHTMLAttributeTable();
+ BuildCSSAttributeTable();
+ BuildJSEAttributeTable();
+
+ // Build attribute name arrays for menulists
+ BuildJSEAttributeNameList();
+ BuildHTMLAttributeNameList();
+ // No menulists for CSS panel (yet)
+
+ // Set focus to Name editable menulist in HTML panel
+ SetTextboxFocus(gDialog.AddHTMLAttributeNameInput);
+
+ // size the dialog properly
+ window.sizeToContent();
+
+ SetWindowLocation();
+}
+
+/**
+ * function : bool onAccept ( void );
+ * parameters : none
+ * returns : boolean true to close the window
+ * desc. : event handler for ok button
+ **/
+function onAccept() {
+ var editor = GetCurrentEditor();
+ editor.beginTransaction();
+ try {
+ // Update our gElement attributes
+ UpdateHTMLAttributes();
+ UpdateCSSAttributes();
+ UpdateJSEAttributes();
+ } catch (ex) {
+ dump(ex);
+ }
+ editor.endTransaction();
+
+ window.opener.AdvancedEditOK = true;
+ SaveWindowLocation();
+}
+
+// Helpers for removing and setting attributes
+// Use editor transactions if modifying the element already in the document
+// (Temporary element from a property dialog won't have a parent node)
+function doRemoveAttribute(attrib) {
+ try {
+ var editor = GetCurrentEditor();
+ if (gElement.parentNode) {
+ editor.removeAttribute(gElement, attrib);
+ } else {
+ gElement.removeAttribute(attrib);
+ }
+ } catch (ex) {}
+}
+
+function doSetAttribute(attrib, value) {
+ try {
+ var editor = GetCurrentEditor();
+ if (gElement.parentNode) {
+ editor.setAttribute(gElement, attrib, value);
+ } else {
+ gElement.setAttribute(attrib, value);
+ }
+ } catch (ex) {}
+}
+
+/**
+ * function : bool CheckAttributeNameSimilarity ( string attName, array attArray );
+ * parameters : attribute to look for, array of current attributes
+ * returns : true if attribute already exists, false if it does not
+ * desc. : checks to see if any other attributes by the same name as the arg supplied
+ * already exist.
+ **/
+function CheckAttributeNameSimilarity(attName, attArray) {
+ for (var i = 0; i < attArray.length; i++) {
+ if (attName.toLowerCase() == attArray[i].toLowerCase()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * function : bool UpdateExistingAttribute ( string attName, string attValue, string treeChildrenId );
+ * parameters : attribute to look for, new value, ID of node in XUL tree
+ * returns : true if attribute already exists in tree, false if it does not
+ * desc. : checks to see if any other attributes by the same name as the arg supplied
+ * already exist while setting the associated value if different from current value
+ **/
+function UpdateExistingAttribute(attName, attValue, treeChildrenId) {
+ var treeChildren = document.getElementById(treeChildrenId);
+ if (!treeChildren) {
+ return false;
+ }
+
+ var name;
+ var i;
+ attName = TrimString(attName).toLowerCase();
+ attValue = TrimString(attValue);
+
+ for (i = 0; i < treeChildren.childNodes.length; i++) {
+ var item = treeChildren.childNodes[i];
+ name = GetTreeItemAttributeStr(item);
+ if (name.toLowerCase() == attName) {
+ // Set the text in the "value' column treecell
+ SetTreeItemValueStr(item, attValue);
+
+ // Select item just changed,
+ // but don't trigger the tree's onSelect handler
+ gDoOnSelectTree = false;
+ try {
+ selectTreeItem(treeChildren, item);
+ } catch (e) {}
+ gDoOnSelectTree = true;
+
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * function : string GetAndSelectExistingAttributeValue ( string attName, string treeChildrenId );
+ * parameters : attribute to look for, ID of node in XUL tree
+ * returns : value in from the tree or empty string if name not found
+ **/
+function GetAndSelectExistingAttributeValue(attName, treeChildrenId) {
+ if (!attName) {
+ return "";
+ }
+
+ var treeChildren = document.getElementById(treeChildrenId);
+ var name;
+ var i;
+
+ for (i = 0; i < treeChildren.childNodes.length; i++) {
+ var item = treeChildren.childNodes[i];
+ name = GetTreeItemAttributeStr(item);
+ if (name.toLowerCase() == attName.toLowerCase()) {
+ // Select item in the tree
+ // but don't trigger the tree's onSelect handler
+ gDoOnSelectTree = false;
+ try {
+ selectTreeItem(treeChildren, item);
+ } catch (e) {}
+ gDoOnSelectTree = true;
+
+ // Get the text in the "value' column treecell
+ return GetTreeItemValueStr(item);
+ }
+ }
+
+ // Attribute doesn't exist in tree, so remove selection
+ gDoOnSelectTree = false;
+ try {
+ treeChildren.parentNode.view.selection.clearSelection();
+ } catch (e) {}
+ gDoOnSelectTree = true;
+
+ return "";
+}
+
+/* Tree structure:
+
+
+ // Name Cell
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/comm/suite/editor/components/dialogs/content/EdButtonProps.js b/comm/suite/editor/components/dialogs/content/EdButtonProps.js
new file mode 100644
index 0000000000..1cd0ee7365
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdButtonProps.js
@@ -0,0 +1,146 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var insertNew;
+var buttonElement;
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ window.close();
+ return;
+ }
+
+ gDialog = {
+ buttonType: document.getElementById("ButtonType"),
+ buttonName: document.getElementById("ButtonName"),
+ buttonValue: document.getElementById("ButtonValue"),
+ buttonDisabled: document.getElementById("ButtonDisabled"),
+ buttonTabIndex: document.getElementById("ButtonTabIndex"),
+ buttonAccessKey: document.getElementById("ButtonAccessKey"),
+ MoreSection: document.getElementById("MoreSection"),
+ MoreFewerButton: document.getElementById("MoreFewerButton"),
+ RemoveButton: document.getElementById("RemoveButton"),
+ };
+
+ // Get a single selected button element
+ const kTagName = "button";
+ try {
+ buttonElement = editor.getSelectedElement(kTagName);
+ } catch (e) {}
+
+ if (buttonElement) {
+ // We found an element and don't need to insert one
+ insertNew = false;
+ } else {
+ insertNew = true;
+
+ // We don't have an element selected,
+ // so create one with default attributes
+ try {
+ buttonElement = editor.createElementWithDefaults(kTagName);
+ } catch (e) {}
+
+ if (!buttonElement) {
+ dump("Failed to get selected element or create a new one!\n");
+ window.close();
+ return;
+ }
+ // Hide button removing existing button
+ gDialog.RemoveButton.hidden = true;
+ }
+
+ // Make a copy to use for AdvancedEdit
+ globalElement = buttonElement.cloneNode(false);
+
+ InitDialog();
+
+ InitMoreFewer();
+
+ gDialog.buttonType.focus();
+
+ SetWindowLocation();
+}
+
+function InitDialog() {
+ var type = globalElement.getAttribute("type");
+ var index = 0;
+ switch (type) {
+ case "button":
+ index = 2;
+ break;
+ case "reset":
+ index = 1;
+ break;
+ }
+ gDialog.buttonType.selectedIndex = index;
+ gDialog.buttonName.value = globalElement.getAttribute("name");
+ gDialog.buttonValue.value = globalElement.getAttribute("value");
+ gDialog.buttonDisabled.setAttribute(
+ "checked",
+ globalElement.hasAttribute("disabled")
+ );
+ gDialog.buttonTabIndex.value = globalElement.getAttribute("tabindex");
+ gDialog.buttonAccessKey.value = globalElement.getAttribute("accesskey");
+}
+
+function RemoveButton() {
+ RemoveContainer(buttonElement);
+ SaveWindowLocation();
+ window.close();
+}
+
+function ValidateData() {
+ var attributes = {
+ type: ["", "reset", "button"][gDialog.buttonType.selectedIndex],
+ name: gDialog.buttonName.value,
+ value: gDialog.buttonValue.value,
+ tabindex: gDialog.buttonTabIndex.value,
+ accesskey: gDialog.buttonAccessKey.value,
+ };
+ for (var a in attributes) {
+ if (attributes[a]) {
+ globalElement.setAttribute(a, attributes[a]);
+ } else {
+ globalElement.removeAttribute(a);
+ }
+ }
+ if (gDialog.buttonDisabled.checked) {
+ globalElement.setAttribute("disabled", "");
+ } else {
+ globalElement.removeAttribute("disabled");
+ }
+ return true;
+}
+
+function onAccept() {
+ // All values are valid - copy to actual element in doc or
+ // element created to insert
+ ValidateData();
+
+ var editor = GetCurrentEditor();
+
+ editor.cloneAttributes(buttonElement, globalElement);
+
+ if (insertNew) {
+ if (!InsertElementAroundSelection(buttonElement)) {
+ /* eslint-disable-next-line no-unsanitized/property */
+ buttonElement.innerHTML = editor.outputToString(
+ "text/html",
+ kOutputSelectionOnly
+ );
+ editor.insertElementAtSelection(buttonElement, true);
+ }
+ }
+
+ SaveWindowLocation();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdButtonProps.xhtml b/comm/suite/editor/components/dialogs/content/EdButtonProps.xhtml
new file mode 100644
index 0000000000..70e4774f13
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdButtonProps.xhtml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+%edButtonProperties;
+
+%edDialogOverlay;
+]>
+
+
diff --git a/comm/suite/editor/components/dialogs/content/EdColorPicker.js b/comm/suite/editor/components/dialogs/content/EdColorPicker.js
new file mode 100644
index 0000000000..95ce279368
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdColorPicker.js
@@ -0,0 +1,297 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+// Cancel() is in EdDialogCommon.js
+
+var insertNew = true;
+var tagname = "TAG NAME";
+var gColor = "";
+var LastPickedColor = "";
+var ColorType = "Text";
+var TextType = false;
+var HighlightType = false;
+var TableOrCell = false;
+var LastPickedIsDefault = true;
+var NoDefault = false;
+var gColorObj;
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancelColor);
+
+function Startup() {
+ if (!window.arguments[1]) {
+ dump("EdColorPicker: Missing color object param\n");
+ return;
+ }
+
+ // window.arguments[1] is object to get initial values and return color data
+ gColorObj = window.arguments[1];
+ gColorObj.Cancel = false;
+
+ gDialog.ColorPicker = document.getElementById("ColorPicker");
+ gDialog.ColorInput = document.getElementById("ColorInput");
+ gDialog.LastPickedButton = document.getElementById("LastPickedButton");
+ gDialog.LastPickedColor = document.getElementById("LastPickedColor");
+ gDialog.CellOrTableGroup = document.getElementById("CellOrTableGroup");
+ gDialog.TableRadio = document.getElementById("TableRadio");
+ gDialog.CellRadio = document.getElementById("CellRadio");
+ gDialog.ColorSwatch = document.getElementById("ColorPickerSwatch");
+ gDialog.Ok = document.documentElement.getButton("accept");
+
+ // The type of color we are setting:
+ // text: Text, Link, ActiveLink, VisitedLink,
+ // or background: Page, Table, or Cell
+ if (gColorObj.Type) {
+ ColorType = gColorObj.Type;
+ // Get string for dialog title from passed-in type
+ // (note constraint on editor.properties string name)
+ let IsCSSPrefChecked = Services.prefs.getBoolPref("editor.use_css");
+
+ if (GetCurrentEditor()) {
+ if (ColorType == "Page" && IsCSSPrefChecked && IsHTMLEditor()) {
+ document.title = GetString("BlockColor");
+ } else {
+ document.title = GetString(ColorType + "Color");
+ }
+ }
+ }
+
+ gDialog.ColorInput.value = "";
+ var tmpColor;
+ var haveTableRadio = false;
+
+ switch (ColorType) {
+ case "Page":
+ tmpColor = gColorObj.PageColor;
+ if (tmpColor && tmpColor.toLowerCase() != "window") {
+ gColor = tmpColor;
+ }
+ break;
+ case "Table":
+ if (gColorObj.TableColor) {
+ gColor = gColorObj.TableColor;
+ }
+ break;
+ case "Cell":
+ if (gColorObj.CellColor) {
+ gColor = gColorObj.CellColor;
+ }
+ break;
+ case "TableOrCell":
+ TableOrCell = true;
+ document.getElementById("TableOrCellGroup").collapsed = false;
+ haveTableRadio = true;
+ if (gColorObj.SelectedType == "Cell") {
+ gColor = gColorObj.CellColor;
+ gDialog.CellOrTableGroup.selectedItem = gDialog.CellRadio;
+ gDialog.CellRadio.focus();
+ } else {
+ gColor = gColorObj.TableColor;
+ gDialog.CellOrTableGroup.selectedItem = gDialog.TableRadio;
+ gDialog.TableRadio.focus();
+ }
+ break;
+ case "Highlight":
+ HighlightType = true;
+ if (gColorObj.HighlightColor) {
+ gColor = gColorObj.HighlightColor;
+ }
+ break;
+ default:
+ // Any other type will change some kind of text,
+ TextType = true;
+ tmpColor = gColorObj.TextColor;
+ if (tmpColor && tmpColor.toLowerCase() != "windowtext") {
+ gColor = gColorObj.TextColor;
+ }
+ break;
+ }
+
+ // Set initial color in input field and in the colorpicker
+ SetCurrentColor(gColor);
+ gDialog.ColorPicker.value = gColor;
+
+ // Use last-picked colors passed in, or those persistent on dialog
+ if (TextType) {
+ if (!("LastTextColor" in gColorObj) || !gColorObj.LastTextColor) {
+ gColorObj.LastTextColor = gDialog.LastPickedColor.getAttribute(
+ "LastTextColor"
+ );
+ }
+ LastPickedColor = gColorObj.LastTextColor;
+ } else if (HighlightType) {
+ if (!("LastHighlightColor" in gColorObj) || !gColorObj.LastHighlightColor) {
+ gColorObj.LastHighlightColor = gDialog.LastPickedColor.getAttribute(
+ "LastHighlightColor"
+ );
+ }
+ LastPickedColor = gColorObj.LastHighlightColor;
+ } else {
+ if (
+ !("LastBackgroundColor" in gColorObj) ||
+ !gColorObj.LastBackgroundColor
+ ) {
+ gColorObj.LastBackgroundColor = gDialog.LastPickedColor.getAttribute(
+ "LastBackgroundColor"
+ );
+ }
+ LastPickedColor = gColorObj.LastBackgroundColor;
+ }
+
+ // Set method to detect clicking on OK button
+ // so we don't get fooled by changing "default" behavior
+ gDialog.Ok.setAttribute("onclick", "SetDefaultToOk()");
+
+ if (!LastPickedColor) {
+ // Hide the button, as there is no last color available.
+ gDialog.LastPickedButton.hidden = true;
+ } else {
+ gDialog.LastPickedColor.setAttribute(
+ "style",
+ "background-color: " + LastPickedColor
+ );
+
+ // Make "Last-picked" the default button, until the user selects a color.
+ gDialog.Ok.removeAttribute("default");
+ gDialog.LastPickedButton.setAttribute("default", "true");
+ }
+
+ // Caller can prevent user from submitting an empty, i.e., default color
+ NoDefault = gColorObj.NoDefault;
+ if (NoDefault) {
+ // Hide the "Default button -- user must pick a color
+ document.getElementById("DefaultColorButton").collapsed = true;
+ }
+
+ // Set focus to colorpicker if not set to table radio buttons above
+ if (!haveTableRadio) {
+ gDialog.ColorPicker.focus();
+ }
+
+ SetWindowLocation();
+}
+
+function SelectColor() {
+ var color = gDialog.ColorPicker.value;
+ if (color) {
+ SetCurrentColor(color);
+ }
+}
+
+function RemoveColor() {
+ SetCurrentColor("");
+ gDialog.ColorInput.focus();
+ SetDefaultToOk();
+}
+
+function SelectColorByKeypress(aEvent) {
+ if (aEvent.charCode == aEvent.DOM_VK_SPACE) {
+ SelectColor();
+ SetDefaultToOk();
+ }
+}
+
+function SelectLastPickedColor() {
+ SetCurrentColor(LastPickedColor);
+ if (onAccept()) {
+ // window.close();
+ return true;
+ }
+
+ return false;
+}
+
+function SetCurrentColor(color) {
+ // TODO: Validate color?
+ if (!color) {
+ color = "";
+ }
+ gColor = TrimString(color).toLowerCase();
+ if (gColor == "mixed") {
+ gColor = "";
+ }
+ gDialog.ColorInput.value = gColor;
+ SetColorSwatch();
+}
+
+function SetColorSwatch() {
+ // TODO: DON'T ALLOW SPACES?
+ var color = TrimString(gDialog.ColorInput.value);
+ if (color) {
+ gDialog.ColorSwatch.setAttribute("style", "background-color:" + color);
+ gDialog.ColorSwatch.removeAttribute("default");
+ } else {
+ gDialog.ColorSwatch.setAttribute("style", "background-color:inherit");
+ gDialog.ColorSwatch.setAttribute("default", "true");
+ }
+}
+
+function SetDefaultToOk() {
+ gDialog.LastPickedButton.removeAttribute("default");
+ gDialog.Ok.setAttribute("default", "true");
+ LastPickedIsDefault = false;
+}
+
+function ValidateData() {
+ if (LastPickedIsDefault) {
+ gColor = LastPickedColor;
+ } else {
+ gColor = gDialog.ColorInput.value;
+ }
+
+ gColor = TrimString(gColor).toLowerCase();
+
+ // TODO: Validate the color string!
+
+ if (NoDefault && !gColor) {
+ ShowInputErrorMessage(GetString("NoColorError"));
+ SetTextboxFocus(gDialog.ColorInput);
+ return false;
+ }
+ return true;
+}
+
+function onAccept(event) {
+ if (!ValidateData()) {
+ event.preventDefault();
+ return;
+ }
+
+ // Set return values and save in persistent color attributes
+ if (TextType) {
+ gColorObj.TextColor = gColor;
+ if (gColor.length > 0) {
+ gDialog.LastPickedColor.setAttribute("LastTextColor", gColor);
+ gColorObj.LastTextColor = gColor;
+ }
+ } else if (HighlightType) {
+ gColorObj.HighlightColor = gColor;
+ if (gColor.length > 0) {
+ gDialog.LastPickedColor.setAttribute("LastHighlightColor", gColor);
+ gColorObj.LastHighlightColor = gColor;
+ }
+ } else {
+ gColorObj.BackgroundColor = gColor;
+ if (gColor.length > 0) {
+ gDialog.LastPickedColor.setAttribute("LastBackgroundColor", gColor);
+ gColorObj.LastBackgroundColor = gColor;
+ }
+ // If table or cell requested, tell caller which element to set on
+ if (TableOrCell && gDialog.TableRadio.selected) {
+ gColorObj.Type = "Table";
+ }
+ }
+ SaveWindowLocation();
+}
+
+function onCancelColor() {
+ // Tells caller that user canceled
+ gColorObj.Cancel = true;
+ SaveWindowLocation();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdColorPicker.xhtml b/comm/suite/editor/components/dialogs/content/EdColorPicker.xhtml
new file mode 100644
index 0000000000..c18bc90e62
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdColorPicker.xhtml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
diff --git a/comm/suite/editor/components/dialogs/content/EdColorProps.js b/comm/suite/editor/components/dialogs/content/EdColorProps.js
new file mode 100644
index 0000000000..62d3f29c9a
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdColorProps.js
@@ -0,0 +1,476 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ Behavior notes:
+ Radio buttons select "UseDefaultColors" vs. "UseCustomColors" modes.
+ If any color attribute is set in the body, mode is "Custom Colors",
+ even if 1 or more (but not all) are actually null (= "use default")
+ When in "Custom Colors" mode, all colors will be set on body tag,
+ even if they are just default colors, to assure compatible colors in page.
+ User cannot select "use default" for individual colors
+*/
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+// Cancel() is in EdDialogCommon.js
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+var gBodyElement;
+var prefs;
+var gBackgroundImage;
+
+// Initialize in case we can't get them from prefs???
+var defaultTextColor = "#000000";
+var defaultLinkColor = "#000099";
+var defaultActiveColor = "#000099";
+var defaultVisitedColor = "#990099";
+var defaultBackgroundColor = "#FFFFFF";
+const styleStr = "style";
+const textStr = "text";
+const linkStr = "link";
+const vlinkStr = "vlink";
+const alinkStr = "alink";
+const bgcolorStr = "bgcolor";
+const backgroundStr = "background";
+const cssColorStr = "color";
+const cssBackgroundColorStr = "background-color";
+const cssBackgroundImageStr = "background-image";
+const colorStyle = cssColorStr + ": ";
+const backColorStyle = cssBackgroundColorStr + ": ";
+const backImageStyle = "; " + cssBackgroundImageStr + ": url(";
+
+var customTextColor;
+var customLinkColor;
+var customActiveColor;
+var customVisitedColor;
+var customBackgroundColor;
+var previewBGColor;
+
+// dialog initialization code
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ window.close();
+ return;
+ }
+
+ gDialog.ColorPreview = document.getElementById("ColorPreview");
+ gDialog.NormalText = document.getElementById("NormalText");
+ gDialog.LinkText = document.getElementById("LinkText");
+ gDialog.ActiveLinkText = document.getElementById("ActiveLinkText");
+ gDialog.VisitedLinkText = document.getElementById("VisitedLinkText");
+ gDialog.PageColorGroup = document.getElementById("PageColorGroup");
+ gDialog.DefaultColorsRadio = document.getElementById("DefaultColorsRadio");
+ gDialog.CustomColorsRadio = document.getElementById("CustomColorsRadio");
+ gDialog.BackgroundImageInput = document.getElementById(
+ "BackgroundImageInput"
+ );
+
+ try {
+ gBodyElement = editor.rootElement;
+ } catch (e) {}
+
+ if (!gBodyElement) {
+ dump("Failed to get BODY element!\n");
+ window.close();
+ }
+
+ // Set element we will edit
+ globalElement = gBodyElement.cloneNode(false);
+
+ // Initialize default colors from browser prefs
+ var browserColors = GetDefaultBrowserColors();
+ if (browserColors) {
+ // Use author's browser pref colors passed into dialog
+ defaultTextColor = browserColors.TextColor;
+ defaultLinkColor = browserColors.LinkColor;
+ defaultActiveColor = browserColors.ActiveLinkColor;
+ defaultVisitedColor = browserColors.VisitedLinkColor;
+ defaultBackgroundColor = browserColors.BackgroundColor;
+ }
+
+ // We only need to test for this once per dialog load
+ gHaveDocumentUrl = GetDocumentBaseUrl();
+
+ InitDialog();
+
+ gDialog.PageColorGroup.focus();
+
+ SetWindowLocation();
+}
+
+function InitDialog() {
+ // Get image from document
+ gBackgroundImage = GetHTMLOrCSSStyleValue(
+ globalElement,
+ backgroundStr,
+ cssBackgroundImageStr
+ );
+ if (/url\((.*)\)/.test(gBackgroundImage)) {
+ gBackgroundImage = RegExp.$1;
+ }
+
+ if (gBackgroundImage) {
+ // Shorten data URIs for display.
+ shortenImageData(gBackgroundImage, gDialog.BackgroundImageInput);
+ gDialog.ColorPreview.setAttribute(
+ styleStr,
+ backImageStyle + gBackgroundImage + ");"
+ );
+ }
+
+ SetRelativeCheckbox();
+
+ customTextColor = GetHTMLOrCSSStyleValue(globalElement, textStr, cssColorStr);
+ customTextColor = ConvertRGBColorIntoHEXColor(customTextColor);
+ customLinkColor = globalElement.getAttribute(linkStr);
+ customActiveColor = globalElement.getAttribute(alinkStr);
+ customVisitedColor = globalElement.getAttribute(vlinkStr);
+ customBackgroundColor = GetHTMLOrCSSStyleValue(
+ globalElement,
+ bgcolorStr,
+ cssBackgroundColorStr
+ );
+ customBackgroundColor = ConvertRGBColorIntoHEXColor(customBackgroundColor);
+
+ var haveCustomColor =
+ customTextColor ||
+ customLinkColor ||
+ customVisitedColor ||
+ customActiveColor ||
+ customBackgroundColor;
+
+ // Set default color explicitly for any that are missing
+ // PROBLEM: We are using "windowtext" and "window" for the Windows OS
+ // default color values. This works with CSS in preview window,
+ // but we should NOT use these as values for HTML attributes!
+
+ if (!customTextColor) {
+ customTextColor = defaultTextColor;
+ }
+ if (!customLinkColor) {
+ customLinkColor = defaultLinkColor;
+ }
+ if (!customActiveColor) {
+ customActiveColor = defaultActiveColor;
+ }
+ if (!customVisitedColor) {
+ customVisitedColor = defaultVisitedColor;
+ }
+ if (!customBackgroundColor) {
+ customBackgroundColor = defaultBackgroundColor;
+ }
+
+ if (haveCustomColor) {
+ // If any colors are set, then check the "Custom" radio button
+ gDialog.PageColorGroup.selectedItem = gDialog.CustomColorsRadio;
+ UseCustomColors();
+ } else {
+ gDialog.PageColorGroup.selectedItem = gDialog.DefaultColorsRadio;
+ UseDefaultColors();
+ }
+}
+
+function GetColorAndUpdate(ColorWellID) {
+ // Only allow selecting when in custom mode
+ if (!gDialog.CustomColorsRadio.selected) {
+ return;
+ }
+
+ var colorWell = document.getElementById(ColorWellID);
+ if (!colorWell) {
+ return;
+ }
+
+ // Don't allow a blank color, i.e., using the "default"
+ var colorObj = {
+ NoDefault: true,
+ Type: "",
+ TextColor: 0,
+ PageColor: 0,
+ Cancel: false,
+ };
+
+ switch (ColorWellID) {
+ case "textCW":
+ colorObj.Type = "Text";
+ colorObj.TextColor = customTextColor;
+ break;
+ case "linkCW":
+ colorObj.Type = "Link";
+ colorObj.TextColor = customLinkColor;
+ break;
+ case "activeCW":
+ colorObj.Type = "ActiveLink";
+ colorObj.TextColor = customActiveColor;
+ break;
+ case "visitedCW":
+ colorObj.Type = "VisitedLink";
+ colorObj.TextColor = customVisitedColor;
+ break;
+ case "backgroundCW":
+ colorObj.Type = "Page";
+ colorObj.PageColor = customBackgroundColor;
+ break;
+ }
+
+ window.openDialog(
+ "chrome://editor/content/EdColorPicker.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal",
+ "",
+ colorObj
+ );
+
+ // User canceled the dialog
+ if (colorObj.Cancel) {
+ return;
+ }
+
+ var color = "";
+ switch (ColorWellID) {
+ case "textCW":
+ color = customTextColor = colorObj.TextColor;
+ break;
+ case "linkCW":
+ color = customLinkColor = colorObj.TextColor;
+ break;
+ case "activeCW":
+ color = customActiveColor = colorObj.TextColor;
+ break;
+ case "visitedCW":
+ color = customVisitedColor = colorObj.TextColor;
+ break;
+ case "backgroundCW":
+ color = customBackgroundColor = colorObj.BackgroundColor;
+ break;
+ }
+
+ setColorWell(ColorWellID, color);
+ SetColorPreview(ColorWellID, color);
+}
+
+function SetColorPreview(ColorWellID, color) {
+ switch (ColorWellID) {
+ case "textCW":
+ gDialog.NormalText.setAttribute(styleStr, colorStyle + color);
+ break;
+ case "linkCW":
+ gDialog.LinkText.setAttribute(styleStr, colorStyle + color);
+ break;
+ case "activeCW":
+ gDialog.ActiveLinkText.setAttribute(styleStr, colorStyle + color);
+ break;
+ case "visitedCW":
+ gDialog.VisitedLinkText.setAttribute(styleStr, colorStyle + color);
+ break;
+ case "backgroundCW":
+ // Must combine background color and image style values
+ var styleValue = backColorStyle + color;
+ if (gBackgroundImage) {
+ styleValue += ";" + backImageStyle + gBackgroundImage + ");";
+ }
+
+ gDialog.ColorPreview.setAttribute(styleStr, styleValue);
+ previewBGColor = color;
+ break;
+ }
+}
+
+function UseCustomColors() {
+ SetElementEnabledById("TextButton", true);
+ SetElementEnabledById("LinkButton", true);
+ SetElementEnabledById("ActiveLinkButton", true);
+ SetElementEnabledById("VisitedLinkButton", true);
+ SetElementEnabledById("BackgroundButton", true);
+ SetElementEnabledById("Text", true);
+ SetElementEnabledById("Link", true);
+ SetElementEnabledById("Active", true);
+ SetElementEnabledById("Visited", true);
+ SetElementEnabledById("Background", true);
+
+ SetColorPreview("textCW", customTextColor);
+ SetColorPreview("linkCW", customLinkColor);
+ SetColorPreview("activeCW", customActiveColor);
+ SetColorPreview("visitedCW", customVisitedColor);
+ SetColorPreview("backgroundCW", customBackgroundColor);
+
+ setColorWell("textCW", customTextColor);
+ setColorWell("linkCW", customLinkColor);
+ setColorWell("activeCW", customActiveColor);
+ setColorWell("visitedCW", customVisitedColor);
+ setColorWell("backgroundCW", customBackgroundColor);
+}
+
+function UseDefaultColors() {
+ SetColorPreview("textCW", defaultTextColor);
+ SetColorPreview("linkCW", defaultLinkColor);
+ SetColorPreview("activeCW", defaultActiveColor);
+ SetColorPreview("visitedCW", defaultVisitedColor);
+ SetColorPreview("backgroundCW", defaultBackgroundColor);
+
+ // Setting to blank color will remove color from buttons,
+ setColorWell("textCW", "");
+ setColorWell("linkCW", "");
+ setColorWell("activeCW", "");
+ setColorWell("visitedCW", "");
+ setColorWell("backgroundCW", "");
+
+ // Disable color buttons and labels
+ SetElementEnabledById("TextButton", false);
+ SetElementEnabledById("LinkButton", false);
+ SetElementEnabledById("ActiveLinkButton", false);
+ SetElementEnabledById("VisitedLinkButton", false);
+ SetElementEnabledById("BackgroundButton", false);
+ SetElementEnabledById("Text", false);
+ SetElementEnabledById("Link", false);
+ SetElementEnabledById("Active", false);
+ SetElementEnabledById("Visited", false);
+ SetElementEnabledById("Background", false);
+}
+
+function chooseFile() {
+ // Get a local image file, converted into URL format
+ GetLocalFileURL("img").then(fileURL => {
+ // Always try to relativize local file URLs
+ if (gHaveDocumentUrl) {
+ fileURL = MakeRelativeUrl(fileURL);
+ }
+
+ gDialog.BackgroundImageInput.value = fileURL;
+
+ SetRelativeCheckbox();
+ ValidateAndPreviewImage(true);
+ SetTextboxFocus(gDialog.BackgroundImageInput);
+ });
+}
+
+function ChangeBackgroundImage() {
+ // Don't show error message for image while user is typing
+ ValidateAndPreviewImage(false);
+ SetRelativeCheckbox();
+}
+
+function ValidateAndPreviewImage(ShowErrorMessage) {
+ // First make a string with just background color
+ var styleValue = backColorStyle + previewBGColor + ";";
+
+ var retVal = true;
+ var image = TrimString(gDialog.BackgroundImageInput.value);
+ if (image) {
+ if (isImageDataShortened(image)) {
+ gBackgroundImage = restoredImageData(gDialog.BackgroundImageInput);
+ } else {
+ gBackgroundImage = image;
+
+ // Display must use absolute URL if possible
+ var displayImage = gHaveDocumentUrl ? MakeAbsoluteUrl(image) : image;
+ styleValue += backImageStyle + displayImage + ");";
+ }
+ } else {
+ gBackgroundImage = null;
+ }
+
+ // Set style on preview (removes image if not valid)
+ gDialog.ColorPreview.setAttribute(styleStr, styleValue);
+
+ // Note that an "empty" string is valid
+ return retVal;
+}
+
+function ValidateData() {
+ var editor = GetCurrentEditor();
+ try {
+ // Colors values are updated as they are picked, no validation necessary
+ if (gDialog.DefaultColorsRadio.selected) {
+ editor.removeAttributeOrEquivalent(globalElement, textStr, true);
+ globalElement.removeAttribute(linkStr);
+ globalElement.removeAttribute(vlinkStr);
+ globalElement.removeAttribute(alinkStr);
+ editor.removeAttributeOrEquivalent(globalElement, bgcolorStr, true);
+ } else {
+ // Do NOT accept the CSS "WindowsOS" color strings!
+ // Problem: We really should try to get the actual color values
+ // from windows, but I don't know how to do that!
+ var tmpColor = customTextColor.toLowerCase();
+ if (tmpColor != "windowtext") {
+ editor.setAttributeOrEquivalent(
+ globalElement,
+ textStr,
+ customTextColor,
+ true
+ );
+ } else {
+ editor.removeAttributeOrEquivalent(globalElement, textStr, true);
+ }
+
+ tmpColor = customBackgroundColor.toLowerCase();
+ if (tmpColor != "window") {
+ editor.setAttributeOrEquivalent(
+ globalElement,
+ bgcolorStr,
+ customBackgroundColor,
+ true
+ );
+ } else {
+ editor.removeAttributeOrEquivalent(globalElement, bgcolorStr, true);
+ }
+
+ globalElement.setAttribute(linkStr, customLinkColor);
+ globalElement.setAttribute(vlinkStr, customVisitedColor);
+ globalElement.setAttribute(alinkStr, customActiveColor);
+ }
+
+ if (ValidateAndPreviewImage(true)) {
+ // A valid image may be null for no image
+ if (gBackgroundImage) {
+ globalElement.setAttribute(backgroundStr, gBackgroundImage);
+ } else {
+ editor.removeAttributeOrEquivalent(globalElement, backgroundStr, true);
+ }
+
+ return true;
+ }
+ } catch (e) {}
+ return false;
+}
+
+function onAccept(event) {
+ // If it's a file, convert to a data URL.
+ if (gBackgroundImage && /^file:/i.test(gBackgroundImage)) {
+ let nsFile = Services.io
+ .newURI(gBackgroundImage)
+ .QueryInterface(Ci.nsIFileURL).file;
+ if (nsFile.exists()) {
+ let reader = new FileReader();
+ reader.addEventListener("load", function() {
+ gBackgroundImage = reader.result;
+ gDialog.BackgroundImageInput.value = reader.result;
+ if (onAccept(event)) {
+ window.close();
+ }
+ });
+ File.createFromNsIFile(nsFile).then(file => {
+ reader.readAsDataURL(file);
+ });
+ event.preventDefault(); // Don't close just yet...
+ return false;
+ }
+ }
+ if (ValidateData()) {
+ // Copy attributes to element we are changing
+ try {
+ GetCurrentEditor().cloneAttributes(gBodyElement, globalElement);
+ } catch (e) {}
+
+ SaveWindowLocation();
+ return true; // do close the window
+ }
+ event.preventDefault();
+ return false;
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdColorProps.xhtml b/comm/suite/editor/components/dialogs/content/EdColorProps.xhtml
new file mode 100644
index 0000000000..85393ed209
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdColorProps.xhtml
@@ -0,0 +1,134 @@
+
+
+
+
+
+
+
+%edColorPropertiesDTD;
+
+%composeEditorOverlayDTD;
+
+%edDialogOverlay;
+]>
+
+
diff --git a/comm/suite/editor/components/dialogs/content/EdConvertToTable.js b/comm/suite/editor/components/dialogs/content/EdConvertToTable.js
new file mode 100644
index 0000000000..a149e708f8
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdConvertToTable.js
@@ -0,0 +1,326 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+var gIndex;
+var gCommaIndex = "0";
+var gSpaceIndex = "1";
+var gOtherIndex = "2";
+
+// dialog initialization code
+function Startup() {
+ if (!GetCurrentEditor()) {
+ window.close();
+ return;
+ }
+
+ gDialog.sepRadioGroup = document.getElementById("SepRadioGroup");
+ gDialog.sepCharacterInput = document.getElementById("SepCharacterInput");
+ gDialog.deleteSepCharacter = document.getElementById("DeleteSepCharacter");
+ gDialog.collapseSpaces = document.getElementById("CollapseSpaces");
+
+ // We persist the user's separator character
+ gDialog.sepCharacterInput.value = gDialog.sepRadioGroup.getAttribute(
+ "character"
+ );
+
+ gIndex = gDialog.sepRadioGroup.getAttribute("index");
+
+ switch (gIndex) {
+ case gCommaIndex:
+ default:
+ gDialog.sepRadioGroup.selectedItem = document.getElementById("comma");
+ break;
+ case gSpaceIndex:
+ gDialog.sepRadioGroup.selectedItem = document.getElementById("space");
+ break;
+ case gOtherIndex:
+ gDialog.sepRadioGroup.selectedItem = document.getElementById("other");
+ break;
+ }
+
+ // Set initial enable state on character input and "collapse" checkbox
+ SelectCharacter(gIndex);
+
+ SetWindowLocation();
+}
+
+function InputSepCharacter() {
+ var str = gDialog.sepCharacterInput.value;
+
+ // Limit input to 1 character
+ if (str.length > 1) {
+ str = str.slice(0, 1);
+ }
+
+ // We can never allow tag or entity delimiters for separator character
+ if (str == "<" || str == ">" || str == "&" || str == ";" || str == " ") {
+ str = "";
+ }
+
+ gDialog.sepCharacterInput.value = str;
+}
+
+function SelectCharacter(radioGroupIndex) {
+ gIndex = radioGroupIndex;
+ SetElementEnabledById("SepCharacterInput", gIndex == gOtherIndex);
+ SetElementEnabledById("CollapseSpaces", gIndex == gSpaceIndex);
+}
+
+/* eslint-disable complexity */
+function onAccept() {
+ var sepCharacter = "";
+ switch (gIndex) {
+ case gCommaIndex:
+ sepCharacter = ",";
+ break;
+ case gSpaceIndex:
+ sepCharacter = " ";
+ break;
+ case gOtherIndex:
+ sepCharacter = gDialog.sepCharacterInput.value.slice(0, 1);
+ break;
+ }
+
+ var editor = GetCurrentEditor();
+ var str;
+ try {
+ str = editor.outputToString(
+ "text/html",
+ kOutputLFLineBreak | kOutputSelectionOnly
+ );
+ } catch (e) {}
+ if (!str) {
+ SaveWindowLocation();
+ return;
+ }
+
+ // Replace nbsp with spaces:
+ str = str.replace(/\u00a0/g, " ");
+
+ // Strip out
completely
+ str = str.replace(/\s*<\/p>\s*/g, "");
+
+ // Trim whitespace adjacent to
and tags
+ // and replace
with
+ // (which will be replaced with below)
+ str = str.replace(/\s*
\s*|\s* \s*/g, " ");
+
+ // Trim leading s
+ str = str.replace(/^( )+/, "");
+
+ // Trim trailing s
+ str = str.replace(/( )+$/, "");
+
+ // Reduce multiple internal to just 1
+ // TODO: Maybe add a checkbox to let user decide
+ // str = str.replace(/( )+/g, " ");
+
+ // Trim leading and trailing spaces
+ str = str.trim();
+
+ // Remove all tag contents so we don't replace
+ // separator character within tags
+ // Also converts lists to something useful
+ var stack = [];
+ var start;
+ var end;
+ var searchStart = 0;
+ var listSeparator = "";
+ var listItemSeparator = "";
+ var endList = false;
+
+ do {
+ start = str.indexOf("<", searchStart);
+
+ if (start >= 0) {
+ end = str.indexOf(">", start + 1);
+ if (end > start) {
+ let tagContent = str.slice(start + 1, end).trim();
+
+ if (/^ol|^ul|^dl/.test(tagContent)) {
+ // Replace list tag with to start new row
+ // at beginning of second or greater list tag
+ str = str.slice(0, start) + listSeparator + str.slice(end + 1);
+ if (listSeparator == "") {
+ listSeparator = " ";
+ }
+
+ // Reset for list item separation into cells
+ listItemSeparator = "";
+ } else if (/^li|^dt|^dd/.test(tagContent)) {
+ // Start a new row if this is first item after the ending the last list
+ if (endList) {
+ listItemSeparator = " ";
+ }
+
+ // Start new cell at beginning of second or greater list items
+ str = str.slice(0, start) + listItemSeparator + str.slice(end + 1);
+
+ if (endList || listItemSeparator == "") {
+ listItemSeparator = sepCharacter;
+ }
+
+ endList = false;
+ } else {
+ // Find end tags
+ endList = /^\/ol|^\/ul|^\/dl/.test(tagContent);
+ if (endList || /^\/li|^\/dt|^\/dd/.test(tagContent)) {
+ // Strip out tag
+ str = str.slice(0, start) + str.slice(end + 1);
+ } else {
+ // Not a list-related tag: Store tag contents in an array
+ stack.push(tagContent);
+
+ // Keep the "<" and ">" while removing from source string
+ start++;
+ str = str.slice(0, start) + str.slice(end);
+ }
+ }
+ }
+ searchStart = start + 1;
+ }
+ } while (start >= 0);
+
+ // Replace separator characters with table cells
+ var replaceString;
+ if (gDialog.deleteSepCharacter.checked) {
+ replaceString = "";
+ } else {
+ // Don't delete separator character,
+ // so include it at start of string to replace
+ replaceString = sepCharacter;
+ }
+
+ replaceString += "
";
+
+ if (sepCharacter.length > 0) {
+ var tempStr = sepCharacter;
+ var regExpChars = ".!@#$%^&*-+[]{}()|\\/";
+ if (regExpChars.includes(sepCharacter)) {
+ tempStr = "\\" + sepCharacter;
+ }
+
+ if (gIndex == gSpaceIndex) {
+ // If checkbox is checked,
+ // one or more adjacent spaces are one separator
+ if (gDialog.collapseSpaces.checked) {
+ tempStr = "\\s+";
+ } else {
+ tempStr = "\\s";
+ }
+ }
+ var pattern = new RegExp(tempStr, "g");
+ str = str.replace(pattern, replaceString);
+ }
+
+ // Put back tag contents that we removed above
+ searchStart = 0;
+ var stackIndex = 0;
+ do {
+ start = str.indexOf("<", searchStart);
+ end = start + 1;
+ if (start >= 0 && str.charAt(end) == ">") {
+ // We really need a FIFO stack!
+ str = str.slice(0, end) + stack[stackIndex++] + str.slice(end);
+ }
+ searchStart = end;
+ } while (start >= 0);
+
+ // End table row and start another for each br or p
+ str = str.replace(/\s* \s*/g, "\n
");
+
+ // Add the table tags and the opening and closing tr/td tags
+ // Default table attributes should be same as those used in nsHTMLEditor::CreateElementWithDefaults()
+ // (Default width="100%" is used in EdInsertTable.js)
+ str =
+ '
\n
' +
+ str +
+ "
\n
\n";
+
+ editor.beginTransaction();
+
+ // Delete the selection -- makes it easier to find where table will insert
+ var nodeBeforeTable = null;
+ var nodeAfterTable = null;
+ try {
+ editor.deleteSelection(editor.eNone, editor.eStrip);
+
+ var anchorNodeBeforeInsert = editor.selection.anchorNode;
+ var offset = editor.selection.anchorOffset;
+ if (anchorNodeBeforeInsert.nodeType == Node.TEXT_NODE) {
+ // Text was split. Table should be right after the first or before
+ nodeBeforeTable = anchorNodeBeforeInsert.previousSibling;
+ nodeAfterTable = anchorNodeBeforeInsert;
+ } else {
+ // Table should be inserted right after node pointed to by selection
+ if (offset > 0) {
+ nodeBeforeTable = anchorNodeBeforeInsert.childNodes.item(offset - 1);
+ }
+
+ nodeAfterTable = anchorNodeBeforeInsert.childNodes.item(offset);
+ }
+
+ editor.insertHTML(str);
+ } catch (e) {}
+
+ var table = null;
+ if (nodeAfterTable) {
+ var previous = nodeAfterTable.previousSibling;
+ if (previous && previous.nodeName.toLowerCase() == "table") {
+ table = previous;
+ }
+ }
+ if (!table && nodeBeforeTable) {
+ var next = nodeBeforeTable.nextSibling;
+ if (next && next.nodeName.toLowerCase() == "table") {
+ table = next;
+ }
+ }
+
+ if (table) {
+ // Fixup table only if pref is set
+ var firstRow;
+ try {
+ if (Services.prefs.getBoolPref("editor.table.maintain_structure")) {
+ editor.normalizeTable(table);
+ }
+
+ firstRow = editor.getFirstRow(table);
+ } catch (e) {}
+
+ // Put caret in first cell
+ if (firstRow) {
+ var node2 = firstRow.firstChild;
+ do {
+ if (
+ node2.nodeName.toLowerCase() == "td" ||
+ node2.nodeName.toLowerCase() == "th"
+ ) {
+ try {
+ editor.selection.collapse(node2, 0);
+ } catch (e) {}
+ break;
+ }
+ node2 = node2.nextSibling;
+ } while (node2);
+ }
+ }
+
+ editor.endTransaction();
+
+ // Save persisted attributes
+ gDialog.sepRadioGroup.setAttribute("index", gIndex);
+ if (gIndex == gOtherIndex) {
+ gDialog.sepRadioGroup.setAttribute("character", sepCharacter);
+ }
+
+ SaveWindowLocation();
+}
+/* eslint-enable complexity */
diff --git a/comm/suite/editor/components/dialogs/content/EdConvertToTable.xhtml b/comm/suite/editor/components/dialogs/content/EdConvertToTable.xhtml
new file mode 100644
index 0000000000..d3d5c4a465
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdConvertToTable.xhtml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/comm/suite/editor/components/dialogs/content/EdDialogCommon.js b/comm/suite/editor/components/dialogs/content/EdDialogCommon.js
new file mode 100644
index 0000000000..c6b7c63778
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdDialogCommon.js
@@ -0,0 +1,1038 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Each editor window must include this file
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* globals InitDialog, ChangeLinkLocation, ValidateData */
+
+// Object to attach commonly-used widgets (all dialogs should use this)
+var gDialog = {};
+
+var gHaveDocumentUrl = false;
+var gValidationError = false;
+
+// Use for 'defaultIndex' param in InitPixelOrPercentMenulist
+const gPixel = 0;
+const gPercent = 1;
+
+const gMaxPixels = 100000; // Used for image size, borders, spacing, and padding
+// Gecko code uses 1000 for maximum rowspan, colspan
+// Also, editing performance is really bad above this
+const gMaxRows = 1000;
+const gMaxColumns = 1000;
+const gMaxTableSize = 1000000; // Width or height of table or cells
+
+// For dialogs that expand in size. Default is smaller size see "onMoreFewer()" below
+var SeeMore = false;
+
+// A XUL element with id="location" for managing
+// dialog location relative to parent window
+var gLocation;
+
+// The element being edited - so AdvancedEdit can have access to it
+var globalElement;
+
+/* Validate contents of an input field
+ *
+ * inputWidget The 'textbox' XUL element for text input of the attribute's value
+ * listWidget The 'menulist' XUL element for choosing "pixel" or "percent"
+ * May be null when no pixel/percent is used.
+ * minVal minimum allowed for input widget's value
+ * maxVal maximum allowed for input widget's value
+ * (when "listWidget" is used, maxVal is used for "pixel" maximum,
+ * 100% is assumed if "percent" is the user's choice)
+ * element The DOM element that we set the attribute on. May be null.
+ * attName Name of the attribute to set. May be null or ignored if "element" is null
+ * mustHaveValue If true, error dialog is displayed if "value" is empty string
+ *
+ * This calls "ValidateNumberRange()", which puts up an error dialog to inform the user.
+ * If error, we also:
+ * Shift focus and select contents of the inputWidget,
+ * Switch to appropriate panel of tabbed dialog if user implements "SwitchToValidate()",
+ * and/or will expand the dialog to full size if "More / Fewer" feature is implemented
+ *
+ * Returns the "value" as a string, or "" if error or input contents are empty
+ * The global "gValidationError" variable is set true if error was found
+ */
+function ValidateNumber(
+ inputWidget,
+ listWidget,
+ minVal,
+ maxVal,
+ element,
+ attName,
+ mustHaveValue,
+ mustShowMoreSection
+) {
+ if (!inputWidget) {
+ gValidationError = true;
+ return "";
+ }
+
+ // Global error return value
+ gValidationError = false;
+ var maxLimit = maxVal;
+ var isPercent = false;
+
+ var numString = TrimString(inputWidget.value);
+ if (numString || mustHaveValue) {
+ if (listWidget) {
+ isPercent = listWidget.selectedIndex == 1;
+ }
+ if (isPercent) {
+ maxLimit = 100;
+ }
+
+ // This method puts up the error message
+ numString = ValidateNumberRange(numString, minVal, maxLimit, mustHaveValue);
+ if (!numString) {
+ // Switch to appropriate panel for error reporting
+ SwitchToValidatePanel();
+
+ // or expand dialog for users of "More / Fewer" button
+ if (
+ "dialog" in window &&
+ window.dialog &&
+ "MoreSection" in gDialog &&
+ gDialog.MoreSection
+ ) {
+ if (!SeeMore) {
+ onMoreFewer();
+ }
+ }
+
+ // Error - shift to offending input widget
+ SetTextboxFocus(inputWidget);
+ gValidationError = true;
+ } else {
+ if (isPercent) {
+ numString += "%";
+ }
+ if (element) {
+ GetCurrentEditor().setAttributeOrEquivalent(
+ element,
+ attName,
+ numString,
+ true
+ );
+ }
+ }
+ } else if (element) {
+ GetCurrentEditor().removeAttributeOrEquivalent(element, attName, true);
+ }
+ return numString;
+}
+
+/* Validate contents of an input field
+ *
+ * value number to validate
+ * minVal minimum allowed for input widget's value
+ * maxVal maximum allowed for input widget's value
+ * (when "listWidget" is used, maxVal is used for "pixel" maximum,
+ * 100% is assumed if "percent" is the user's choice)
+ * mustHaveValue If true, error dialog is displayed if "value" is empty string
+ *
+ * If inputWidget's value is outside of range, or is empty when "mustHaveValue" = true,
+ * an error dialog is popuped up to inform the user. The focus is shifted
+ * to the inputWidget.
+ *
+ * Returns the "value" as a string, or "" if error or input contents are empty
+ * The global "gValidationError" variable is set true if error was found
+ */
+function ValidateNumberRange(value, minValue, maxValue, mustHaveValue) {
+ // Initialize global error flag
+ gValidationError = false;
+ value = TrimString(String(value));
+
+ // We don't show error for empty string unless caller wants to
+ if (!value && !mustHaveValue) {
+ return "";
+ }
+
+ var numberStr = "";
+
+ if (value.length > 0) {
+ // Extract just numeric characters
+ var number = Number(value.replace(/\D+/g, ""));
+ if (number >= minValue && number <= maxValue) {
+ // Return string version of the number
+ return String(number);
+ }
+ numberStr = String(number);
+ }
+
+ var message = "";
+
+ if (numberStr.length > 0) {
+ // We have a number from user outside of allowed range
+ message = GetString("ValidateRangeMsg");
+ message = message.replace(/%n%/, numberStr);
+ message += "\n ";
+ }
+ message += GetString("ValidateNumberMsg");
+
+ // Replace variable placeholders in message with number values
+ message = message.replace(/%min%/, minValue).replace(/%max%/, maxValue);
+ ShowInputErrorMessage(message);
+
+ // Return an empty string to indicate error
+ gValidationError = true;
+ return "";
+}
+
+function SetTextboxFocusById(id) {
+ SetTextboxFocus(document.getElementById(id));
+}
+
+function SetTextboxFocus(textbox) {
+ if (textbox) {
+ // XXX Using the setTimeout is hacky workaround for bug 103197
+ // Must create a new function to keep "textbox" in scope
+ setTimeout(
+ function(textbox) {
+ textbox.focus();
+ textbox.select();
+ },
+ 0,
+ textbox
+ );
+ }
+}
+
+function ShowInputErrorMessage(message) {
+ Services.prompt.alert(window, GetString("InputError"), message);
+ window.focus();
+}
+
+// Get the text appropriate to parent container
+// to determine what a "%" value is referring to.
+// elementForAtt is element we are actually setting attributes on
+// (a temporary copy of element in the doc to allow canceling),
+// but elementInDoc is needed to find parent context in document
+function GetAppropriatePercentString(elementForAtt, elementInDoc) {
+ var editor = GetCurrentEditor();
+ try {
+ var name = elementForAtt.nodeName.toLowerCase();
+ if (name == "td" || name == "th") {
+ return GetString("PercentOfTable");
+ }
+
+ // Check if element is within a table cell
+ if (editor.getElementOrParentByTagName("td", elementInDoc)) {
+ return GetString("PercentOfCell");
+ }
+ return GetString("PercentOfWindow");
+ } catch (e) {
+ return "";
+ }
+}
+
+function ClearListbox(listbox) {
+ if (listbox) {
+ listbox.clearSelection();
+ while (listbox.hasChildNodes()) {
+ listbox.lastChild.remove();
+ }
+ }
+}
+
+function forceInteger(elementID) {
+ var editField = document.getElementById(elementID);
+ if (!editField) {
+ return;
+ }
+
+ var stringIn = editField.value;
+ if (stringIn && stringIn.length > 0) {
+ // Strip out all nonnumeric characters
+ stringIn = stringIn.replace(/\D+/g, "");
+ if (!stringIn) {
+ stringIn = "";
+ }
+
+ // Write back only if changed
+ if (stringIn != editField.value) {
+ editField.value = stringIn;
+ }
+ }
+}
+
+function InitPixelOrPercentMenulist(
+ elementForAtt,
+ elementInDoc,
+ attribute,
+ menulistID,
+ defaultIndex
+) {
+ if (!defaultIndex) {
+ defaultIndex = gPixel;
+ }
+
+ // var size = elementForAtt.getAttribute(attribute);
+ var size = GetHTMLOrCSSStyleValue(elementForAtt, attribute, attribute);
+ var menulist = document.getElementById(menulistID);
+ var pixelItem;
+ var percentItem;
+
+ if (!menulist) {
+ dump("NO MENULIST found for ID=" + menulistID + "\n");
+ return size;
+ }
+
+ menulist.removeAllItems();
+ pixelItem = menulist.appendItem(GetString("Pixels"));
+
+ if (!pixelItem) {
+ return 0;
+ }
+
+ percentItem = menulist.appendItem(
+ GetAppropriatePercentString(elementForAtt, elementInDoc)
+ );
+ if (size && size.length > 0) {
+ // Search for a "%" or "px"
+ if (size.includes("%")) {
+ // Strip out the %
+ size = size.substr(0, size.indexOf("%"));
+ if (percentItem) {
+ menulist.selectedItem = percentItem;
+ }
+ } else {
+ if (size.includes("px")) {
+ // Strip out the px
+ size = size.substr(0, size.indexOf("px"));
+ }
+ menulist.selectedItem = pixelItem;
+ }
+ } else {
+ menulist.selectedIndex = defaultIndex;
+ }
+
+ return size;
+}
+
+function onAdvancedEdit() {
+ // First validate data from widgets in the "simpler" property dialog
+ if (ValidateData()) {
+ // Set true if OK is clicked in the Advanced Edit dialog
+ window.AdvancedEditOK = false;
+ // Open the AdvancedEdit dialog, passing in the element to be edited
+ // (the copy named "globalElement")
+ window.openDialog(
+ "chrome://editor/content/EdAdvancedEdit.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal,resizable=yes",
+ "",
+ globalElement
+ );
+ window.focus();
+ if (window.AdvancedEditOK) {
+ // Copy edited attributes to the dialog widgets:
+ InitDialog();
+ }
+ }
+}
+
+function getColor(ColorPickerID) {
+ var colorPicker = document.getElementById(ColorPickerID);
+ var color;
+ if (colorPicker) {
+ // Extract color from colorPicker and assign to colorWell.
+ color = colorPicker.getAttribute("color");
+ if (color && color == "") {
+ return null;
+ }
+ // Clear color so next if it's called again before
+ // color picker is actually used, we dedect the "don't set color" state
+ colorPicker.setAttribute("color", "");
+ }
+
+ return color;
+}
+
+function setColorWell(ColorWellID, color) {
+ var colorWell = document.getElementById(ColorWellID);
+ if (colorWell) {
+ if (!color || color == "") {
+ // Don't set color (use default)
+ // Trigger change to not show color swatch
+ colorWell.setAttribute("default", "true");
+ // Style in CSS sets "background-color",
+ // but color won't clear unless we do this:
+ colorWell.removeAttribute("style");
+ } else {
+ colorWell.removeAttribute("default");
+ // Use setAttribute so colorwell can be a XUL element, such as button
+ colorWell.setAttribute("style", "background-color:" + color);
+ }
+ }
+}
+
+function getColorAndSetColorWell(ColorPickerID, ColorWellID) {
+ var color = getColor(ColorPickerID);
+ setColorWell(ColorWellID, color);
+ return color;
+}
+
+function InitMoreFewer() {
+ // Set SeeMore bool to the OPPOSITE of the current state,
+ // which is automatically saved by using the 'persist="more"'
+ // attribute on the gDialog.MoreFewerButton button
+ // onMoreFewer will toggle it and redraw the dialog
+ SeeMore = gDialog.MoreFewerButton.getAttribute("more") != "1";
+ onMoreFewer();
+ gDialog.MoreFewerButton.setAttribute(
+ "accesskey",
+ GetString("PropertiesAccessKey")
+ );
+}
+
+function onMoreFewer() {
+ if (SeeMore) {
+ gDialog.MoreSection.collapsed = true;
+ gDialog.MoreFewerButton.setAttribute("more", "0");
+ gDialog.MoreFewerButton.setAttribute("label", GetString("MoreProperties"));
+ SeeMore = false;
+ } else {
+ gDialog.MoreSection.collapsed = false;
+ gDialog.MoreFewerButton.setAttribute("more", "1");
+ gDialog.MoreFewerButton.setAttribute("label", GetString("FewerProperties"));
+ SeeMore = true;
+ }
+ window.sizeToContent();
+}
+
+function SwitchToValidatePanel() {
+ // no default implementation
+ // Only EdTableProps.js currently implements this
+}
+
+const nsIFilePicker = Ci.nsIFilePicker;
+
+/**
+ * @return {Promise} URL spec of the file chosen, or null
+ */
+function GetLocalFileURL(filterType) {
+ var fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
+ var fileType = "html";
+
+ if (filterType == "img") {
+ fp.init(window, GetString("SelectImageFile"), nsIFilePicker.modeOpen);
+ fp.appendFilters(nsIFilePicker.filterImages);
+ fileType = "image";
+ } else if (filterType.startsWith("html")) {
+ // Current usage of this is in Link dialog,
+ // where we always want HTML first
+ fp.init(window, GetString("OpenHTMLFile"), nsIFilePicker.modeOpen);
+
+ // When loading into Composer, direct user to prefer HTML files and text files,
+ // so we call separately to control the order of the filter list
+ fp.appendFilters(nsIFilePicker.filterHTML);
+ fp.appendFilters(nsIFilePicker.filterText);
+
+ // Link dialog also allows linking to images
+ if (filterType.includes("img", 1)) {
+ fp.appendFilters(nsIFilePicker.filterImages);
+ }
+ }
+ // Default or last filter is "All Files"
+ fp.appendFilters(nsIFilePicker.filterAll);
+
+ // set the file picker's current directory to last-opened location saved in prefs
+ SetFilePickerDirectory(fp, fileType);
+
+ return new Promise(resolve => {
+ fp.open(rv => {
+ if (rv != nsIFilePicker.returnOK || !fp.file) {
+ resolve(null);
+ return;
+ }
+ SaveFilePickerDirectory(fp, fileType);
+ resolve(fp.fileURL.spec);
+ });
+ });
+}
+
+function GetMetaElementByAttribute(name, value) {
+ if (name) {
+ name = name.toLowerCase();
+ let editor = GetCurrentEditor();
+ try {
+ return editor.document.querySelector(
+ "meta[" + name + '="' + value + '"]'
+ );
+ } catch (e) {}
+ }
+ return null;
+}
+
+function CreateMetaElementWithAttribute(name, value) {
+ let editor = GetCurrentEditor();
+ try {
+ let metaElement = editor.createElementWithDefaults("meta");
+ if (name) {
+ metaElement.setAttribute(name, value);
+ }
+ return metaElement;
+ } catch (e) {}
+ return null;
+}
+
+// Change "content" attribute on a META element,
+// or delete entire element it if content is empty
+// This uses undoable editor transactions
+function SetMetaElementContent(metaElement, content, insertNew, prepend) {
+ if (metaElement) {
+ var editor = GetCurrentEditor();
+ try {
+ if (!content || content == "") {
+ if (!insertNew) {
+ editor.deleteNode(metaElement);
+ }
+ } else if (insertNew) {
+ metaElement.setAttribute("content", content);
+ if (prepend) {
+ PrependHeadElement(metaElement);
+ } else {
+ AppendHeadElement(metaElement);
+ }
+ } else {
+ editor.setAttribute(metaElement, "content", content);
+ }
+ } catch (e) {}
+ }
+}
+
+function GetHeadElement() {
+ var editor = GetCurrentEditor();
+ try {
+ return editor.document.querySelector("head");
+ } catch (e) {}
+
+ return null;
+}
+
+function PrependHeadElement(element) {
+ var head = GetHeadElement();
+ if (head) {
+ var editor = GetCurrentEditor();
+ try {
+ // Use editor's undoable transaction
+ // XXX Here tried to prevent updating Selection with unknown 4th argument,
+ // but nsIEditor.setShouldTxnSetSelection is not used for that.
+ editor.insertNode(element, head, 0);
+ } catch (e) {}
+ }
+}
+
+function AppendHeadElement(element) {
+ var head = GetHeadElement();
+ if (head) {
+ var position = 0;
+ if (head.hasChildNodes()) {
+ position = head.childNodes.length;
+ }
+
+ var editor = GetCurrentEditor();
+ try {
+ // Use editor's undoable transaction
+ // XXX Here tried to prevent updating Selection with unknown 4th argument,
+ // but nsIEditor.setShouldTxnSetSelection is not used for that.
+ editor.insertNode(element, head, position);
+ } catch (e) {}
+ }
+}
+
+function SetWindowLocation() {
+ gLocation = document.getElementById("location");
+ if (gLocation) {
+ window.screenX = Math.max(
+ 0,
+ Math.min(
+ window.opener.screenX + Number(gLocation.getAttribute("offsetX")),
+ screen.availWidth - window.outerWidth
+ )
+ );
+ window.screenY = Math.max(
+ 0,
+ Math.min(
+ window.opener.screenY + Number(gLocation.getAttribute("offsetY")),
+ screen.availHeight - window.outerHeight
+ )
+ );
+ }
+}
+
+function SaveWindowLocation() {
+ if (gLocation) {
+ gLocation.setAttribute("offsetX", window.screenX - window.opener.screenX);
+ gLocation.setAttribute("offsetY", window.screenY - window.opener.screenY);
+ }
+}
+
+function onCancel() {
+ SaveWindowLocation();
+}
+
+function SetRelativeCheckbox(checkbox) {
+ if (!checkbox) {
+ checkbox = document.getElementById("MakeRelativeCheckbox");
+ if (!checkbox) {
+ return;
+ }
+ }
+
+ var editor = GetCurrentEditor();
+ // Mail never allows relative URLs, so hide the checkbox
+ if (editor && editor.flags & Ci.nsIEditor.eEditorMailMask) {
+ checkbox.collapsed = true;
+ return;
+ }
+
+ var input = document.getElementById(checkbox.getAttribute("for"));
+ if (!input) {
+ return;
+ }
+
+ var url = TrimString(input.value);
+ var urlScheme = GetScheme(url);
+
+ // Check it if url is relative (no scheme).
+ checkbox.checked = url.length > 0 && !urlScheme;
+
+ // Now do checkbox enabling:
+ var enable = false;
+
+ var docUrl = GetDocumentBaseUrl();
+ var docScheme = GetScheme(docUrl);
+
+ if (url && docUrl && docScheme) {
+ if (urlScheme) {
+ // Url is absolute
+ // If we can make a relative URL, then enable must be true!
+ // (this lets the smarts of MakeRelativeUrl do all the hard work)
+ enable = GetScheme(MakeRelativeUrl(url)).length == 0;
+ } else if (url[0] == "#") {
+ // Url is relative
+ // Check if url is a named anchor
+ // but document doesn't have a filename
+ // (it's probably "index.html" or "index.htm",
+ // but we don't want to allow a malformed URL)
+ var docFilename = GetFilename(docUrl);
+ enable = docFilename.length > 0;
+ } else {
+ // Any other url is assumed
+ // to be ok to try to make absolute
+ enable = true;
+ }
+ }
+
+ SetElementEnabled(checkbox, enable);
+}
+
+// oncommand handler for the Relativize checkbox in EditorOverlay.xhtml
+function MakeInputValueRelativeOrAbsolute(checkbox) {
+ var input = document.getElementById(checkbox.getAttribute("for"));
+ if (!input) {
+ return;
+ }
+
+ var docUrl = GetDocumentBaseUrl();
+ if (!docUrl) {
+ // Checkbox should be disabled if not saved,
+ // but keep this error message in case we change that
+ Services.prompt.alert(window, "", GetString("SaveToUseRelativeUrl"));
+ window.focus();
+ } else {
+ // Note that "checked" is opposite of its last state,
+ // which determines what we want to do here
+ if (checkbox.checked) {
+ input.value = MakeRelativeUrl(input.value);
+ } else {
+ input.value = MakeAbsoluteUrl(input.value);
+ }
+
+ // Reset checkbox to reflect url state
+ SetRelativeCheckbox(checkbox);
+ }
+}
+
+var IsBlockParent = [
+ "applet",
+ "blockquote",
+ "body",
+ "center",
+ "dd",
+ "div",
+ "form",
+ "li",
+ "noscript",
+ "object",
+ "td",
+ "th",
+];
+
+var NotAnInlineParent = [
+ "col",
+ "colgroup",
+ "dl",
+ "dir",
+ "menu",
+ "ol",
+ "table",
+ "tbody",
+ "tfoot",
+ "thead",
+ "tr",
+ "ul",
+];
+
+function nodeIsBreak(editor, node) {
+ return !node || node.localName == "br" || editor.nodeIsBlock(node);
+}
+
+function InsertElementAroundSelection(element) {
+ var editor = GetCurrentEditor();
+ editor.beginTransaction();
+
+ try {
+ // First get the selection as a single range
+ var range, start, end, offset;
+ var count = editor.selection.rangeCount;
+ if (count == 1) {
+ range = editor.selection.getRangeAt(0).cloneRange();
+ } else {
+ range = editor.document.createRange();
+ start = editor.selection.getRangeAt(0);
+ range.setStart(start.startContainer, start.startOffset);
+ end = editor.selection.getRangeAt(--count);
+ range.setEnd(end.endContainer, end.endOffset);
+ }
+
+ // Flatten the selection to child nodes of the common ancestor
+ while (range.startContainer != range.commonAncestorContainer) {
+ range.setStartBefore(range.startContainer);
+ }
+ while (range.endContainer != range.commonAncestorContainer) {
+ range.setEndAfter(range.endContainer);
+ }
+
+ if (editor.nodeIsBlock(element)) {
+ // Block element parent must be a valid block
+ while (!IsBlockParent.includes(range.commonAncestorContainer.localName)) {
+ range.selectNode(range.commonAncestorContainer);
+ }
+ } else {
+ if (!nodeIsBreak(editor, range.commonAncestorContainer)) {
+ // Fail if we're not inserting a block (use setInlineProperty instead)
+ return false;
+ }
+ if (NotAnInlineParent.includes(range.commonAncestorContainer.localName)) {
+ // Inline element parent must not be an invalid block
+ do {
+ range.selectNode(range.commonAncestorContainer);
+ } while (
+ NotAnInlineParent.includes(range.commonAncestorContainer.localName)
+ );
+ } else {
+ // Further insert block check
+ for (var i = range.startOffset; ; i++) {
+ if (i == range.endOffset) {
+ return false;
+ }
+ if (
+ nodeIsBreak(editor, range.commonAncestorContainer.childNodes[i])
+ ) {
+ break;
+ }
+ }
+ }
+ }
+
+ // The range may be contained by body text, which should all be selected.
+ offset = range.startOffset;
+ start = range.startContainer.childNodes[offset];
+ if (!nodeIsBreak(editor, start)) {
+ while (!nodeIsBreak(editor, start.previousSibling)) {
+ start = start.previousSibling;
+ offset--;
+ }
+ }
+ end = range.endContainer.childNodes[range.endOffset];
+ if (end && !nodeIsBreak(editor, end.previousSibling)) {
+ while (!nodeIsBreak(editor, end)) {
+ end = end.nextSibling;
+ }
+ }
+
+ // Now insert the node
+ // XXX Here tried to prevent updating Selection with unknown 4th argument,
+ // but nsIEditor.setShouldTxnSetSelection is not used for that.
+ editor.insertNode(element, range.commonAncestorContainer, offset);
+ offset = element.childNodes.length;
+ if (!editor.nodeIsBlock(element)) {
+ editor.setShouldTxnSetSelection(false);
+ }
+
+ // Move all the old child nodes to the element
+ var empty = true;
+ while (start != end) {
+ var next = start.nextSibling;
+ editor.deleteNode(start);
+ editor.insertNode(start, element, element.childNodes.length);
+ empty = false;
+ start = next;
+ }
+ if (!editor.nodeIsBlock(element)) {
+ editor.setShouldTxnSetSelection(true);
+ } else {
+ // Also move a trailing
+ if (start && start.localName == "br") {
+ editor.deleteNode(start);
+ editor.insertNode(start, element, element.childNodes.length);
+ empty = false;
+ }
+ // Still nothing? Insert a so the node is not empty
+ if (empty) {
+ editor.insertNode(
+ editor.createElementWithDefaults("br"),
+ element,
+ element.childNodes.length
+ );
+ }
+
+ // Hack to set the selection just inside the element
+ editor.insertNode(editor.document.createTextNode(""), element, offset);
+ }
+ } finally {
+ editor.endTransaction();
+ }
+
+ return true;
+}
+
+function nodeIsBlank(node) {
+ return node && node.nodeType == Node.TEXT_NODE && !/\S/.test(node.data);
+}
+
+function nodeBeginsBlock(editor, node) {
+ while (nodeIsBlank(node)) {
+ node = node.nextSibling;
+ }
+ return nodeIsBreak(editor, node);
+}
+
+function nodeEndsBlock(editor, node) {
+ while (nodeIsBlank(node)) {
+ node = node.previousSibling;
+ }
+ return nodeIsBreak(editor, node);
+}
+
+// C++ function isn't exposed to JS :-(
+function RemoveBlockContainer(element) {
+ var editor = GetCurrentEditor();
+ editor.beginTransaction();
+
+ try {
+ var range = editor.document.createRange();
+ range.selectNode(element);
+ var offset = range.startOffset;
+ var parent = element.parentNode;
+
+ // May need to insert a break after the removed element
+ if (
+ !nodeBeginsBlock(editor, element.nextSibling) &&
+ !nodeEndsBlock(editor, element.lastChild)
+ ) {
+ editor.insertNode(
+ editor.createElementWithDefaults("br"),
+ parent,
+ range.endOffset
+ );
+ }
+
+ // May need to insert a break before the removed element, or if it was empty
+ if (
+ !nodeEndsBlock(editor, element.previousSibling) &&
+ !nodeBeginsBlock(editor, element.firstChild || element.nextSibling)
+ ) {
+ editor.insertNode(
+ editor.createElementWithDefaults("br"),
+ parent,
+ offset++
+ );
+ }
+
+ // Now remove the element
+ editor.deleteNode(element);
+
+ // Need to copy the contained nodes?
+ for (var i = 0; i < element.childNodes.length; i++) {
+ editor.insertNode(
+ element.childNodes[i].cloneNode(true),
+ parent,
+ offset++
+ );
+ }
+ } finally {
+ editor.endTransaction();
+ }
+}
+
+// C++ function isn't exposed to JS :-(
+function RemoveContainer(element) {
+ var editor = GetCurrentEditor();
+ editor.beginTransaction();
+
+ try {
+ var range = editor.document.createRange();
+ var parent = element.parentNode;
+ // Allow for automatic joining of text nodes
+ // so we can't delete the container yet
+ // so we need to copy the contained nodes
+ for (var i = 0; i < element.childNodes.length; i++) {
+ range.selectNode(element);
+ editor.insertNode(
+ element.childNodes[i].cloneNode(true),
+ parent,
+ range.startOffset
+ );
+ }
+ // Now remove the element
+ editor.deleteNode(element);
+ } finally {
+ editor.endTransaction();
+ }
+}
+
+function FillLinkMenulist(linkMenulist, headingsArray) {
+ var menupopup = linkMenulist.firstChild;
+ var editor = GetCurrentEditor();
+ try {
+ var treeWalker = editor.document.createTreeWalker(
+ editor.document,
+ 1,
+ null,
+ true
+ );
+ var headingList = [];
+ var anchorList = []; // for sorting
+ var anchorMap = {}; // for weeding out duplicates and making heading anchors unique
+ var anchor;
+ var i;
+ for (
+ var element = treeWalker.nextNode();
+ element;
+ element = treeWalker.nextNode()
+ ) {
+ // grab headings
+ // Skip headings that already have a named anchor as their first child
+ // (this may miss nearby anchors, but at least we don't insert another
+ // under the same heading)
+ if (
+ element instanceof HTMLHeadingElement &&
+ element.textContent &&
+ !(
+ element.firstChild instanceof HTMLAnchorElement &&
+ element.firstChild.name
+ )
+ ) {
+ headingList.push(element);
+ }
+
+ // grab named anchors
+ if (element instanceof HTMLAnchorElement && element.name) {
+ anchor = "#" + element.name;
+ if (!(anchor in anchorMap)) {
+ anchorList.push({ anchor, sortkey: anchor.toLowerCase() });
+ anchorMap[anchor] = true;
+ }
+ }
+
+ // grab IDs
+ if (element.id) {
+ anchor = "#" + element.id;
+ if (!(anchor in anchorMap)) {
+ anchorList.push({ anchor, sortkey: anchor.toLowerCase() });
+ anchorMap[anchor] = true;
+ }
+ }
+ }
+ // add anchor for headings
+ for (i = 0; i < headingList.length; i++) {
+ var heading = headingList[i];
+
+ // Use just first 40 characters, don't add "...",
+ // and replace whitespace with "_" and strip non-word characters
+ anchor =
+ "#" +
+ ConvertToCDATAString(
+ TruncateStringAtWordEnd(heading.textContent, 40, false)
+ );
+
+ // Append "_" to any name already in the list
+ while (anchor in anchorMap) {
+ anchor += "_";
+ }
+ anchorList.push({ anchor, sortkey: anchor.toLowerCase() });
+ anchorMap[anchor] = true;
+
+ // Save nodes in an array so we can create anchor node under it later
+ headingsArray[anchor] = heading;
+ }
+ if (anchorList.length) {
+ // case insensitive sort
+ anchorList.sort((a, b) => {
+ if (a.sortkey < b.sortkey) {
+ return -1;
+ }
+ if (a.sortkey > b.sortkey) {
+ return 1;
+ }
+ return 0;
+ });
+
+ for (i = 0; i < anchorList.length; i++) {
+ createMenuItem(menupopup, anchorList[i].anchor);
+ }
+ } else {
+ // Don't bother with named anchors in Mail.
+ if (editor && editor.flags & Ci.nsIEditor.eEditorMailMask) {
+ menupopup.remove();
+ linkMenulist.removeAttribute("enablehistory");
+ return;
+ }
+ var item = createMenuItem(
+ menupopup,
+ GetString("NoNamedAnchorsOrHeadings")
+ );
+ item.setAttribute("disabled", "true");
+ }
+ } catch (e) {}
+}
+
+function createMenuItem(aMenuPopup, aLabel) {
+ var menuitem = document.createXULElement("menuitem");
+ menuitem.setAttribute("label", aLabel);
+ aMenuPopup.appendChild(menuitem);
+ return menuitem;
+}
+
+// Shared by Image and Link dialogs for the "Choose" button for links
+function chooseLinkFile() {
+ GetLocalFileURL("html, img").then(fileURL => {
+ // Always try to relativize local file URLs
+ if (gHaveDocumentUrl) {
+ fileURL = MakeRelativeUrl(fileURL);
+ }
+
+ gDialog.hrefInput.value = fileURL;
+
+ // Do stuff specific to a particular dialog
+ // (This is defined separately in Image and Link dialogs)
+ ChangeLinkLocation();
+ });
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdDialogTemplate.js b/comm/suite/editor/components/dialogs/content/EdDialogTemplate.js
new file mode 100644
index 0000000000..ee74e8c871
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdDialogTemplate.js
@@ -0,0 +1,45 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+// Cancel() is in EdDialogCommon.js
+var insertNew = true;
+var tagname = "TAG NAME";
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ if (!GetCurrentEditor()) {
+ window.close();
+ return;
+ }
+ // gDialog is declared in EdDialogCommon.js
+ // Set commonly-used widgets like this:
+ gDialog.fooButton = document.getElementById("fooButton");
+
+ InitDialog();
+
+ // Set window location relative to parent window (based on persisted attributes)
+ SetWindowLocation();
+
+ // Set focus to first widget in dialog, e.g.:
+ SetTextboxFocus(gDialog.fooButton);
+}
+
+function InitDialog() {
+ // Initialize all dialog widgets here,
+ // e.g., get attributes from an element for property dialog
+}
+
+function onAccept() {
+ // Validate all user data and set attributes and possibly insert new element here
+ // If there's an error the user must correct, return false to keep dialog open.
+
+ SaveWindowLocation();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdDialogTemplate.xhtml b/comm/suite/editor/components/dialogs/content/EdDialogTemplate.xhtml
new file mode 100644
index 0000000000..8f76af4654
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdDialogTemplate.xhtml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/comm/suite/editor/components/dialogs/content/EdDictionary.js b/comm/suite/editor/components/dialogs/content/EdDictionary.js
new file mode 100644
index 0000000000..892e92dd1a
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdDictionary.js
@@ -0,0 +1,164 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var gSpellChecker;
+var gWordToAdd;
+
+function Startup() {
+ if (!GetCurrentEditor()) {
+ window.close();
+ return;
+ }
+ // Get the SpellChecker shell
+ if ("gSpellChecker" in window.opener && window.opener.gSpellChecker) {
+ gSpellChecker = window.opener.gSpellChecker;
+ }
+
+ if (!gSpellChecker) {
+ dump("SpellChecker not found!!!\n");
+ window.close();
+ return;
+ }
+ // The word to add word is passed as the 2nd extra parameter in window.openDialog()
+ gWordToAdd = window.arguments[1];
+
+ gDialog.WordInput = document.getElementById("WordInput");
+ gDialog.DictionaryList = document.getElementById("DictionaryList");
+
+ gDialog.WordInput.value = gWordToAdd;
+ FillDictionaryList();
+
+ // Select the supplied word if it is already in the list
+ SelectWordToAddInList();
+ SetTextboxFocus(gDialog.WordInput);
+}
+
+function ValidateWordToAdd() {
+ gWordToAdd = TrimString(gDialog.WordInput.value);
+ if (gWordToAdd.length > 0) {
+ return true;
+ }
+ return false;
+}
+
+function SelectWordToAddInList() {
+ for (var i = 0; i < gDialog.DictionaryList.getRowCount(); i++) {
+ var wordInList = gDialog.DictionaryList.getItemAtIndex(i);
+ if (wordInList && gWordToAdd == wordInList.label) {
+ gDialog.DictionaryList.selectedIndex = i;
+ break;
+ }
+ }
+}
+
+function AddWord() {
+ if (ValidateWordToAdd()) {
+ try {
+ gSpellChecker.AddWordToDictionary(gWordToAdd);
+ } catch (e) {
+ dump(
+ "Exception occurred in gSpellChecker.AddWordToDictionary\nWord to add probably already existed\n"
+ );
+ }
+
+ // Rebuild the dialog list
+ FillDictionaryList();
+
+ SelectWordToAddInList();
+ gDialog.WordInput.value = "";
+ }
+}
+
+function ReplaceWord() {
+ if (ValidateWordToAdd()) {
+ var selItem = gDialog.DictionaryList.selectedItem;
+ if (selItem) {
+ try {
+ gSpellChecker.RemoveWordFromDictionary(selItem.label);
+ } catch (e) {}
+
+ try {
+ // Add to the dictionary list
+ gSpellChecker.AddWordToDictionary(gWordToAdd);
+
+ // Just change the text on the selected item instead of rebuilding the list.
+ // The items are richlist items, so the label sits in the first child.
+ selItem.firstChild.setAttribute("value", gWordToAdd);
+ } catch (e) {
+ // Rebuild list and select the word - it was probably already in the list
+ dump("Exception occurred adding word in ReplaceWord\n");
+ FillDictionaryList();
+ SelectWordToAddInList();
+ }
+ }
+ }
+}
+
+function RemoveWord() {
+ var selIndex = gDialog.DictionaryList.selectedIndex;
+ if (selIndex >= 0) {
+ var word = gDialog.DictionaryList.selectedItem.label;
+
+ // Remove word from list
+ gDialog.DictionaryList.selectedItem.remove();
+
+ // Remove from dictionary
+ try {
+ // Not working: BUG 43348
+ gSpellChecker.RemoveWordFromDictionary(word);
+ } catch (e) {
+ dump("Failed to remove word from dictionary\n");
+ }
+
+ ResetSelectedItem(selIndex);
+ }
+}
+
+function FillDictionaryList() {
+ var selIndex = gDialog.DictionaryList.selectedIndex;
+
+ // Clear the current contents of the list
+ ClearListbox(gDialog.DictionaryList);
+
+ // Get the list from the spell checker
+ gSpellChecker.GetPersonalDictionary();
+
+ var haveList = false;
+
+ // Get words until an empty string is returned
+ do {
+ var word = gSpellChecker.GetPersonalDictionaryWord();
+ if (word != "") {
+ gDialog.DictionaryList.appendItem(word, "");
+ haveList = true;
+ }
+ } while (word != "");
+
+ // XXX: BUG 74467: If list is empty, it doesn't layout to full height correctly
+ // (ignores "rows" attribute) (bug is latered, so we are fixing here for now)
+ if (!haveList) {
+ gDialog.DictionaryList.appendItem("", "");
+ }
+
+ ResetSelectedItem(selIndex);
+}
+
+function ResetSelectedItem(index) {
+ var lastIndex = gDialog.DictionaryList.getRowCount() - 1;
+ if (index > lastIndex) {
+ index = lastIndex;
+ }
+
+ // If we didn't have a selected item,
+ // set it to the first item
+ if (index == -1 && lastIndex >= 0) {
+ index = 0;
+ }
+
+ gDialog.DictionaryList.selectedIndex = index;
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdDictionary.xhtml b/comm/suite/editor/components/dialogs/content/EdDictionary.xhtml
new file mode 100644
index 0000000000..cebbe1c192
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdDictionary.xhtml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
diff --git a/comm/suite/editor/components/dialogs/content/EdFieldSetProps.js b/comm/suite/editor/components/dialogs/content/EdFieldSetProps.js
new file mode 100644
index 0000000000..1799a2b28f
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdFieldSetProps.js
@@ -0,0 +1,196 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var insertNew;
+var fieldsetElement;
+var newLegend;
+var legendElement;
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ dump("Failed to get active editor!\n");
+ window.close();
+ return;
+ }
+
+ gDialog.editText = document.getElementById("EditText");
+ gDialog.legendText = document.getElementById("LegendText");
+ gDialog.legendAlign = document.getElementById("LegendAlign");
+ gDialog.RemoveFieldSet = document.getElementById("RemoveFieldSet");
+
+ // Get a single selected field set element
+ const kTagName = "fieldset";
+ try {
+ // Find a selected fieldset, or if one is at start or end of selection.
+ fieldsetElement = editor.getSelectedElement(kTagName);
+ if (!fieldsetElement) {
+ fieldsetElement = editor.getElementOrParentByTagName(
+ kTagName,
+ editor.selection.anchorNode
+ );
+ }
+ if (!fieldsetElement) {
+ fieldsetElement = editor.getElementOrParentByTagName(
+ kTagName,
+ editor.selection.focusNode
+ );
+ }
+ } catch (e) {}
+
+ if (fieldsetElement) {
+ // We found an element and don't need to insert one
+ insertNew = false;
+ } else {
+ insertNew = true;
+
+ // We don't have an element selected,
+ // so create one with default attributes
+ try {
+ fieldsetElement = editor.createElementWithDefaults(kTagName);
+ } catch (e) {}
+
+ if (!fieldsetElement) {
+ dump("Failed to get selected element or create a new one!\n");
+ window.close();
+ return;
+ }
+ // Hide button removing existing fieldset
+ gDialog.RemoveFieldSet.hidden = true;
+ }
+
+ legendElement = fieldsetElement.querySelector("legend");
+ if (legendElement) {
+ newLegend = false;
+ var range = editor.document.createRange();
+ range.selectNode(legendElement);
+ gDialog.legendText.value = range.toString();
+ if (legendElement.innerHTML.includes("<")) {
+ gDialog.editText.checked = false;
+ gDialog.editText.disabled = false;
+ gDialog.legendText.disabled = true;
+ gDialog.editText.addEventListener(
+ "command",
+ () =>
+ Services.prompt.alert(
+ window,
+ GetString("Alert"),
+ GetString("EditTextWarning")
+ ),
+ { capture: false, once: true }
+ );
+ gDialog.RemoveFieldSet.focus();
+ } else {
+ SetTextboxFocus(gDialog.legendText);
+ }
+ } else {
+ newLegend = true;
+
+ // We don't have an element selected,
+ // so create one with default attributes
+
+ legendElement = editor.createElementWithDefaults("legend");
+ if (!legendElement) {
+ dump("Failed to get selected element or create a new one!\n");
+ window.close();
+ return;
+ }
+ SetTextboxFocus(gDialog.legendText);
+ }
+
+ // Make a copy to use for AdvancedEdit
+ globalElement = legendElement.cloneNode(false);
+
+ InitDialog();
+
+ SetWindowLocation();
+}
+
+function InitDialog() {
+ gDialog.legendAlign.value = GetHTMLOrCSSStyleValue(
+ globalElement,
+ "align",
+ "caption-side"
+ );
+}
+
+function RemoveFieldSet() {
+ var editor = GetCurrentEditor();
+ editor.beginTransaction();
+ try {
+ if (!newLegend) {
+ editor.deleteNode(legendElement);
+ }
+ RemoveBlockContainer(fieldsetElement);
+ } finally {
+ editor.endTransaction();
+ }
+ SaveWindowLocation();
+ window.close();
+}
+
+function ValidateData() {
+ if (gDialog.legendAlign.value) {
+ globalElement.setAttribute("align", gDialog.legendAlign.value);
+ } else {
+ globalElement.removeAttribute("align");
+ }
+ return true;
+}
+
+function onAccept() {
+ // All values are valid - copy to actual element in doc
+ ValidateData();
+
+ var editor = GetCurrentEditor();
+
+ editor.beginTransaction();
+
+ try {
+ editor.cloneAttributes(legendElement, globalElement);
+
+ if (insertNew) {
+ if (gDialog.legendText.value) {
+ fieldsetElement.appendChild(legendElement);
+ legendElement.appendChild(
+ editor.document.createTextNode(gDialog.legendText.value)
+ );
+ }
+ InsertElementAroundSelection(fieldsetElement);
+ } else if (gDialog.editText.checked) {
+ editor.setShouldTxnSetSelection(false);
+
+ if (gDialog.legendText.value) {
+ if (newLegend) {
+ editor.insertNode(legendElement, fieldsetElement, 0);
+ } else {
+ while (legendElement.firstChild) {
+ editor.deleteNode(legendElement.lastChild);
+ }
+ }
+ editor.insertNode(
+ editor.document.createTextNode(gDialog.legendText.value),
+ legendElement,
+ 0
+ );
+ } else if (!newLegend) {
+ editor.deleteNode(legendElement);
+ }
+
+ editor.setShouldTxnSetSelection(true);
+ }
+ } finally {
+ editor.endTransaction();
+ }
+
+ SaveWindowLocation();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdFieldSetProps.xhtml b/comm/suite/editor/components/dialogs/content/EdFieldSetProps.xhtml
new file mode 100644
index 0000000000..0d67fad276
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdFieldSetProps.xhtml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+%edFieldSetProperties;
+
+%edDialogOverlay;
+]>
+
+
diff --git a/comm/suite/editor/components/dialogs/content/EdFormProps.js b/comm/suite/editor/components/dialogs/content/EdFormProps.js
new file mode 100644
index 0000000000..9391fa4316
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdFormProps.js
@@ -0,0 +1,136 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var gForm;
+var insertNew;
+var formElement;
+var formActionWarning;
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ dump("Failed to get active editor!\n");
+ window.close();
+ return;
+ }
+
+ gForm = {
+ Name: document.getElementById("FormName"),
+ Action: document.getElementById("FormAction"),
+ Method: document.getElementById("FormMethod"),
+ EncType: document.getElementById("FormEncType"),
+ Target: document.getElementById("FormTarget"),
+ };
+ gDialog.MoreSection = document.getElementById("MoreSection");
+ gDialog.MoreFewerButton = document.getElementById("MoreFewerButton");
+ gDialog.RemoveForm = document.getElementById("RemoveForm");
+
+ // Get a single selected form element
+ const kTagName = "form";
+ try {
+ formElement = editor.getSelectedElement(kTagName);
+ if (!formElement) {
+ formElement = editor.getElementOrParentByTagName(
+ kTagName,
+ editor.selection.anchorNode
+ );
+ }
+ if (!formElement) {
+ formElement = editor.getElementOrParentByTagName(
+ kTagName,
+ editor.selection.focusNode
+ );
+ }
+ } catch (e) {}
+
+ if (formElement) {
+ // We found an element and don't need to insert one
+ insertNew = false;
+ formActionWarning = formElement.hasAttribute("action");
+ } else {
+ insertNew = true;
+ formActionWarning = true;
+
+ // We don't have an element selected,
+ // so create one with default attributes
+ try {
+ formElement = editor.createElementWithDefaults(kTagName);
+ } catch (e) {}
+
+ if (!formElement) {
+ dump("Failed to get selected element or create a new one!\n");
+ window.close();
+ return;
+ }
+ // Hide button removing existing form
+ gDialog.RemoveForm.hidden = true;
+ }
+
+ // Make a copy to use for AdvancedEdit
+ globalElement = formElement.cloneNode(false);
+
+ InitDialog();
+
+ InitMoreFewer();
+
+ SetTextboxFocus(gForm.Name);
+
+ SetWindowLocation();
+}
+
+function InitDialog() {
+ for (var attribute in gForm) {
+ gForm[attribute].value = globalElement.getAttribute(attribute);
+ }
+}
+
+function RemoveForm() {
+ RemoveBlockContainer(formElement);
+ SaveWindowLocation();
+ window.close();
+}
+
+function ValidateData() {
+ for (var attribute in gForm) {
+ if (gForm[attribute].value) {
+ globalElement.setAttribute(attribute, gForm[attribute].value);
+ } else {
+ globalElement.removeAttribute(attribute);
+ }
+ }
+ return true;
+}
+
+function onAccept(event) {
+ if (formActionWarning && !gForm.Action.value) {
+ Services.prompt.alert(
+ window,
+ GetString("Alert"),
+ GetString("NoFormAction")
+ );
+ gForm.Action.focus();
+ formActionWarning = false;
+ event.preventDefault();
+ return;
+ }
+ // All values are valid - copy to actual element in doc or
+ // element created to insert
+ ValidateData();
+
+ var editor = GetCurrentEditor();
+
+ editor.cloneAttributes(formElement, globalElement);
+
+ if (insertNew) {
+ InsertElementAroundSelection(formElement);
+ }
+
+ SaveWindowLocation();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdFormProps.xhtml b/comm/suite/editor/components/dialogs/content/EdFormProps.xhtml
new file mode 100644
index 0000000000..275daef785
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdFormProps.xhtml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+%edFormProperties;
+
+%edDialogOverlay;
+]>
+
+
diff --git a/comm/suite/editor/components/dialogs/content/EdHLineProps.js b/comm/suite/editor/components/dialogs/content/EdHLineProps.js
new file mode 100644
index 0000000000..eff9c06a41
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdHLineProps.js
@@ -0,0 +1,227 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var tagName = "hr";
+var gHLineElement;
+var width;
+var height;
+var align;
+var shading;
+const gMaxHRSize = 1000; // This is hard-coded in nsHTMLHRElement::StringToAttribute()
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ window.close();
+ return;
+ }
+ try {
+ // Get the selected horizontal line
+ gHLineElement = editor.getSelectedElement(tagName);
+ } catch (e) {}
+
+ if (!gHLineElement) {
+ // We should never be here if not editing an existing HLine
+ window.close();
+ return;
+ }
+ gDialog.heightInput = document.getElementById("height");
+ gDialog.widthInput = document.getElementById("width");
+ gDialog.leftAlign = document.getElementById("leftAlign");
+ gDialog.centerAlign = document.getElementById("centerAlign");
+ gDialog.rightAlign = document.getElementById("rightAlign");
+ gDialog.alignGroup = gDialog.rightAlign.radioGroup;
+ gDialog.shading = document.getElementById("3dShading");
+ gDialog.pixelOrPercentMenulist = document.getElementById(
+ "pixelOrPercentMenulist"
+ );
+
+ // Make a copy to use for AdvancedEdit and onSaveDefault
+ globalElement = gHLineElement.cloneNode(false);
+
+ // Initialize control values based on existing attributes
+ InitDialog();
+
+ // SET FOCUS TO FIRST CONTROL
+ SetTextboxFocus(gDialog.widthInput);
+
+ // Resize window
+ window.sizeToContent();
+
+ SetWindowLocation();
+}
+
+// Set dialog widgets with attribute data
+// We get them from globalElement copy so this can be used
+// by AdvancedEdit(), which is shared by all property dialogs
+function InitDialog() {
+ // Just to be confusing, "size" is used instead of height because it does
+ // not accept % values, only pixels
+ var height = GetHTMLOrCSSStyleValue(globalElement, "size", "height");
+ if (height.includes("px")) {
+ height = height.substr(0, height.indexOf("px"));
+ }
+ if (!height) {
+ height = 2; // Default value
+ }
+
+ // We will use "height" here and in UI
+ gDialog.heightInput.value = height;
+
+ // Get the width attribute of the element, stripping out "%"
+ // This sets contents of menulist (adds pixel and percent menuitems elements)
+ gDialog.widthInput.value = InitPixelOrPercentMenulist(
+ globalElement,
+ gHLineElement,
+ "width",
+ "pixelOrPercentMenulist"
+ );
+
+ var marginLeft = GetHTMLOrCSSStyleValue(
+ globalElement,
+ "align",
+ "margin-left"
+ ).toLowerCase();
+ var marginRight = GetHTMLOrCSSStyleValue(
+ globalElement,
+ "align",
+ "margin-right"
+ ).toLowerCase();
+ align = marginLeft + " " + marginRight;
+ gDialog.leftAlign.checked = align == "left left" || align == "0px auto";
+ gDialog.centerAlign.checked =
+ align == "center center" || align == "auto auto" || align == " ";
+ gDialog.rightAlign.checked = align == "right right" || align == "auto 0px";
+
+ if (gDialog.centerAlign.checked) {
+ gDialog.alignGroup.selectedItem = gDialog.centerAlign;
+ } else if (gDialog.rightAlign.checked) {
+ gDialog.alignGroup.selectedItem = gDialog.rightAlign;
+ } else {
+ gDialog.alignGroup.selectedItem = gDialog.leftAlign;
+ }
+
+ gDialog.shading.checked = !globalElement.hasAttribute("noshade");
+}
+
+function onSaveDefault() {
+ // "false" means set attributes on the globalElement,
+ // not the real element being edited
+ if (ValidateData()) {
+ var alignInt;
+ if (align == "left") {
+ alignInt = 0;
+ } else if (align == "right") {
+ alignInt = 2;
+ } else {
+ alignInt = 1;
+ }
+ Services.prefs.setIntPref("editor.hrule.align", alignInt);
+
+ var percent;
+ var widthInt;
+ var heightInt;
+
+ if (width) {
+ if (width.includes("%")) {
+ percent = true;
+ widthInt = Number(width.substr(0, width.indexOf("%")));
+ } else {
+ percent = false;
+ widthInt = Number(width);
+ }
+ } else {
+ percent = true;
+ widthInt = Number(100);
+ }
+
+ heightInt = height ? Number(height) : 2;
+
+ Services.prefs.setIntPref("editor.hrule.width", widthInt);
+ Services.prefs.setBoolPref("editor.hrule.width_percent", percent);
+ Services.prefs.setIntPref("editor.hrule.height", heightInt);
+ Services.prefs.setBoolPref("editor.hrule.shading", shading);
+
+ // Write the prefs out NOW!
+ Services.prefs.savePrefFile(null);
+ }
+}
+
+// Get and validate data from widgets.
+// Set attributes on globalElement so they can be accessed by AdvancedEdit()
+function ValidateData() {
+ // Height is always pixels
+ height = ValidateNumber(
+ gDialog.heightInput,
+ null,
+ 1,
+ gMaxHRSize,
+ globalElement,
+ "size",
+ false
+ );
+ if (gValidationError) {
+ return false;
+ }
+
+ width = ValidateNumber(
+ gDialog.widthInput,
+ gDialog.pixelOrPercentMenulist,
+ 1,
+ gMaxPixels,
+ globalElement,
+ "width",
+ false
+ );
+ if (gValidationError) {
+ return false;
+ }
+
+ align = "left";
+ if (gDialog.centerAlign.selected) {
+ // Don't write out default attribute
+ align = "";
+ } else if (gDialog.rightAlign.selected) {
+ align = "right";
+ }
+ if (align) {
+ globalElement.setAttribute("align", align);
+ } else {
+ try {
+ GetCurrentEditor().removeAttributeOrEquivalent(
+ globalElement,
+ "align",
+ true
+ );
+ } catch (e) {}
+ }
+
+ if (gDialog.shading.checked) {
+ shading = true;
+ globalElement.removeAttribute("noshade");
+ } else {
+ shading = false;
+ globalElement.setAttribute("noshade", "noshade");
+ }
+ return true;
+}
+
+function onAccept(event) {
+ if (ValidateData()) {
+ // Copy attributes from the globalElement to the document element
+ try {
+ GetCurrentEditor().cloneAttributes(gHLineElement, globalElement);
+ } catch (e) {}
+ return;
+ }
+ event.preventDefault();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdHLineProps.xhtml b/comm/suite/editor/components/dialogs/content/EdHLineProps.xhtml
new file mode 100644
index 0000000000..fbcfa3b594
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdHLineProps.xhtml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+%edHLineProperties;
+
+%edDialogOverlay;
+]>
+
+
diff --git a/comm/suite/editor/components/dialogs/content/EdImageDialog.js b/comm/suite/editor/components/dialogs/content/EdImageDialog.js
new file mode 100644
index 0000000000..0e697dbd6f
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdImageDialog.js
@@ -0,0 +1,661 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ Note: We encourage non-empty alt text for images inserted into a page.
+ When there's no alt text, we always write 'alt=""' as the attribute, since "alt" is a required attribute.
+ We allow users to not have alt text by checking a "Don't use alterate text" radio button,
+ and we don't accept spaces as valid alt text. A space used to be required to avoid the error message
+ if user didn't enter alt text, but is unnecessary now that we no longer annoy the user
+ with the error dialog if alt="" is present on an img element.
+ We trim all spaces at the beginning and end of user's alt text
+*/
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var gInsertNewImage = true;
+var gDoAltTextError = false;
+var gConstrainOn = false;
+// Note used in current version, but these are set correctly
+// and could be used to reset width and height used for constrain ratio
+var gConstrainWidth = 0;
+var gConstrainHeight = 0;
+var imageElement;
+var gImageMap = 0;
+var gCanRemoveImageMap = false;
+var gRemoveImageMap = false;
+var gImageMapDisabled = false;
+var gActualWidth = "";
+var gActualHeight = "";
+var gOriginalSrc = "";
+var gTimerID;
+var gValidateTab;
+var gInsertNewIMap;
+
+// These must correspond to values in EditorDialog.css for each theme
+// (unfortunately, setting "style" attribute here doesn't work!)
+var gPreviewImageWidth = 80;
+var gPreviewImageHeight = 50;
+
+// dialog initialization code
+
+function ImageStartup() {
+ gDialog.tabBox = document.getElementById("TabBox");
+ gDialog.tabLocation = document.getElementById("imageLocationTab");
+ gDialog.tabDimensions = document.getElementById("imageDimensionsTab");
+ gDialog.tabBorder = document.getElementById("imageBorderTab");
+ gDialog.srcInput = document.getElementById("srcInput");
+ gDialog.titleInput = document.getElementById("titleInput");
+ gDialog.altTextInput = document.getElementById("altTextInput");
+ gDialog.altTextRadioGroup = document.getElementById("altTextRadioGroup");
+ gDialog.altTextRadio = document.getElementById("altTextRadio");
+ gDialog.noAltTextRadio = document.getElementById("noAltTextRadio");
+ gDialog.actualSizeRadio = document.getElementById("actualSizeRadio");
+ gDialog.constrainCheckbox = document.getElementById("constrainCheckbox");
+ gDialog.widthInput = document.getElementById("widthInput");
+ gDialog.heightInput = document.getElementById("heightInput");
+ gDialog.widthUnitsMenulist = document.getElementById("widthUnitsMenulist");
+ gDialog.heightUnitsMenulist = document.getElementById("heightUnitsMenulist");
+ gDialog.imagelrInput = document.getElementById("imageleftrightInput");
+ gDialog.imagetbInput = document.getElementById("imagetopbottomInput");
+ gDialog.border = document.getElementById("border");
+ gDialog.alignTypeSelect = document.getElementById("alignTypeSelect");
+ gDialog.ImageHolder = document.getElementById("preview-image-holder");
+ gDialog.PreviewWidth = document.getElementById("PreviewWidth");
+ gDialog.PreviewHeight = document.getElementById("PreviewHeight");
+ gDialog.PreviewSize = document.getElementById("PreviewSize");
+ gDialog.PreviewImage = null;
+ gDialog.OkButton = document.documentElement.getButton("accept");
+}
+
+// Set dialog widgets with attribute data
+// We get them from globalElement copy so this can be used
+// by AdvancedEdit(), which is shared by all property dialogs
+function InitImage() {
+ // Set the controls to the image's attributes
+ var src = globalElement.getAttribute("src");
+
+ // For image insertion the 'src' attribute is null.
+ if (src) {
+ // Shorten data URIs for display.
+ shortenImageData(src, gDialog.srcInput);
+ }
+
+ // Set "Relativize" checkbox according to current URL state
+ SetRelativeCheckbox();
+
+ // Force loading of image from its source and show preview image
+ LoadPreviewImage();
+
+ gDialog.titleInput.value = globalElement.getAttribute("title");
+
+ var hasAltText = globalElement.hasAttribute("alt");
+ var altText = globalElement.getAttribute("alt");
+ gDialog.altTextInput.value = altText;
+ if (altText || (!hasAltText && globalElement.hasAttribute("src"))) {
+ gDialog.altTextRadioGroup.selectedItem = gDialog.altTextRadio;
+ } else if (hasAltText) {
+ gDialog.altTextRadioGroup.selectedItem = gDialog.noAltTextRadio;
+ }
+ SetAltTextDisabled(
+ gDialog.altTextRadioGroup.selectedItem == gDialog.noAltTextRadio
+ );
+
+ // setup the height and width widgets
+ var width = InitPixelOrPercentMenulist(
+ globalElement,
+ gInsertNewImage ? null : imageElement,
+ "width",
+ "widthUnitsMenulist",
+ gPixel
+ );
+ var height = InitPixelOrPercentMenulist(
+ globalElement,
+ gInsertNewImage ? null : imageElement,
+ "height",
+ "heightUnitsMenulist",
+ gPixel
+ );
+
+ // Set actual radio button if both set values are the same as actual
+ SetSizeWidgets(width, height);
+
+ gDialog.widthInput.value = gConstrainWidth = width || gActualWidth || "";
+ gDialog.heightInput.value = gConstrainHeight = height || gActualHeight || "";
+
+ // set spacing editfields
+ gDialog.imagelrInput.value = globalElement.getAttribute("hspace");
+ gDialog.imagetbInput.value = globalElement.getAttribute("vspace");
+
+ // dialog.border.value = globalElement.getAttribute("border");
+ var bv = GetHTMLOrCSSStyleValue(globalElement, "border", "border-top-width");
+ if (bv.includes("px")) {
+ // Strip out the px
+ bv = bv.substr(0, bv.indexOf("px"));
+ } else if (bv == "thin") {
+ bv = "1";
+ } else if (bv == "medium") {
+ bv = "3";
+ } else if (bv == "thick") {
+ bv = "5";
+ }
+ gDialog.border.value = bv;
+
+ // Get alignment setting
+ var align = globalElement.getAttribute("align");
+ if (align) {
+ align = align.toLowerCase();
+ }
+
+ switch (align) {
+ case "top":
+ case "middle":
+ case "right":
+ case "left":
+ gDialog.alignTypeSelect.value = align;
+ break;
+ default:
+ // Default or "bottom"
+ gDialog.alignTypeSelect.value = "bottom";
+ }
+
+ // Get image map for image
+ gImageMap = GetImageMap();
+
+ doOverallEnabling();
+ doDimensionEnabling();
+}
+
+function SetSizeWidgets(width, height) {
+ if (
+ !(width || height) ||
+ (gActualWidth &&
+ gActualHeight &&
+ width == gActualWidth &&
+ height == gActualHeight)
+ ) {
+ gDialog.actualSizeRadio.radioGroup.selectedItem = gDialog.actualSizeRadio;
+ }
+
+ if (!gDialog.actualSizeRadio.selected) {
+ // Decide if user's sizes are in the same ratio as actual sizes
+ if (gActualWidth && gActualHeight) {
+ if (gActualWidth > gActualHeight) {
+ gDialog.constrainCheckbox.checked =
+ Math.round((gActualHeight * width) / gActualWidth) == height;
+ } else {
+ gDialog.constrainCheckbox.checked =
+ Math.round((gActualWidth * height) / gActualHeight) == width;
+ }
+ }
+ }
+}
+
+// Disable alt text input when "Don't use alt" radio is checked
+function SetAltTextDisabled(disable) {
+ gDialog.altTextInput.disabled = disable;
+}
+
+function GetImageMap() {
+ var usemap = globalElement.getAttribute("usemap");
+ if (usemap) {
+ gCanRemoveImageMap = true;
+ let mapname = usemap.substr(1);
+ try {
+ return GetCurrentEditor().document.querySelector(
+ '[name="' + mapname + '"]'
+ );
+ } catch (e) {}
+ } else {
+ gCanRemoveImageMap = false;
+ }
+
+ return null;
+}
+
+function chooseFile() {
+ if (gTimerID) {
+ clearTimeout(gTimerID);
+ }
+
+ // Put focus into the input field
+ SetTextboxFocus(gDialog.srcInput);
+
+ GetLocalFileURL("img").then(fileURL => {
+ // Always try to relativize local file URLs
+ if (gHaveDocumentUrl) {
+ fileURL = MakeRelativeUrl(fileURL);
+ }
+
+ gDialog.srcInput.value = fileURL;
+
+ SetRelativeCheckbox();
+ doOverallEnabling();
+ LoadPreviewImage();
+ });
+}
+
+function PreviewImageLoaded() {
+ if (gDialog.PreviewImage) {
+ // Image loading has completed -- we can get actual width
+ gActualWidth = gDialog.PreviewImage.naturalWidth;
+ gActualHeight = gDialog.PreviewImage.naturalHeight;
+
+ if (gActualWidth && gActualHeight) {
+ // Use actual size or scale to fit preview if either dimension is too large
+ var width = gActualWidth;
+ var height = gActualHeight;
+ if (gActualWidth > gPreviewImageWidth) {
+ width = gPreviewImageWidth;
+ height = gActualHeight * (gPreviewImageWidth / gActualWidth);
+ }
+ if (height > gPreviewImageHeight) {
+ height = gPreviewImageHeight;
+ width = gActualWidth * (gPreviewImageHeight / gActualHeight);
+ }
+ gDialog.PreviewImage.width = width;
+ gDialog.PreviewImage.height = height;
+
+ gDialog.PreviewWidth.setAttribute("value", gActualWidth);
+ gDialog.PreviewHeight.setAttribute("value", gActualHeight);
+
+ gDialog.PreviewSize.collapsed = false;
+ gDialog.ImageHolder.collapsed = false;
+
+ SetSizeWidgets(gDialog.widthInput.value, gDialog.heightInput.value);
+ }
+
+ if (gDialog.actualSizeRadio.selected) {
+ SetActualSize();
+ }
+ }
+}
+
+function LoadPreviewImage() {
+ gDialog.PreviewSize.collapsed = true;
+ // XXXbz workaround for bug 265416 / bug 266284
+ gDialog.ImageHolder.collapsed = true;
+
+ var imageSrc = TrimString(gDialog.srcInput.value);
+ if (!imageSrc) {
+ return;
+ }
+ if (isImageDataShortened(imageSrc)) {
+ imageSrc = restoredImageData(gDialog.srcInput);
+ }
+
+ try {
+ // Remove the image URL from image cache so it loads fresh
+ // (if we don't do this, loads after the first will always use image cache
+ // and we won't see image edit changes or be able to get actual width and height)
+
+ // We must have an absolute URL to preview it or remove it from the cache
+ imageSrc = MakeAbsoluteUrl(imageSrc);
+
+ if (GetScheme(imageSrc)) {
+ let uri = Services.io.newURI(imageSrc);
+ if (uri) {
+ let imgCache = Cc["@mozilla.org/image/cache;1"].getService(
+ Ci.imgICache
+ );
+
+ // This returns error if image wasn't in the cache; ignore that
+ imgCache.removeEntry(uri);
+ }
+ }
+ } catch (e) {}
+
+ if (gDialog.PreviewImage) {
+ removeEventListener("load", PreviewImageLoaded, true);
+ }
+
+ if (gDialog.ImageHolder.hasChildNodes()) {
+ gDialog.ImageHolder.firstChild.remove();
+ }
+
+ gDialog.PreviewImage = document.createElementNS(
+ "http://www.w3.org/1999/xhtml",
+ "img"
+ );
+ if (gDialog.PreviewImage) {
+ // set the src before appending to the document -- see bug 198435 for why
+ // this is needed.
+ // XXXbz that bug is long-since fixed. Is this still needed?
+ gDialog.PreviewImage.addEventListener("load", PreviewImageLoaded, true);
+ gDialog.PreviewImage.src = imageSrc;
+ gDialog.ImageHolder.appendChild(gDialog.PreviewImage);
+ }
+}
+
+function SetActualSize() {
+ gDialog.widthInput.value = gActualWidth ? gActualWidth : "";
+ gDialog.widthUnitsMenulist.selectedIndex = 0;
+ gDialog.heightInput.value = gActualHeight ? gActualHeight : "";
+ gDialog.heightUnitsMenulist.selectedIndex = 0;
+ doDimensionEnabling();
+}
+
+function ChangeImageSrc() {
+ if (gTimerID) {
+ clearTimeout(gTimerID);
+ }
+
+ gTimerID = setTimeout(LoadPreviewImage, 800);
+
+ SetRelativeCheckbox();
+ doOverallEnabling();
+}
+
+function doDimensionEnabling() {
+ // Enabled unless "Actual Size" is selected
+ var enable = !gDialog.actualSizeRadio.selected;
+
+ // BUG 74145: After input field is disabled,
+ // setting it enabled causes blinking caret to appear
+ // even though focus isn't set to it.
+ SetElementEnabledById("heightInput", enable);
+ SetElementEnabledById("heightLabel", enable);
+ SetElementEnabledById("heightUnitsMenulist", enable);
+
+ SetElementEnabledById("widthInput", enable);
+ SetElementEnabledById("widthLabel", enable);
+ SetElementEnabledById("widthUnitsMenulist", enable);
+
+ var constrainEnable =
+ enable &&
+ gDialog.widthUnitsMenulist.selectedIndex == 0 &&
+ gDialog.heightUnitsMenulist.selectedIndex == 0;
+
+ SetElementEnabledById("constrainCheckbox", constrainEnable);
+}
+
+function doOverallEnabling() {
+ var enabled = TrimString(gDialog.srcInput.value) != "";
+
+ SetElementEnabled(gDialog.OkButton, enabled);
+ SetElementEnabledById("AdvancedEditButton1", enabled);
+ SetElementEnabledById("imagemapLabel", enabled);
+ SetElementEnabledById("removeImageMap", gCanRemoveImageMap);
+}
+
+function ToggleConstrain() {
+ // If just turned on, save the current width and height as basis for constrain ratio
+ // Thus clicking on/off lets user say "Use these values as aspect ration"
+ if (
+ gDialog.constrainCheckbox.checked &&
+ !gDialog.constrainCheckbox.disabled &&
+ gDialog.widthUnitsMenulist.selectedIndex == 0 &&
+ gDialog.heightUnitsMenulist.selectedIndex == 0
+ ) {
+ gConstrainWidth = Number(TrimString(gDialog.widthInput.value));
+ gConstrainHeight = Number(TrimString(gDialog.heightInput.value));
+ }
+}
+
+function constrainProportions(srcID, destID) {
+ var srcElement = document.getElementById(srcID);
+ if (!srcElement) {
+ return;
+ }
+
+ var destElement = document.getElementById(destID);
+ if (!destElement) {
+ return;
+ }
+
+ // always force an integer (whether we are constraining or not)
+ forceInteger(srcID);
+
+ if (
+ !gActualWidth ||
+ !gActualHeight ||
+ !(gDialog.constrainCheckbox.checked && !gDialog.constrainCheckbox.disabled)
+ ) {
+ return;
+ }
+
+ // double-check that neither width nor height is in percent mode; bail if so!
+ if (
+ gDialog.widthUnitsMenulist.selectedIndex != 0 ||
+ gDialog.heightUnitsMenulist.selectedIndex != 0
+ ) {
+ return;
+ }
+
+ // This always uses the actual width and height ratios
+ // which is kind of funky if you change one number without the constrain
+ // and then turn constrain on and change a number
+ // I prefer the old strategy (below) but I can see some merit to this solution
+ if (srcID == "widthInput") {
+ destElement.value = Math.round(
+ (srcElement.value * gActualHeight) / gActualWidth
+ );
+ } else {
+ destElement.value = Math.round(
+ (srcElement.value * gActualWidth) / gActualHeight
+ );
+ }
+
+ /*
+ // With this strategy, the width and height ratio
+ // can be reset to whatever the user entered.
+ if (srcID == "widthInput") {
+ destElement.value = Math.round( srcElement.value * gConstrainHeight / gConstrainWidth );
+ } else {
+ destElement.value = Math.round( srcElement.value * gConstrainWidth / gConstrainHeight );
+ }
+ */
+}
+
+function removeImageMap() {
+ gRemoveImageMap = true;
+ gCanRemoveImageMap = false;
+ SetElementEnabledById("removeImageMap", false);
+}
+
+function SwitchToValidatePanel() {
+ if (
+ gDialog.tabBox &&
+ gValidateTab &&
+ gDialog.tabBox.selectedTab != gValidateTab
+ ) {
+ gDialog.tabBox.selectedTab = gValidateTab;
+ }
+}
+
+// Get data from widgets, validate, and set for the global element
+// accessible to AdvancedEdit() [in EdDialogCommon.js]
+function ValidateImage() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ return false;
+ }
+
+ gValidateTab = gDialog.tabLocation;
+ if (!gDialog.srcInput.value) {
+ Services.prompt.alert(
+ window,
+ GetString("Alert"),
+ GetString("MissingImageError")
+ );
+ SwitchToValidatePanel();
+ gDialog.srcInput.focus();
+ return false;
+ }
+
+ // We must convert to "file:///" or "http://" format else image doesn't load!
+ let src = gDialog.srcInput.value.trim();
+
+ if (isImageDataShortened(src)) {
+ src = restoredImageData(gDialog.srcInput);
+ } else {
+ var checkbox = document.getElementById("MakeRelativeCheckbox");
+ try {
+ if (checkbox && !checkbox.checked) {
+ src = Services.uriFixup.getFixupURIInfo(
+ src,
+ Ci.nsIURIFixup.FIXUP_FLAG_NONE
+ ).preferredURI.spec;
+ }
+ } catch (e) {}
+
+ globalElement.setAttribute("src", src);
+ }
+
+ let title = gDialog.titleInput.value.trim();
+ if (title) {
+ globalElement.setAttribute("title", title);
+ } else {
+ globalElement.removeAttribute("title");
+ }
+
+ // Force user to enter Alt text only if "Alternate text" radio is checked
+ // Don't allow just spaces in alt text
+ var alt = "";
+ var useAlt = gDialog.altTextRadioGroup.selectedItem == gDialog.altTextRadio;
+ if (useAlt) {
+ alt = TrimString(gDialog.altTextInput.value);
+ }
+
+ if (alt || !useAlt) {
+ globalElement.setAttribute("alt", alt);
+ } else if (!gDoAltTextError) {
+ globalElement.removeAttribute("alt");
+ } else {
+ Services.prompt.alert(window, GetString("Alert"), GetString("NoAltText"));
+ SwitchToValidatePanel();
+ gDialog.altTextInput.focus();
+ return false;
+ }
+
+ var width = "";
+ var height = "";
+
+ gValidateTab = gDialog.tabDimensions;
+ if (!gDialog.actualSizeRadio.selected) {
+ // Get user values for width and height
+ width = ValidateNumber(
+ gDialog.widthInput,
+ gDialog.widthUnitsMenulist,
+ 1,
+ gMaxPixels,
+ globalElement,
+ "width",
+ false,
+ true
+ );
+ if (gValidationError) {
+ return false;
+ }
+
+ height = ValidateNumber(
+ gDialog.heightInput,
+ gDialog.heightUnitsMenulist,
+ 1,
+ gMaxPixels,
+ globalElement,
+ "height",
+ false,
+ true
+ );
+ if (gValidationError) {
+ return false;
+ }
+ }
+
+ // We always set the width and height attributes, even if same as actual.
+ // This speeds up layout of pages since sizes are known before image is loaded
+ if (!width) {
+ width = gActualWidth;
+ }
+ if (!height) {
+ height = gActualHeight;
+ }
+
+ // Remove existing width and height only if source changed
+ // and we couldn't obtain actual dimensions
+ var srcChanged = src != gOriginalSrc;
+ if (width) {
+ editor.setAttributeOrEquivalent(globalElement, "width", width, true);
+ } else if (srcChanged) {
+ editor.removeAttributeOrEquivalent(globalElement, "width", true);
+ }
+
+ if (height) {
+ editor.setAttributeOrEquivalent(globalElement, "height", height, true);
+ } else if (srcChanged) {
+ editor.removeAttributeOrEquivalent(globalElement, "height", true);
+ }
+
+ // spacing attributes
+ gValidateTab = gDialog.tabBorder;
+ ValidateNumber(
+ gDialog.imagelrInput,
+ null,
+ 0,
+ gMaxPixels,
+ globalElement,
+ "hspace",
+ false,
+ true,
+ true
+ );
+ if (gValidationError) {
+ return false;
+ }
+
+ ValidateNumber(
+ gDialog.imagetbInput,
+ null,
+ 0,
+ gMaxPixels,
+ globalElement,
+ "vspace",
+ false,
+ true
+ );
+ if (gValidationError) {
+ return false;
+ }
+
+ // note this is deprecated and should be converted to stylesheets
+ ValidateNumber(
+ gDialog.border,
+ null,
+ 0,
+ gMaxPixels,
+ globalElement,
+ "border",
+ false,
+ true
+ );
+ if (gValidationError) {
+ return false;
+ }
+
+ // Default or setting "bottom" means don't set the attribute
+ // Note that the attributes "left" and "right" are opposite
+ // of what we use in the UI, which describes where the TEXT wraps,
+ // not the image location (which is what the HTML describes)
+ switch (gDialog.alignTypeSelect.value) {
+ case "top":
+ case "middle":
+ case "right":
+ case "left":
+ editor.setAttributeOrEquivalent(
+ globalElement,
+ "align",
+ gDialog.alignTypeSelect.value,
+ true
+ );
+ break;
+ default:
+ try {
+ editor.removeAttributeOrEquivalent(globalElement, "align", true);
+ } catch (e) {}
+ }
+
+ return true;
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdImageLinkLoader.js b/comm/suite/editor/components/dialogs/content/EdImageLinkLoader.js
new file mode 100755
index 0000000000..5b88a5703f
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdImageLinkLoader.js
@@ -0,0 +1,145 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+var gMsgCompProcessLink = false;
+var gMsgCompInputElement = null;
+var gMsgCompPrevInputValue = null;
+var gMsgCompPrevMozDoNotSendAttribute;
+var gMsgCompAttachSourceElement = null;
+
+function OnLoadDialog() {
+ gMsgCompAttachSourceElement = document.getElementById("AttachSourceToMail");
+ var editor = GetCurrentEditor();
+ if (
+ gMsgCompAttachSourceElement &&
+ editor &&
+ editor.flags & Ci.nsIEditor.eEditorMailMask
+ ) {
+ SetRelativeCheckbox = function() {
+ SetAttachCheckbox();
+ };
+ // initialize the AttachSourceToMail checkbox
+ gMsgCompAttachSourceElement.hidden = false;
+
+ switch (document.documentElement.id) {
+ case "imageDlg":
+ gMsgCompInputElement = gDialog.srcInput;
+ gMsgCompProcessLink = false;
+ break;
+ case "linkDlg":
+ gMsgCompInputElement = gDialog.hrefInput;
+ gMsgCompProcessLink = true;
+ break;
+ }
+ if (gMsgCompInputElement) {
+ SetAttachCheckbox();
+ gMsgCompPrevMozDoNotSendAttribute = globalElement.getAttribute(
+ "moz-do-not-send"
+ );
+ }
+ }
+}
+addEventListener("load", OnLoadDialog, false);
+
+function OnAcceptDialog() {
+ // Auto-convert file URLs to data URLs. If we're in the link properties
+ // dialog convert only when requested - for the image dialog do it always.
+ if (gMsgCompInputElement &&
+ /^file:/i.test(gMsgCompInputElement.value.trim()) &&
+ (gMsgCompAttachSourceElement.checked || !gMsgCompProcessLink)) {
+ var dataURI = GenerateDataURL(gMsgCompInputElement.value.trim());
+ gMsgCompInputElement.value = dataURI;
+ gMsgCompAttachSourceElement.checked = true;
+ }
+ DoAttachSourceCheckbox();
+}
+document.addEventListener("dialogaccept", OnAcceptDialog, true);
+
+function SetAttachCheckbox() {
+ var resetCheckbox = false;
+ var mozDoNotSend = globalElement.getAttribute("moz-do-not-send");
+
+ // In case somebody played with the advanced property and changed the moz-do-not-send attribute
+ if (mozDoNotSend != gMsgCompPrevMozDoNotSendAttribute) {
+ gMsgCompPrevMozDoNotSendAttribute = mozDoNotSend;
+ resetCheckbox = true;
+ }
+
+ // Has the URL changed
+ if (
+ gMsgCompInputElement &&
+ gMsgCompInputElement.value != gMsgCompPrevInputValue
+ ) {
+ gMsgCompPrevInputValue = gMsgCompInputElement.value;
+ resetCheckbox = true;
+ }
+
+ if (gMsgCompInputElement && resetCheckbox) {
+ // Here is the rule about how to set the checkbox Attach Source To Message:
+ // If the attribute "moz-do-not-send" has not been set, we look at the scheme of the URL
+ // and at some preference to decide what is the best for the user.
+ // If it is set to "false", the checkbox is checked, otherwise unchecked.
+ var attach = false;
+ if (mozDoNotSend == null) {
+ // We haven't yet set the "moz-do-not-send" attribute.
+ var inputValue = gMsgCompInputElement.value.trim();
+ if (/^(file|data):/i.test(inputValue)) {
+ // For files or data URLs, default to attach them.
+ attach = true;
+ } else if (
+ !gMsgCompProcessLink && // Implies image dialogue.
+ /^https?:/i.test(inputValue)
+ ) {
+ // For images loaded via http(s) we default to the preference value.
+ attach = Services.prefs.getBoolPref("mail.compose.attach_http_images");
+ }
+ } else {
+ attach = mozDoNotSend == "false";
+ }
+
+ gMsgCompAttachSourceElement.checked = attach;
+ }
+}
+
+function DoAttachSourceCheckbox() {
+ gMsgCompPrevMozDoNotSendAttribute = (!gMsgCompAttachSourceElement.checked).toString();
+ globalElement.setAttribute(
+ "moz-do-not-send",
+ gMsgCompPrevMozDoNotSendAttribute
+ );
+}
+
+function GenerateDataURL(url) {
+ var file = Services.io.newURI(url).QueryInterface(Ci.nsIFileURL).file;
+ var contentType = Cc["@mozilla.org/mime;1"]
+ .getService(Ci.nsIMIMEService)
+ .getTypeFromFile(file);
+ var inputStream = Cc[
+ "@mozilla.org/network/file-input-stream;1"
+ ].createInstance(Ci.nsIFileInputStream);
+ inputStream.init(file, 0x01, 0o600, 0);
+ var stream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(
+ Ci.nsIBinaryInputStream
+ );
+ stream.setInputStream(inputStream);
+ let data = "";
+ while (stream.available() > 0) {
+ data += stream.readBytes(stream.available());
+ }
+ let encoded = btoa(data);
+ stream.close();
+ return (
+ "data:" +
+ contentType +
+ ";filename=" +
+ encodeURIComponent(file.leafName) +
+ ";base64," +
+ encoded
+ );
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdImageProps.js b/comm/suite/editor/components/dialogs/content/EdImageProps.js
new file mode 100644
index 0000000000..5b73488dcb
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdImageProps.js
@@ -0,0 +1,293 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+/* import-globals-from EdImageDialog.js */
+
+var gAnchorElement = null;
+var gLinkElement = null;
+var gOriginalHref = "";
+var gHNodeArray = {};
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ window.close();
+ return;
+ }
+
+ ImageStartup();
+ gDialog.hrefInput = document.getElementById("hrefInput");
+ gDialog.makeRelativeLink = document.getElementById("MakeRelativeLink");
+ gDialog.showLinkBorder = document.getElementById("showLinkBorder");
+ gDialog.linkTab = document.getElementById("imageLinkTab");
+ gDialog.linkAdvanced = document.getElementById("LinkAdvancedEditButton");
+
+ // Get a single selected image element
+ var tagName = "img";
+ if ("arguments" in window && window.arguments[0]) {
+ imageElement = window.arguments[0];
+ // We've been called from form field properties, so we can't insert a link
+ gDialog.linkTab.remove();
+ gDialog.linkTab = null;
+ } else {
+ // First check for
+ try {
+ imageElement = editor.getSelectedElement("input");
+
+ if (!imageElement || imageElement.getAttribute("type") != "image") {
+ // Get a single selected image element
+ imageElement = editor.getSelectedElement(tagName);
+ if (imageElement) {
+ gAnchorElement = editor.getElementOrParentByTagName(
+ "href",
+ imageElement
+ );
+ }
+ }
+ } catch (e) {}
+ }
+
+ if (imageElement) {
+ // We found an element and don't need to insert one
+ if (imageElement.hasAttribute("src")) {
+ gInsertNewImage = false;
+ gActualWidth = imageElement.naturalWidth;
+ gActualHeight = imageElement.naturalHeight;
+ }
+ } else {
+ gInsertNewImage = true;
+
+ // We don't have an element selected,
+ // so create one with default attributes
+ try {
+ imageElement = editor.createElementWithDefaults(tagName);
+ } catch (e) {}
+
+ if (!imageElement) {
+ dump("Failed to get selected element or create a new one!\n");
+ window.close();
+ return;
+ }
+ try {
+ gAnchorElement = editor.getSelectedElement("href");
+ } catch (e) {}
+ }
+
+ // Make a copy to use for AdvancedEdit
+ globalElement = imageElement.cloneNode(false);
+
+ // We only need to test for this once per dialog load
+ gHaveDocumentUrl = GetDocumentBaseUrl();
+
+ InitDialog();
+ if (gAnchorElement) {
+ gOriginalHref = gAnchorElement.getAttribute("href");
+ // Make a copy to use for AdvancedEdit
+ gLinkElement = gAnchorElement.cloneNode(false);
+ } else {
+ gLinkElement = editor.createElementWithDefaults("a");
+ }
+ gDialog.hrefInput.value = gOriginalHref;
+
+ FillLinkMenulist(gDialog.hrefInput, gHNodeArray);
+ ChangeLinkLocation();
+
+ // Save initial source URL
+ gOriginalSrc = gDialog.srcInput.value;
+
+ // By default turn constrain on, but both width and height must be in pixels
+ gDialog.constrainCheckbox.checked =
+ gDialog.widthUnitsMenulist.selectedIndex == 0 &&
+ gDialog.heightUnitsMenulist.selectedIndex == 0;
+
+ // Start in "Link" tab if 2nd argument is true
+ if (gDialog.linkTab && "arguments" in window && window.arguments[1]) {
+ document.getElementById("TabBox").selectedTab = gDialog.linkTab;
+ SetTextboxFocus(gDialog.hrefInput);
+ } else {
+ SetTextboxFocus(gDialog.srcInput);
+ }
+
+ SetWindowLocation();
+}
+
+// Set dialog widgets with attribute data
+// We get them from globalElement copy so this can be used
+// by AdvancedEdit(), which is shared by all property dialogs
+function InitDialog() {
+ InitImage();
+ var border = TrimString(gDialog.border.value);
+ gDialog.showLinkBorder.checked = border != "" && border > 0;
+}
+
+function ChangeLinkLocation() {
+ var href = TrimString(gDialog.hrefInput.value);
+ SetRelativeCheckbox(gDialog.makeRelativeLink);
+ gDialog.showLinkBorder.disabled = !href;
+ gDialog.linkAdvanced.disabled = !href;
+ gLinkElement.setAttribute("href", href);
+}
+
+function ToggleShowLinkBorder() {
+ if (gDialog.showLinkBorder.checked) {
+ var border = TrimString(gDialog.border.value);
+ if (!border || border == "0") {
+ gDialog.border.value = "2";
+ }
+ } else {
+ gDialog.border.value = "0";
+ }
+}
+
+// Get data from widgets, validate, and set for the global element
+// accessible to AdvancedEdit() [in EdDialogCommon.js]
+function ValidateData() {
+ return ValidateImage();
+}
+
+function onAccept(event) {
+ // Use this now (default = false) so Advanced Edit button dialog doesn't trigger error message
+ gDoAltTextError = true;
+
+ if (ValidateData()) {
+ if ("arguments" in window && window.arguments[0]) {
+ SaveWindowLocation();
+ return;
+ }
+
+ var editor = GetCurrentEditor();
+
+ editor.beginTransaction();
+
+ try {
+ if (gRemoveImageMap) {
+ globalElement.removeAttribute("usemap");
+ if (gImageMap) {
+ editor.deleteNode(gImageMap);
+ gInsertNewIMap = true;
+ gImageMap = null;
+ }
+ } else if (gImageMap) {
+ // un-comment to see that inserting image maps does not work!
+ /*
+ gImageMap = editor.createElementWithDefaults("map");
+ gImageMap.setAttribute("name", "testing");
+ var testArea = editor.createElementWithDefaults("area");
+ testArea.setAttribute("shape", "circle");
+ testArea.setAttribute("coords", "86,102,52");
+ testArea.setAttribute("href", "test");
+ gImageMap.appendChild(testArea);
+ */
+
+ // Assign to map if there is one
+ var mapName = gImageMap.getAttribute("name");
+ if (mapName != "") {
+ globalElement.setAttribute("usemap", "#" + mapName);
+ if (globalElement.getAttribute("border") == "") {
+ globalElement.setAttribute("border", 0);
+ }
+ }
+ }
+
+ // Create or remove the link as appropriate
+ var href = gDialog.hrefInput.value;
+ if (href != gOriginalHref) {
+ if (href && !gInsertNewImage) {
+ EditorSetTextProperty("a", "href", href);
+ // gAnchorElement is needed for cloning attributes later.
+ if (!gAnchorElement) {
+ gAnchorElement = editor.getElementOrParentByTagName(
+ "href",
+ imageElement
+ );
+ }
+ } else {
+ EditorRemoveTextProperty("href", "");
+ }
+ }
+
+ // If inside a link, always write the 'border' attribute
+ if (href) {
+ if (gDialog.showLinkBorder.checked) {
+ // Use default = 2 if border attribute is empty
+ if (!globalElement.hasAttribute("border")) {
+ globalElement.setAttribute("border", "2");
+ }
+ } else {
+ globalElement.setAttribute("border", "0");
+ }
+ }
+
+ if (gInsertNewImage) {
+ if (href) {
+ gLinkElement.appendChild(imageElement);
+ editor.insertElementAtSelection(gLinkElement, true);
+ } else {
+ // 'true' means delete the selection before inserting
+ editor.insertElementAtSelection(imageElement, true);
+ }
+ }
+
+ // Check to see if the link was to a heading
+ // Do this last because it moves the caret (BAD!)
+ if (href in gHNodeArray) {
+ var anchorNode = editor.createElementWithDefaults("a");
+ if (anchorNode) {
+ anchorNode.name = href.substr(1);
+ // Remember to use editor method so it is undoable!
+ editor.insertNode(anchorNode, gHNodeArray[href], 0);
+ }
+ }
+ // All values are valid - copy to actual element in doc or
+ // element we just inserted
+ editor.cloneAttributes(imageElement, globalElement);
+ if (gAnchorElement) {
+ editor.cloneAttributes(gAnchorElement, gLinkElement);
+ }
+
+ // If document is empty, the map element won't insert,
+ // so always insert the image first
+ if (gImageMap && gInsertNewIMap) {
+ // Insert the ImageMap element at beginning of document
+ var body = editor.rootElement;
+ editor.setShouldTxnSetSelection(false);
+ editor.insertNode(gImageMap, body, 0);
+ editor.setShouldTxnSetSelection(true);
+ }
+ } catch (e) {
+ dump(e);
+ }
+
+ editor.endTransaction();
+
+ SaveWindowLocation();
+ return;
+ }
+
+ gDoAltTextError = false;
+
+ event.preventDefault();
+}
+
+function onLinkAdvancedEdit() {
+ window.AdvancedEditOK = false;
+ window.openDialog(
+ "chrome://editor/content/EdAdvancedEdit.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal,resizable=yes",
+ "",
+ gLinkElement
+ );
+ window.focus();
+ if (window.AdvancedEditOK) {
+ gDialog.hrefInput.value = gLinkElement.getAttribute("href");
+ }
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdImageProps.xhtml b/comm/suite/editor/components/dialogs/content/EdImageProps.xhtml
new file mode 100644
index 0000000000..11d7d03026
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdImageProps.xhtml
@@ -0,0 +1,116 @@
+
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+
+
+
+%edImageProperties;
+
+%composeEditorOverlayDTD;
+
+%edDialogOverlay;
+]>
+
+
+
diff --git a/comm/suite/editor/components/dialogs/content/EdInputImage.js b/comm/suite/editor/components/dialogs/content/EdInputImage.js
new file mode 100644
index 0000000000..556acc7b13
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdInputImage.js
@@ -0,0 +1,189 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+/* import-globals-from EdImageDialog.js */
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ window.close();
+ return;
+ }
+
+ gDialog = {
+ inputName: document.getElementById("InputName"),
+ inputDisabled: document.getElementById("InputDisabled"),
+ inputTabIndex: document.getElementById("InputTabIndex"),
+ };
+
+ ImageStartup();
+
+ // Get a single selected input element
+ var tagName = "input";
+ try {
+ imageElement = editor.getSelectedElement(tagName);
+ } catch (e) {}
+
+ if (imageElement) {
+ // We found an element and don't need to insert one
+ gInsertNewImage = false;
+ } else {
+ gInsertNewImage = true;
+
+ // We don't have an element selected,
+ // so create one with default attributes
+ try {
+ imageElement = editor.createElementWithDefaults(tagName);
+ } catch (e) {}
+
+ if (!imageElement) {
+ dump("Failed to get selected element or create a new one!\n");
+ window.close();
+ return;
+ }
+ var imgElement;
+ try {
+ imgElement = editor.getSelectedElement("img");
+ } catch (e) {}
+
+ if (imgElement) {
+ // We found an image element, convert it to an input type="image"
+ var attributes = [
+ "src",
+ "alt",
+ "width",
+ "height",
+ "hspace",
+ "vspace",
+ "border",
+ "align",
+ "usemap",
+ "ismap",
+ ];
+ for (let i in attributes) {
+ imageElement.setAttribute(
+ attributes[i],
+ imgElement.getAttribute(attributes[i])
+ );
+ }
+ }
+ }
+
+ // Make a copy to use for AdvancedEdit
+ globalElement = imageElement.cloneNode(false);
+
+ // We only need to test for this once per dialog load
+ gHaveDocumentUrl = GetDocumentBaseUrl();
+
+ InitDialog();
+
+ // Save initial source URL
+ gOriginalSrc = gDialog.srcInput.value;
+
+ // By default turn constrain on, but both width and height must be in pixels
+ gDialog.constrainCheckbox.checked =
+ gDialog.widthUnitsMenulist.selectedIndex == 0 &&
+ gDialog.heightUnitsMenulist.selectedIndex == 0;
+
+ SetTextboxFocus(gDialog.inputName);
+
+ SetWindowLocation();
+}
+
+function InitDialog() {
+ InitImage();
+ gDialog.inputName.value = globalElement.getAttribute("name");
+ gDialog.inputDisabled.setAttribute(
+ "checked",
+ globalElement.hasAttribute("disabled")
+ );
+ gDialog.inputTabIndex.value = globalElement.getAttribute("tabindex");
+}
+
+function ValidateData() {
+ if (!ValidateImage()) {
+ return false;
+ }
+ if (gDialog.inputName.value) {
+ globalElement.setAttribute("name", gDialog.inputName.value);
+ } else {
+ globalElement.removeAttribute("name");
+ }
+ if (gDialog.inputTabIndex.value) {
+ globalElement.setAttribute("tabindex", gDialog.inputTabIndex.value);
+ } else {
+ globalElement.removeAttribute("tabindex");
+ }
+ if (gDialog.inputDisabled.checked) {
+ globalElement.setAttribute("disabled", "");
+ } else {
+ globalElement.removeAttribute("disabled");
+ }
+ globalElement.setAttribute("type", "image");
+ return true;
+}
+
+function onAccept(event) {
+ // Show alt text error only once
+ // (we don't initialize doAltTextError=true
+ // so Advanced edit button dialog doesn't trigger that error message)
+ // Use this now (default = false) so Advanced Edit button dialog doesn't trigger error message
+ gDoAltTextError = true;
+
+ if (ValidateData()) {
+ var editor = GetCurrentEditor();
+ editor.beginTransaction();
+
+ try {
+ if (gRemoveImageMap) {
+ globalElement.removeAttribute("usemap");
+ if (gImageMap) {
+ editor.deleteNode(gImageMap);
+ gInsertNewIMap = true;
+ gImageMap = null;
+ }
+ } else if (gImageMap) {
+ // Assign to map if there is one
+ var mapName = gImageMap.getAttribute("name");
+ if (mapName != "") {
+ globalElement.setAttribute("usemap", "#" + mapName);
+ if (globalElement.getAttribute("border") == "") {
+ globalElement.setAttribute("border", 0);
+ }
+ }
+ }
+
+ if (gInsertNewImage) {
+ // 'true' means delete the selection before inserting
+ // in case were are converting an image to an input type="image"
+ editor.insertElementAtSelection(imageElement, true);
+ }
+ editor.cloneAttributes(imageElement, globalElement);
+
+ // If document is empty, the map element won't insert,
+ // so always insert the image element first
+ if (gImageMap && gInsertNewIMap) {
+ // Insert the ImageMap element at beginning of document
+ var body = editor.rootElement;
+ editor.setShouldTxnSetSelection(false);
+ editor.insertNode(gImageMap, body, 0);
+ editor.setShouldTxnSetSelection(true);
+ }
+ } catch (e) {}
+
+ editor.endTransaction();
+
+ SaveWindowLocation();
+
+ return;
+ }
+ event.preventDefault();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdInputImage.xhtml b/comm/suite/editor/components/dialogs/content/EdInputImage.xhtml
new file mode 100644
index 0000000000..d3fc8c8270
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdInputImage.xhtml
@@ -0,0 +1,104 @@
+
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+
+
+
+%edInputProperties;
+
+%edImageProperties;
+
+%composeEditorOverlayDTD;
+
+%edDialogOverlay;
+]>
+
+
diff --git a/comm/suite/editor/components/dialogs/content/EdInputProps.js b/comm/suite/editor/components/dialogs/content/EdInputProps.js
new file mode 100644
index 0000000000..a737e263c7
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdInputProps.js
@@ -0,0 +1,345 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var insertNew;
+var inputElement;
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ dump("Failed to get active editor!\n");
+ window.close();
+ return;
+ }
+
+ gDialog = {
+ accept: document.documentElement.getButton("accept"),
+ inputType: document.getElementById("InputType"),
+ inputNameDeck: document.getElementById("InputNameDeck"),
+ inputName: document.getElementById("InputName"),
+ inputValueDeck: document.getElementById("InputValueDeck"),
+ inputValue: document.getElementById("InputValue"),
+ inputDeck: document.getElementById("InputDeck"),
+ inputChecked: document.getElementById("InputChecked"),
+ inputSelected: document.getElementById("InputSelected"),
+ inputReadOnly: document.getElementById("InputReadOnly"),
+ inputDisabled: document.getElementById("InputDisabled"),
+ inputTabIndex: document.getElementById("InputTabIndex"),
+ inputAccessKey: document.getElementById("InputAccessKey"),
+ inputSize: document.getElementById("InputSize"),
+ inputMaxLength: document.getElementById("InputMaxLength"),
+ inputAccept: document.getElementById("InputAccept"),
+ MoreSection: document.getElementById("MoreSection"),
+ MoreFewerButton: document.getElementById("MoreFewerButton"),
+ AdvancedEditButton: document.getElementById("AdvancedEditButton"),
+ AdvancedEditDeck: document.getElementById("AdvancedEditDeck"),
+ };
+
+ // Get a single selected input element
+ const kTagName = "input";
+ try {
+ inputElement = editor.getSelectedElement(kTagName);
+ } catch (e) {}
+
+ if (inputElement) {
+ // We found an element and don't need to insert one
+ insertNew = false;
+ } else {
+ insertNew = true;
+
+ // We don't have an element selected,
+ // so create one with default attributes
+ try {
+ inputElement = editor.createElementWithDefaults(kTagName);
+ } catch (e) {}
+
+ if (!inputElement) {
+ dump("Failed to get selected element or create a new one!\n");
+ window.close();
+ return;
+ }
+
+ var imgElement = editor.getSelectedElement("img");
+ if (imgElement) {
+ // We found an image element, convert it to an input type="image"
+ inputElement.setAttribute("type", "image");
+
+ var attributes = [
+ "src",
+ "alt",
+ "width",
+ "height",
+ "hspace",
+ "vspace",
+ "border",
+ "align",
+ ];
+ for (let i in attributes) {
+ inputElement.setAttribute(
+ attributes[i],
+ imgElement.getAttribute(attributes[i])
+ );
+ }
+ } else {
+ inputElement.setAttribute("value", GetSelectionAsText());
+ }
+ }
+
+ // Make a copy to use for AdvancedEdit
+ globalElement = inputElement.cloneNode(false);
+
+ InitDialog();
+
+ InitMoreFewer();
+
+ gDialog.inputType.focus();
+
+ SetWindowLocation();
+}
+
+function InitDialog() {
+ var type = globalElement.getAttribute("type");
+ var index = 0;
+ switch (type) {
+ case "button":
+ index = 9;
+ break;
+ case "checkbox":
+ index = 2;
+ break;
+ case "file":
+ index = 6;
+ break;
+ case "hidden":
+ index = 7;
+ break;
+ case "image":
+ index = 8;
+ break;
+ case "password":
+ index = 1;
+ break;
+ case "radio":
+ index = 3;
+ break;
+ case "reset":
+ index = 5;
+ break;
+ case "submit":
+ index = 4;
+ break;
+ }
+ gDialog.inputType.selectedIndex = index;
+ gDialog.inputName.value = globalElement.getAttribute("name");
+ gDialog.inputValue.value = globalElement.getAttribute("value");
+ gDialog.inputChecked.setAttribute(
+ "checked",
+ globalElement.hasAttribute("checked")
+ );
+ gDialog.inputSelected.setAttribute(
+ "checked",
+ globalElement.hasAttribute("checked")
+ );
+ gDialog.inputReadOnly.setAttribute(
+ "checked",
+ globalElement.hasAttribute("readonly")
+ );
+ gDialog.inputDisabled.setAttribute(
+ "checked",
+ globalElement.hasAttribute("disabled")
+ );
+ gDialog.inputTabIndex.value = globalElement.getAttribute("tabindex");
+ gDialog.inputAccessKey.value = globalElement.getAttribute("accesskey");
+ gDialog.inputSize.value = globalElement.getAttribute("size");
+ gDialog.inputMaxLength.value = globalElement.getAttribute("maxlength");
+ gDialog.inputAccept.value = globalElement.getAttribute("accept");
+ SelectInputType();
+}
+
+function SelectInputType() {
+ var index = gDialog.inputType.selectedIndex;
+ gDialog.AdvancedEditDeck.setAttribute("selectedIndex", 0);
+ gDialog.inputNameDeck.setAttribute("selectedIndex", 0);
+ gDialog.inputValueDeck.setAttribute("selectedIndex", 0);
+ gDialog.inputValue.disabled = false;
+ gDialog.inputChecked.disabled = index != 2;
+ gDialog.inputSelected.disabled = index != 3;
+ gDialog.inputReadOnly.disabled = index > 1;
+ gDialog.inputTabIndex.disabled = index == 7;
+ gDialog.inputAccessKey.disabled = index == 7;
+ gDialog.inputSize.disabled = index > 1;
+ gDialog.inputMaxLength.disabled = index > 1;
+ gDialog.inputAccept.disabled = index != 6;
+ switch (index) {
+ case 0:
+ case 1:
+ gDialog.inputValueDeck.setAttribute("selectedIndex", 1);
+ gDialog.inputDeck.setAttribute("selectedIndex", 2);
+ break;
+ case 2:
+ gDialog.inputDeck.setAttribute("selectedIndex", 0);
+ break;
+ case 3:
+ gDialog.inputDeck.setAttribute("selectedIndex", 1);
+ gDialog.inputNameDeck.setAttribute("selectedIndex", 1);
+ break;
+ case 6:
+ gDialog.inputValue.disabled = true;
+ gDialog.inputAccept.disabled = false;
+ break;
+ case 8:
+ gDialog.inputValue.disabled = true;
+ gDialog.AdvancedEditDeck.setAttribute("selectedIndex", 1);
+ gDialog.inputName.removeEventListener("input", onInput);
+ break;
+ case 7:
+ gDialog.inputValueDeck.setAttribute("selectedIndex", 1);
+ break;
+ }
+ onInput();
+}
+
+function onInput() {
+ var disabled = false;
+ switch (gDialog.inputType.selectedIndex) {
+ case 3:
+ disabled = disabled || !gDialog.inputValue.value;
+ break;
+ case 4:
+ case 5:
+ break;
+ case 8:
+ disabled = !globalElement.hasAttribute("src");
+ break;
+ default:
+ disabled = !gDialog.inputName.value;
+ break;
+ }
+ if (gDialog.accept.disabled != disabled) {
+ gDialog.accept.disabled = disabled;
+ gDialog.AdvancedEditButton.disabled = disabled;
+ }
+}
+
+function doImageProperties() {
+ window.openDialog(
+ "chrome://editor/content/EdImageProps.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal",
+ globalElement
+ );
+ window.focus();
+ onInput();
+}
+
+function ValidateData() {
+ var attributes = {
+ type: "",
+ name: gDialog.inputName.value,
+ value: gDialog.inputValue.value,
+ tabindex: gDialog.inputTabIndex.value,
+ accesskey: "",
+ size: "",
+ maxlength: "",
+ accept: "",
+ };
+ var index = gDialog.inputType.selectedIndex;
+ var flags = {
+ checked: false,
+ readonly: false,
+ disabled: gDialog.inputDisabled.checked,
+ };
+ switch (index) {
+ case 1:
+ attributes.type = "password";
+ // Falls through
+ case 0:
+ flags.readonly = gDialog.inputReadOnly.checked;
+ attributes.size = gDialog.inputSize.value;
+ attributes.maxlength = gDialog.inputMaxLength.value;
+ break;
+ case 2:
+ attributes.type = "checkbox";
+ flags.checked = gDialog.inputChecked.checked;
+ break;
+ case 3:
+ attributes.type = "radio";
+ flags.checked = gDialog.inputSelected.checked;
+ break;
+ case 4:
+ attributes.type = "submit";
+ attributes.accesskey = gDialog.inputAccessKey.value;
+ break;
+ case 5:
+ attributes.type = "reset";
+ attributes.accesskey = gDialog.inputAccessKey.value;
+ break;
+ case 6:
+ attributes.type = "file";
+ attributes.accept = gDialog.inputAccept.value;
+ attributes.value = "";
+ break;
+ case 7:
+ attributes.type = "hidden";
+ attributes.tabindex = "";
+ break;
+ case 8:
+ attributes.type = "image";
+ attributes.value = "";
+ break;
+ case 9:
+ attributes.type = "button";
+ attributes.accesskey = gDialog.inputAccessKey.value;
+ break;
+ }
+ for (var a in attributes) {
+ if (attributes[a]) {
+ globalElement.setAttribute(a, attributes[a]);
+ } else {
+ globalElement.removeAttribute(a);
+ }
+ }
+ for (var f in flags) {
+ if (flags[f]) {
+ globalElement.setAttribute(f, "");
+ } else {
+ globalElement.removeAttribute(f);
+ }
+ }
+ return true;
+}
+
+function onAccept(event) {
+ if (ValidateData()) {
+ // All values are valid - copy to actual element in doc or
+ // element created to insert
+
+ var editor = GetCurrentEditor();
+
+ editor.cloneAttributes(inputElement, globalElement);
+
+ if (insertNew) {
+ try {
+ // 'true' means delete the selection before inserting
+ // in case were are converting an image to an input type="image"
+ editor.insertElementAtSelection(inputElement, true);
+ } catch (e) {
+ dump(e);
+ }
+ }
+
+ SaveWindowLocation();
+
+ return;
+ }
+ event.preventDefault();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdInputProps.xhtml b/comm/suite/editor/components/dialogs/content/EdInputProps.xhtml
new file mode 100644
index 0000000000..c6011ee896
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdInputProps.xhtml
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+%edInputProperties;
+
+%edDialogOverlay;
+]>
+
+
diff --git a/comm/suite/editor/components/dialogs/content/EdInsSrc.js b/comm/suite/editor/components/dialogs/content/EdInsSrc.js
new file mode 100644
index 0000000000..0f0304ef1e
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdInsSrc.js
@@ -0,0 +1,160 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Insert Source HTML dialog */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var gFullDataStrings = new Map();
+var gShortDataStrings = new Map();
+var gListenerAttached = false;
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ let editor = GetCurrentEditor();
+ if (!editor) {
+ window.close();
+ return;
+ }
+
+ document.documentElement.getButton("accept").removeAttribute("default");
+
+ // Create dialog object to store controls for easy access
+ gDialog.srcInput = document.getElementById("srcInput");
+
+ // Attach a paste listener so we can detect pasted data URIs we need to shorten.
+ gDialog.srcInput.addEventListener("paste", onPaste);
+
+ let selection;
+ try {
+ selection = editor.outputToString(
+ "text/html",
+ kOutputFormatted | kOutputSelectionOnly | kOutputWrap
+ );
+ } catch (e) {}
+ if (selection) {
+ selection = selection.replace(/]*>/, "").replace(/<\/body>/, "");
+
+ // Shorten data URIs for display.
+ selection = replaceDataURIs(selection);
+
+ if (selection) {
+ gDialog.srcInput.value = selection;
+ }
+ }
+ // Set initial focus
+ gDialog.srcInput.focus();
+ SetWindowLocation();
+}
+
+function replaceDataURIs(input) {
+ return input.replace(/(data:.+;base64,)([^"' >]+)/gi, function(
+ match,
+ nonDataPart,
+ dataPart
+ ) {
+ if (gShortDataStrings.has(dataPart)) {
+ // We found the exact same data URI, just return the shortened URI.
+ return nonDataPart + gShortDataStrings.get(dataPart);
+ }
+
+ let l = 5;
+ let key;
+ // Normally we insert the ellipsis after five characters but if it's not unique
+ // we include more data.
+ do {
+ key = dataPart.substr(0, l) + "…" + dataPart.substr(dataPart.length - 10);
+ l++;
+ } while (gFullDataStrings.has(key) && l < dataPart.length - 10);
+ gFullDataStrings.set(key, dataPart);
+ gShortDataStrings.set(dataPart, key);
+
+ // Attach listeners. In case anyone copies/cuts from the HTML window,
+ // we want to restore the data URI on the clipboard.
+ if (!gListenerAttached) {
+ gDialog.srcInput.addEventListener("copy", onCopyOrCut);
+ gDialog.srcInput.addEventListener("cut", onCopyOrCut);
+ gListenerAttached = true;
+ }
+
+ return nonDataPart + key;
+ });
+}
+
+function onCopyOrCut(event) {
+ let startPos = gDialog.srcInput.selectionStart;
+ if (startPos == undefined) {
+ return;
+ }
+ let endPos = gDialog.srcInput.selectionEnd;
+ let clipboard = gDialog.srcInput.value.substring(startPos, endPos);
+
+ // Add back the original data URIs we stashed away earlier.
+ clipboard = clipboard.replace(/(data:.+;base64,)([^"' >]+)/gi, function(
+ match,
+ nonDataPart,
+ key
+ ) {
+ if (!gFullDataStrings.has(key)) {
+ // User changed data URI.
+ return match;
+ }
+ return nonDataPart + gFullDataStrings.get(key);
+ });
+ event.clipboardData.setData("text/plain", clipboard);
+ if (event.type == "cut") {
+ // We have to cut the selection manually.
+ gDialog.srcInput.value =
+ gDialog.srcInput.value.substr(0, startPos) +
+ gDialog.srcInput.value.substr(endPos);
+ }
+ event.preventDefault();
+}
+
+function onPaste(event) {
+ let startPos = gDialog.srcInput.selectionStart;
+ if (startPos == undefined) {
+ return;
+ }
+ let endPos = gDialog.srcInput.selectionEnd;
+ let clipboard = event.clipboardData.getData("text/plain");
+
+ // We do out own paste by replacing the selection with the pre-processed
+ // clipboard data.
+ gDialog.srcInput.value =
+ gDialog.srcInput.value.substr(0, startPos) +
+ replaceDataURIs(clipboard) +
+ gDialog.srcInput.value.substr(endPos);
+ event.preventDefault();
+}
+
+function onAccept(event) {
+ let html = gDialog.srcInput.value;
+ if (!html) {
+ event.preventDefault();
+ return;
+ }
+
+ // Add back the original data URIs we stashed away earlier.
+ html = html.replace(/(data:.+;base64,)([^"' >]+)/gi, function(
+ match,
+ nonDataPart,
+ key
+ ) {
+ if (!gFullDataStrings.has(key)) {
+ // User changed data URI.
+ return match;
+ }
+ return nonDataPart + gFullDataStrings.get(key);
+ });
+
+ try {
+ GetCurrentEditor().insertHTML(html);
+ } catch (e) {}
+ SaveWindowLocation();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdInsSrc.xhtml b/comm/suite/editor/components/dialogs/content/EdInsSrc.xhtml
new file mode 100644
index 0000000000..32b89ebefd
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdInsSrc.xhtml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/comm/suite/editor/components/dialogs/content/EdInsertChars.js b/comm/suite/editor/components/dialogs/content/EdInsertChars.js
new file mode 100644
index 0000000000..6ee88afcdd
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdInsertChars.js
@@ -0,0 +1,409 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+// ------------------------------------------------------------------
+// From Unicode 3.0 Page 54. 3.11 Conjoining Jamo Behavior
+var SBase = 0xac00;
+var LBase = 0x1100;
+var VBase = 0x1161;
+var TBase = 0x11a7;
+var LCount = 19;
+var VCount = 21;
+var TCount = 28;
+var NCount = VCount * TCount;
+// End of Unicode 3.0
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onClose);
+
+// dialog initialization code
+function Startup() {
+ if (!GetCurrentEditor()) {
+ window.close();
+ return;
+ }
+
+ StartupLatin();
+
+ // Set a variable on the opener window so we
+ // can track ownership of close this window with it
+ window.opener.InsertCharWindow = window;
+ window.sizeToContent();
+
+ SetWindowLocation();
+}
+
+function onAccept(event) {
+ // Insert the character
+ try {
+ GetCurrentEditor().insertText(LatinM.label);
+ } catch (e) {}
+
+ // Set persistent attributes to save
+ // which category, letter, and character modifier was used
+ CategoryGroup.setAttribute("category", category);
+ CategoryGroup.setAttribute("letter_index", indexL);
+ CategoryGroup.setAttribute("char_index", indexM);
+
+ // Don't close the dialog
+ event.preventDefault();
+}
+
+// Don't allow inserting in HTML Source Mode
+function onFocus() {
+ var enable = true;
+ if ("gEditorDisplayMode" in window.opener) {
+ enable = !window.opener.IsInHTMLSourceMode();
+ }
+
+ SetElementEnabled(document.documentElement.getButton("accept"), enable);
+}
+
+function onClose() {
+ window.opener.InsertCharWindow = null;
+ SaveWindowLocation();
+}
+
+// ------------------------------------------------------------------
+var LatinL;
+var LatinM;
+var LatinL_Label;
+var LatinM_Label;
+var indexL = 0;
+var indexM = 0;
+var indexM_AU = 0;
+var indexM_AL = 0;
+var indexM_U = 0;
+var indexM_L = 0;
+var indexM_S = 0;
+var LItems = 0;
+var category;
+var CategoryGroup;
+var initialize = true;
+
+function StartupLatin() {
+ LatinL = document.getElementById("LatinL");
+ LatinM = document.getElementById("LatinM");
+ LatinL_Label = document.getElementById("LatinL_Label");
+ LatinM_Label = document.getElementById("LatinM_Label");
+
+ var Symbol = document.getElementById("Symbol");
+ var AccentUpper = document.getElementById("AccentUpper");
+ var AccentLower = document.getElementById("AccentLower");
+ var Upper = document.getElementById("Upper");
+ var Lower = document.getElementById("Lower");
+ CategoryGroup = document.getElementById("CatGrp");
+
+ // Initialize which radio button is set from persistent attribute...
+ var category = CategoryGroup.getAttribute("category");
+
+ // ...as well as indexes into the letter and character lists
+ var index = Number(CategoryGroup.getAttribute("letter_index"));
+ if (index && index >= 0) {
+ indexL = index;
+ }
+ index = Number(CategoryGroup.getAttribute("char_index"));
+ if (index && index >= 0) {
+ indexM = index;
+ }
+
+ switch (category) {
+ case "AccentUpper": // Uppercase Diacritical
+ CategoryGroup.selectedItem = AccentUpper;
+ indexM_AU = indexM;
+ break;
+ case "AccentLower": // Lowercase Diacritical
+ CategoryGroup.selectedItem = AccentLower;
+ indexM_AL = indexM;
+ break;
+ case "Upper": // Uppercase w/o Diacritical
+ CategoryGroup.selectedItem = Upper;
+ indexM_U = indexM;
+ break;
+ case "Lower": // Lowercase w/o Diacritical
+ CategoryGroup.selectedItem = Lower;
+ indexM_L = indexM;
+ break;
+ default:
+ category = "Symbol";
+ CategoryGroup.selectedItem = Symbol;
+ indexM_S = indexM;
+ break;
+ }
+
+ ChangeCategory(category);
+ initialize = false;
+}
+
+function ChangeCategory(newCategory) {
+ if (category != newCategory || initialize) {
+ category = newCategory;
+ // Note: Must do L before M to set LatinL.selectedIndex
+ UpdateLatinL();
+ UpdateLatinM();
+ UpdateCharacter();
+ }
+}
+
+function SelectLatinLetter() {
+ if (LatinL.selectedIndex != indexL) {
+ indexL = LatinL.selectedIndex;
+ UpdateLatinM();
+ UpdateCharacter();
+ }
+}
+
+function SelectLatinModifier() {
+ if (LatinM.selectedIndex != indexM) {
+ indexM = LatinM.selectedIndex;
+ UpdateCharacter();
+ }
+}
+function DisableLatinL(disable) {
+ if (disable) {
+ LatinL_Label.setAttribute("disabled", "true");
+ LatinL.setAttribute("disabled", "true");
+ } else {
+ LatinL_Label.removeAttribute("disabled");
+ LatinL.removeAttribute("disabled");
+ }
+}
+
+function UpdateLatinL() {
+ LatinL.removeAllItems();
+ if (category == "AccentUpper" || category == "AccentLower") {
+ DisableLatinL(false);
+ // No Q or q
+ var alphabet =
+ category == "AccentUpper"
+ ? "ABCDEFGHIJKLMNOPRSTUVWXYZ"
+ : "abcdefghijklmnoprstuvwxyz";
+ for (var letter = 0; letter < alphabet.length; letter++) {
+ LatinL.appendItem(alphabet.charAt(letter));
+ }
+
+ LatinL.selectedIndex = indexL;
+ } else {
+ // Other categories don't hinge on a "letter"
+ DisableLatinL(true);
+ // Note: don't change the indexL so it can be used next time
+ }
+}
+
+function UpdateLatinM() {
+ LatinM.removeAllItems();
+ var i, accent;
+ switch (category) {
+ case "AccentUpper": // Uppercase Diacritical
+ accent = upper[indexL];
+ for (i = 0; i < accent.length; i++) {
+ LatinM.appendItem(accent.charAt(i));
+ }
+
+ if (indexM_AU < accent.length) {
+ indexM = indexM_AU;
+ } else {
+ indexM = accent.length - 1;
+ }
+ indexM_AU = indexM;
+ break;
+
+ case "AccentLower": // Lowercase Diacritical
+ accent = lower[indexL];
+ for (i = 0; i < accent.length; i++) {
+ LatinM.appendItem(accent.charAt(i));
+ }
+
+ if (indexM_AL < accent.length) {
+ indexM = indexM_AL;
+ } else {
+ indexM = lower[indexL].length - 1;
+ }
+ indexM_AL = indexM;
+ break;
+
+ case "Upper": // Uppercase w/o Diacritical
+ for (i = 0; i < otherupper.length; i++) {
+ LatinM.appendItem(otherupper.charAt(i));
+ }
+
+ if (indexM_U < otherupper.length) {
+ indexM = indexM_U;
+ } else {
+ indexM = otherupper.length - 1;
+ }
+ indexM_U = indexM;
+ break;
+
+ case "Lower": // Lowercase w/o Diacritical
+ for (i = 0; i < otherlower.length; i++) {
+ LatinM.appendItem(otherlower.charAt(i));
+ }
+
+ if (indexM_L < otherlower.length) {
+ indexM = indexM_L;
+ } else {
+ indexM = otherlower.length - 1;
+ }
+ indexM_L = indexM;
+ break;
+
+ case "Symbol": // Symbol
+ for (i = 0; i < symbol.length; i++) {
+ LatinM.appendItem(symbol.charAt(i));
+ }
+
+ if (indexM_S < symbol.length) {
+ indexM = indexM_S;
+ } else {
+ indexM = symbol.length - 1;
+ }
+ indexM_S = indexM;
+ break;
+ }
+ LatinM.selectedIndex = indexM;
+}
+
+function UpdateCharacter() {
+ indexM = LatinM.selectedIndex;
+
+ switch (category) {
+ case "AccentUpper": // Uppercase Diacritical
+ indexM_AU = indexM;
+ break;
+ case "AccentLower": // Lowercase Diacritical
+ indexM_AL = indexM;
+ break;
+ case "Upper": // Uppercase w/o Diacritical
+ indexM_U = indexM;
+ break;
+ case "Lower": // Lowercase w/o Diacritical
+ indexM_L = indexM;
+ break;
+ case "Symbol":
+ indexM_S = indexM;
+ break;
+ }
+ // dump("Letter Index="+indexL+", Character Index="+indexM+", Character = "+LatinM.label+"\n");
+}
+
+const upper = [
+ // A
+ "\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5\u0100\u0102\u0104\u01cd\u01de\u01de\u01e0\u01fa\u0200\u0202\u0226\u1e00\u1ea0\u1ea2\u1ea4\u1ea6\u1ea8\u1eaa\u1eac\u1eae\u1eb0\u1eb2\u1eb4\u1eb6",
+ // B
+ "\u0181\u0182\u0184\u1e02\u1e04\u1e06",
+ // C
+ "\u00c7\u0106\u0108\u010a\u010c\u0187\u1e08",
+ // D
+ "\u010e\u0110\u0189\u018a\u1e0a\u1e0c\u1e0e\u1e10\u1e12",
+ // E
+ "\u00C8\u00C9\u00CA\u00CB\u0112\u0114\u0116\u0118\u011A\u0204\u0206\u0228\u1e14\u1e16\u1e18\u1e1a\u1e1c\u1eb8\u1eba\u1ebc\u1ebe\u1ec0\u1ec2\u1ec4\u1ec6",
+ // F
+ "\u1e1e",
+ // G
+ "\u011c\u011E\u0120\u0122\u01e4\u01e6\u01f4\u1e20",
+ // H
+ "\u0124\u0126\u021e\u1e22\u1e24\u1e26\u1e28\u1e2a",
+ // I
+ "\u00CC\u00CD\u00CE\u00CF\u0128\u012a\u012C\u012e\u0130\u0208\u020a\u1e2c\u1e2e\u1ec8\u1eca",
+ // J
+ "\u0134\u01f0",
+ // K
+ "\u0136\u0198\u01e8\u1e30\u1e32\u1e34",
+ // L
+ "\u0139\u013B\u013D\u013F\u0141\u1e36\u1e38\u1e3a\u1e3c",
+ // M
+ "\u1e3e\u1e40\u1e42",
+ // N
+ "\u00D1\u0143\u0145\u0147\u014A\u01F8\u1e44\u1e46\u1e48\u1e4a",
+ // O
+ "\u00D2\u00D3\u00D4\u00D5\u00D6\u014C\u014E\u0150\u01ea\u01ec\u020c\u020e\u022A\u022C\u022E\u0230\u1e4c\u1e4e\u1e50\u1e52\u1ecc\u1ece\u1ed0\u1ed2\u1ed4\u1ed6\u1ed8\u1eda\u1edc\u1ede\u1ee0\u1ee2",
+ // P
+ "\u1e54\u1e56",
+ // No Q
+ // R
+ "\u0154\u0156\u0158\u0210\u0212\u1e58\u1e5a\u1e5c\u1e5e",
+ // S
+ "\u015A\u015C\u015E\u0160\u0218\u1e60\u1e62\u1e64\u1e66\u1e68",
+ // T
+ "\u0162\u0164\u0166\u021A\u1e6a\u1e6c\u1e6e\u1e70",
+ // U
+ "\u00D9\u00DA\u00DB\u00DC\u0168\u016A\u016C\u016E\u0170\u0172\u0214\u0216\u1e72\u1e74\u1e76\u1e78\u1e7a\u1ee4\u1ee6\u1ee8\u1eea\u1eec\u1eee\u1ef0",
+ // V
+ "\u1e7c\u1e7e",
+ // W
+ "\u0174\u1e80\u1e82\u1e84\u1e86\u1e88",
+ // X
+ "\u1e8a\u1e8c",
+ // Y
+ "\u00DD\u0176\u0178\u0232\u1e8e\u1ef2\u1ef4\u1ef6\u1ef8",
+ // Z
+ "\u0179\u017B\u017D\u0224\u1e90\u1e92\u1e94",
+];
+
+const lower = [
+ // a
+ "\u00e0\u00e1\u00e2\u00e3\u00e4\u00e5\u0101\u0103\u0105\u01ce\u01df\u01e1\u01fb\u0201\u0203\u0227\u1e01\u1e9a\u1ea1\u1ea3\u1ea5\u1ea7\u1ea9\u1eab\u1ead\u1eaf\u1eb1\u1eb3\u1eb5\u1eb7",
+ // b
+ "\u0180\u0183\u0185\u1e03\u1e05\u1e07",
+ // c
+ "\u00e7\u0107\u0109\u010b\u010d\u0188\u1e09",
+ // d
+ "\u010f\u0111\u1e0b\u1e0d\u1e0f\u1e11\u1e13",
+ // e
+ "\u00e8\u00e9\u00ea\u00eb\u0113\u0115\u0117\u0119\u011b\u0205\u0207\u0229\u1e15\u1e17\u1e19\u1e1b\u1e1d\u1eb9\u1ebb\u1ebd\u1ebf\u1ec1\u1ec3\u1ec5\u1ec7",
+ // f
+ "\u1e1f",
+ // g
+ "\u011d\u011f\u0121\u0123\u01e5\u01e7\u01f5\u1e21",
+ // h
+ "\u0125\u0127\u021f\u1e23\u1e25\u1e27\u1e29\u1e2b\u1e96",
+ // i
+ "\u00ec\u00ed\u00ee\u00ef\u0129\u012b\u012d\u012f\u0131\u01d0\u0209\u020b\u1e2d\u1e2f\u1ec9\u1ecb",
+ // j
+ "\u0135",
+ // k
+ "\u0137\u0138\u01e9\u1e31\u1e33\u1e35",
+ // l
+ "\u013a\u013c\u013e\u0140\u0142\u1e37\u1e39\u1e3b\u1e3d",
+ // m
+ "\u1e3f\u1e41\u1e43",
+ // n
+ "\u00f1\u0144\u0146\u0148\u0149\u014b\u01f9\u1e45\u1e47\u1e49\u1e4b",
+ // o
+ "\u00f2\u00f3\u00f4\u00f5\u00f6\u014d\u014f\u0151\u01d2\u01eb\u01ed\u020d\u020e\u022b\u022d\u022f\u0231\u1e4d\u1e4f\u1e51\u1e53\u1ecd\u1ecf\u1ed1\u1ed3\u1ed5\u1ed7\u1ed9\u1edb\u1edd\u1edf\u1ee1\u1ee3",
+ // p
+ "\u1e55\u1e57",
+ // No q
+ // r
+ "\u0155\u0157\u0159\u0211\u0213\u1e59\u1e5b\u1e5d\u1e5f",
+ // s
+ "\u015b\u015d\u015f\u0161\u0219\u1e61\u1e63\u1e65\u1e67\u1e69",
+ // t
+ "\u0162\u0163\u0165\u0167\u021b\u1e6b\u1e6d\u1e6f\u1e71\u1e97",
+ // u
+ "\u00f9\u00fa\u00fb\u00fc\u0169\u016b\u016d\u016f\u0171\u0173\u01d4\u01d6\u01d8\u01da\u01dc\u0215\u0217\u1e73\u1e75\u1e77\u1e79\u1e7b\u1ee5\u1ee7\u1ee9\u1eeb\u1eed\u1eef\u1ef1",
+ // v
+ "\u1e7d\u1e7f",
+ // w
+ "\u0175\u1e81\u1e83\u1e85\u1e87\u1e89\u1e98",
+ // x
+ "\u1e8b\u1e8d",
+ // y
+ "\u00fd\u00ff\u0177\u0233\u1e8f\u1e99\u1ef3\u1ef5\u1ef7\u1ef9",
+ // z
+ "\u017a\u017c\u017e\u0225\u1e91\u1e93\u1e95",
+];
+
+const symbol =
+ "\u00a1\u00a2\u00a3\u00a4\u00a5\u20ac\u00a6\u00a7\u00a8\u00a9\u00aa\u00ab\u00ac\u00ae\u00af\u00b0\u00b1\u00b2\u00b3\u00b4\u00b5\u00b6\u00b7\u00b8\u00b9\u00ba\u00bb\u00bc\u00bd\u00be\u00bf\u00d7\u00f7";
+
+const otherupper =
+ "\u00c6\u00d0\u00d8\u00de\u0132\u0152\u0186\u01c4\u01c5\u01c7\u01c8\u01ca\u01cb\u01F1\u01f2";
+
+const otherlower =
+ "\u00e6\u00f0\u00f8\u00fe\u00df\u0133\u0153\u01c6\u01c9\u01cc\u01f3";
diff --git a/comm/suite/editor/components/dialogs/content/EdInsertChars.xhtml b/comm/suite/editor/components/dialogs/content/EdInsertChars.xhtml
new file mode 100644
index 0000000000..4e6c1020fa
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdInsertChars.xhtml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
diff --git a/comm/suite/editor/components/dialogs/content/EdInsertMath.js b/comm/suite/editor/components/dialogs/content/EdInsertMath.js
new file mode 100644
index 0000000000..c99bf8edac
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdInsertMath.js
@@ -0,0 +1,330 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Insert MathML dialog */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ window.close();
+ return;
+ }
+
+ // Create dialog object for easy access
+ gDialog.accept = document.documentElement.getButton("accept");
+ gDialog.mode = document.getElementById("optionMode");
+ gDialog.direction = document.getElementById("optionDirection");
+ gDialog.input = document.getElementById("input");
+ gDialog.output = document.getElementById("output");
+ gDialog.tabbox = document.getElementById("tabboxInsertLaTeXCommand");
+
+ // Set initial focus
+ gDialog.input.focus();
+
+ // Load TeXZilla
+ // TeXZilla.js contains non-ASCII characters and explicitly sets
+ // window.TeXZilla, so we have to specify the charset parameter but don't
+ // need to worry about the targetObj parameter.
+ /* globals TeXZilla */
+ Services.scriptloader.loadSubScript(
+ "chrome://editor/content/TeXZilla.js",
+ {},
+ "UTF-8"
+ );
+
+ // Verify if the selection is on a