diff options
Diffstat (limited to '')
89 files changed, 18623 insertions, 0 deletions
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 <deck> 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 <treeChildren> 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 <treeChildren> 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: + <treeItem> + <treeRow> + <treeCell> // Name Cell + <treeCell // Value Cell +*/ +function GetTreeItemAttributeStr(treeItem) { + if (treeItem) { + return TrimString(treeItem.firstChild.firstChild.getAttribute("label")); + } + + return ""; +} + +function GetTreeItemValueStr(treeItem) { + if (treeItem) { + return TrimString(treeItem.firstChild.lastChild.getAttribute("label")); + } + + return ""; +} + +function SetTreeItemValueStr(treeItem, value) { + if (treeItem && GetTreeItemValueStr(treeItem) != value) { + treeItem.firstChild.lastChild.setAttribute("label", value); + } +} + +function IsNotTreeHeader(treeCell) { + if (treeCell) { + return treeCell.parentNode.parentNode.nodeName != "treehead"; + } + + return false; +} + +function RemoveNameFromAttArray(attName, attArray) { + for (var i = 0; i < attArray.length; i++) { + if (attName.toLowerCase() == attArray[i].toLowerCase()) { + // Remove 1 array item + attArray.splice(i, 1); + break; + } + } +} + +// adds a generalised treeitem. +function AddTreeItem(name, value, treeChildrenId, attArray) { + attArray[attArray.length] = name; + var treeChildren = document.getElementById(treeChildrenId); + var treeitem = document.createXULElement("treeitem"); + var treerow = document.createXULElement("treerow"); + + var attrCell = document.createXULElement("treecell"); + attrCell.setAttribute("class", "propertylist"); + attrCell.setAttribute("label", name); + + var valueCell = document.createXULElement("treecell"); + valueCell.setAttribute("class", "propertylist"); + valueCell.setAttribute("label", value); + + treerow.appendChild(attrCell); + treerow.appendChild(valueCell); + treeitem.appendChild(treerow); + treeChildren.appendChild(treeitem); + + // Select item just added, but suppress calling the onSelect handler. + gDoOnSelectTree = false; + try { + selectTreeItem(treeChildren, treeitem); + } catch (e) {} + gDoOnSelectTree = true; + + return treeitem; +} + +function selectTreeItem(treeChildren, item) { + var index = treeChildren.parentNode.view.getIndexOfItem(item); + treeChildren.parentNode.view.selection.select(index); +} + +function getSelectedItem(tree) { + if (tree.view.selection.count == 1) { + return tree.view.getItemAtIndex(tree.currentIndex); + } + return null; +} diff --git a/comm/suite/editor/components/dialogs/content/EdAdvancedEdit.xhtml b/comm/suite/editor/components/dialogs/content/EdAdvancedEdit.xhtml new file mode 100644 index 0000000000..94942709a1 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdAdvancedEdit.xhtml @@ -0,0 +1,182 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +<!-- first checkin of the year 2000! --> +<!-- Ben Goodger, 12:50AM, 01/00/00 NZST --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> +<?xml-stylesheet href="chrome://messenger/skin/menulist.css" type="text/css"?> + +<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EdAdvancedEdit.dtd"> + +<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + id="advancedEditDlg" + style="width: 40em;" + title="&WindowTitle.label;" + onload="Startup()"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <!-- element page functions --> + <script src="chrome://editor/content/EdAEHTMLAttributes.js"/> + <script src="chrome://editor/content/EdAECSSAttributes.js"/> + <script src="chrome://editor/content/EdAEJSEAttributes.js"/> + <script src="chrome://editor/content/EdAEAttributes.js"/> + + <!-- global dialog functions --> + <script src="chrome://editor/content/EdAdvancedEdit.js"/> + + <script src="chrome://messenger/content/customElements.js"/> + + <hbox> + <label value="¤tattributesfor.label;"/> + <label class="header" id="tagLabel"/> + </hbox> + + <separator class="thin"/> + + <tabbox flex="1"> + <tabs> + <tab label="&tabHTML.label;"/> + <tab label="&tabCSS.label;"/> + <tab label="&tabJSE.label;" id="tabJSE"/> + </tabs> + <tabpanels flex="1"> + <!-- ============================================================== --> + <!-- HTML Attributes --> + <!-- ============================================================== --> + <vbox> + <tree id="HTMLATree" class="AttributesTree" flex="1" + hidecolumnpicker="true" seltype="single" + onselect="onSelectHTMLTreeItem();" + onclick="onSelectHTMLTreeItem();" + ondblclick="editHTMLAttributeValue(event.target);"> + <treecols> + <treecol id="HTMLAttrCol" flex="35" label="&tree.attributeHeader.label;"/> + <splitter class="tree-splitter"/> + <treecol id="HTMLValCol" flex="65" label="&tree.valueHeader.label;"/> + </treecols> + <treechildren id="HTMLAList" flex="1"/> + </tree> + <hbox align="center"> + <label value="&editAttribute.label;"/> + <spacer flex="1"/> + <button label="&removeAttribute.label;" oncommand="RemoveHTMLAttribute();"/> + </hbox> + <grid> + <columns> + <column flex="1"/><column flex="1"/> + </columns> + <rows> + <row equalsize="always"> + <label control="AddHTMLAttributeNameInput" value="&AttName.label;"/> + <label control="AddHTMLAttributeValueInput" value="&AttValue.label;"/> + </row> + <row align="top" equalsize="always"> + <!-- Lists are built at runtime --> + <menulist is="menulist-editable" id="AddHTMLAttributeNameInput" + editable="true" flex="1" + oninput="onInputHTMLAttributeName();" + oncommand="onInputHTMLAttributeName();"/> + <deck id="AddHTMLAttributeValueDeck" selectedIndex="0"> + <hbox align="top"> + <textbox id="AddHTMLAttributeValueTextbox" flex="1" + oninput="onInputHTMLAttributeValue();"/> + </hbox> + <hbox align="top"> + <menulist is="menulist-editable" id="AddHTMLAttributeValueMenulist" + editable="true" flex="1" + oninput="onInputHTMLAttributeValue();" + oncommand="onInputHTMLAttributeValue();"/> + </hbox> + </deck> + </row> + </rows> + </grid> + </vbox> + <!-- ============================================================== --> + <!-- CSS Attributes --> + <!-- ============================================================== --> + <vbox> + <tree id="CSSATree" class="AttributesTree" flex="1" + hidecolumnpicker="true" seltype="single" + onselect="onSelectCSSTreeItem();" + onclick="onSelectCSSTreeItem();" + ondblclick="editCSSAttributeValue(event.target);"> + <treecols> + <treecol id="CSSPropCol" flex="35" label="&tree.propertyHeader.label;"/> + <splitter class="tree-splitter"/> + <treecol id="CSSValCol" flex="65" label="&tree.valueHeader.label;"/> + </treecols> + <treechildren id="CSSAList" flex="1"/> + </tree> + <hbox align="center"> + <label value="&editAttribute.label;"/> + <spacer flex="1"/> + <button label="&removeAttribute.label;" oncommand="RemoveCSSAttribute();"/> + </hbox> + <grid> + <columns> + <column flex="1"/><column flex="1"/> + </columns> + <rows> + <row equalsize="always"> + <label value="&PropertyName.label;"/> + <label value="&AttValue.label;"/> + </row> + <row align="top" equalsize="always"> + <textbox id="AddCSSAttributeNameInput" flex="1" + oninput="onInputCSSAttributeName();"/> + <textbox id="AddCSSAttributeValueInput" flex="1" + oninput="onChangeCSSAttribute();"/> + </row> + </rows> + </grid> + </vbox> + <!-- ============================================================== --> + <!-- JavaScript Event Handlers --> + <!-- ============================================================== --> + <vbox> + <tree id="JSEATree" class="AttributesTree" flex="1" + hidecolumnpicker="true" seltype="single" + onselect="onSelectJSETreeItem();" + onclick="onSelectJSETreeItem();" + ondblclick="editJSEAttributeValue(event.target);"> + <treecols> + <treecol id="AttrCol" flex="35" label="&tree.attributeHeader.label;"/> + <splitter class="tree-splitter"/> + <treecol id="HeaderCol" flex="65" label="&tree.valueHeader.label;"/> + </treecols> + <treechildren id="JSEAList" flex="1"/> + </tree> + <hbox align="center"> + <label value="&editAttribute.label;"/> + <spacer flex="1"/> + <button label="&removeAttribute.label;" oncommand="RemoveJSEAttribute()"/> + </hbox> + <grid> + <columns> + <column flex="1"/><column flex="1"/> + </columns> + <rows> + <row equalsize="always"> + <label value="&AttName.label;"/> + <label value="&AttValue.label;"/> + </row> + <row align="top" equalsize="always"> + <!-- List is built at runtime --> + <menulist id="AddJSEAttributeNameList" flex="1" + oncommand="onSelectJSEAttribute();"/> + <textbox id="AddJSEAttributeValueInput" flex="1" + oninput="onInputJSEAttributeValue();"/> + </row> + </rows> + </grid> + </vbox> + </tabpanels> + </tabbox> +</dialog> 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 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog [ +<!ENTITY % edButtonProperties SYSTEM "chrome://editor/locale/EditorButtonProperties.dtd"> +%edButtonProperties; +<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd"> +%edDialogOverlay; +]> + +<dialog title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload="Startup();" + buttons="accept,cancel"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EdButtonProps.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <groupbox> + <hbox class="groupbox-title"> + <label class="header">&Settings.label;</label> + </hbox> + <grid><columns><column/><column/></columns> + <rows> + <row align="center"> + <label control="ButtonType" value="&ButtonType.label;" accesskey="&ButtonType.accesskey;"/> + <menulist id="ButtonType"> + <menupopup> + <menuitem label="&submit.value;"/> + <menuitem label="&reset.value;"/> + <menuitem label="&button.value;"/> + </menupopup> + </menulist> + </row> + <row align="center"> + <label control="ButtonName" value="&ButtonName.label;" accesskey="&ButtonName.accesskey;"/> + <textbox id="ButtonName"/> + </row> + <row align="center"> + <label control="ButtonValue" value="&ButtonValue.label;" accesskey="&ButtonValue.accesskey;"/> + <textbox id="ButtonValue"/> + </row> + </rows> + </grid> + <hbox> + <button id="MoreFewerButton" oncommand="onMoreFewer();" persist="more"/> + </hbox> + <grid id="MoreSection"><columns><column/><column/></columns> + <rows> + <row> + <spacer/> + <checkbox id="ButtonDisabled" label="&ButtonDisabled.label;" accesskey="&ButtonDisabled.accesskey;"/> + </row> + <row align="center"> + <label control="ButtonTabIndex" value="&tabIndex.label;" accesskey="&tabIndex.accesskey;"/> + <hbox> + <textbox id="ButtonTabIndex" class="narrow" oninput="forceInteger(this.id);"/> + </hbox> + </row> + <row align="center"> + <label control="ButtonAccessKey" value="&AccessKey.label;" accesskey="&AccessKey.accesskey;"/> + <hbox> + <textbox id="ButtonAccessKey" class="narrow"/> + </hbox> + </row> + </rows> + </grid> + </groupbox> + + <!-- from EdDialogOverlay --> + <hbox flex="1" style="margin-top: 0.2em"> + <button id="RemoveButton" label="&RemoveButton.label;" accesskey="&RemoveButton.accesskey;" oncommand="RemoveButton();"/> + <!-- This will right-align the button --> + <spacer flex="1"/> + <button id="AdvancedEditButton" + oncommand="onAdvancedEdit();" + label="&AdvancedEditButton.label;" + accesskey="&AdvancedEditButton.accessKey;" + tooltiptext="&AdvancedEditButton.tooltip;"/> + </hbox> + <separator class="groove"/> + +</dialog> 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 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EdColorPicker.dtd"> + +<dialog title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml" + onload="Startup()"> + + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EdColorPicker.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <hbox id="TableOrCellGroup" align="center" collapsed="true"> + <label control="CellOrTableGroup" value="&background.label;" accesskey="&background.accessKey;"/> + <radiogroup id="CellOrTableGroup" orient="horizontal"> + <radio id="TableRadio" label="&table.label;" accesskey="&table.accessKey;"/> + <radio id="CellRadio" label="&cell.label;" accesskey="&cell.accessKey;"/> + </radiogroup> + </hbox> + <label value="&chooseColor1.label;"/> + <html:input type="color" id="ColorPicker" + onclick="SetDefaultToOk();" + ondblclick="if (onAccept()) { window.close(); }" + onkeypress="SelectColorByKeypress(event);" + onchange="SelectColor();"/> + + <spacer class="spacer"/> + <vbox flex="1"> + <button id="LastPickedButton" crop="right" oncommand="SelectLastPickedColor();"> + <spacer id="LastPickedColor" + LastTextColor="" LastBackgroundColor="" + persist="LastTextColor LastBackgroundColor"/> + <label value="&lastPickedColor.label;" accesskey="&lastPickedColor.accessKey;" flex="1" style="text-align: center;"/> + </button> + <label value="&chooseColor2.label;" accesskey="&chooseColor2.accessKey;" control="ColorInput"/> + <label value="&setColorExample.label;"/> + <hbox align="center" flex="1="> + <textbox id="ColorInput" style="width: 8em" oninput="SetColorSwatch(); SetDefaultToOk();"/> + <spacer flex="1"/> + <spacer id="ColorPickerSwatch"/> + <spacer flex="1"/> + <button id="DefaultColorButton" label="&default.label;" accesskey="&default.accessKey;" + style="margin-right:0px;" oncommand="RemoveColor()"/> + </hbox> + </vbox> + <separator class="groove"/> +</dialog> 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 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog [ +<!ENTITY % edColorPropertiesDTD SYSTEM "chrome://editor/locale/EditorColorProperties.dtd"> +%edColorPropertiesDTD; +<!ENTITY % composeEditorOverlayDTD SYSTEM "chrome://messenger/locale/messengercompose/mailComposeEditorOverlay.dtd"> +%composeEditorOverlayDTD; +<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd"> +%edDialogOverlay; +]> + +<dialog title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload="Startup()"> + + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EdColorProps.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <groupbox align="start"> + <hbox class="groupbox-title"> + <label class="header">&pageColors.label;</label> + </hbox> + <radiogroup id="PageColorGroup"> + <radio id="DefaultColorsRadio" label="&defaultColorsRadio.label;" oncommand="UseDefaultColors()" + accesskey="&defaultColorsRadio.accessKey;" + tooltiptext="&defaultColorsRadio.tooltip;" /> + <radio id="CustomColorsRadio" label="&customColorsRadio.label;" oncommand="UseCustomColors()" + accesskey="&customColorsRadio.accessKey;" + tooltiptext="&customColorsRadio.tooltip;" /> + </radiogroup> + <hbox class="indent"> + <grid> + <columns><column/><column/></columns> + <rows> + <row align="center"> + <label id="Text" control="TextButton" + value="&normalText.label;&colon.character;" + accesskey="&normalText.accessKey;"/> + <button id="TextButton" class="color-button" oncommand="GetColorAndUpdate('textCW');"> + <spacer id="textCW" class="color-well"/> + </button> + </row> + <row align="center"> + <label id="Link" control="LinkButton" + value="&linkText.label;&colon.character;" + accesskey="&linkText.accessKey;"/> + <button id="LinkButton" class="color-button" oncommand="GetColorAndUpdate('linkCW');"> + <spacer id="linkCW" class="color-well"/> + </button> + </row> + <row align="center"> + <label id="Active" control="ActiveLinkButton" + value="&activeLinkText.label;&colon.character;" + accesskey="&activeLinkText.accessKey;"/> + <button id="ActiveLinkButton" class="color-button" oncommand="GetColorAndUpdate('activeCW');"> + <spacer id="activeCW" class="color-well"/> + </button> + </row> + <row align="center"> + <label id="Visited" control="VisitedLinkButton" + value="&visitedLinkText.label;&colon.character;" + accesskey="&visitedLinkText.accessKey;"/> + <button id="VisitedLinkButton" class="color-button" oncommand="GetColorAndUpdate('visitedCW');"> + <spacer id="visitedCW" class="color-well"/> + </button> + </row> + <row align="center"> + <label id="Background" control="BackgroundButton" + value="&background.label;" + accesskey="&background.accessKey;"/> + <button id="BackgroundButton" class="color-button" oncommand="GetColorAndUpdate('backgroundCW');"> + <spacer id="backgroundCW" class="color-well"/> + </button> + </row> + </rows> + </grid> + <vbox id="ColorPreview" flex="1"> + <spacer flex="1"/> + <label class="larger" id="NormalText" value="&normalText.label;"/> + <spacer flex="1"/> + <label class="larger" id="LinkText" value="&linkText.label;"/> + <spacer flex="1"/> + <label class="larger" id="ActiveLinkText" value="&activeLinkText.label;"/> + <spacer flex="1"/> + <label class="larger" id="VisitedLinkText" value="&visitedLinkText.label;"/> + <spacer flex="1"/> + </vbox> + <spacer flex="1"/> + </hbox> + <spacer class="spacer"/> + </groupbox> + <spacer class="spacer"/> + <label control="BackgroundImageInput" + value="&backgroundImage.label;" + tooltiptext="&backgroundImage.tooltip;" + accesskey="&backgroundImage.accessKey;"/> + <tooltip id="shortenedDataURI"> + <label value="&backgroundImage.shortenedDataURI;"/> + </tooltip> + <textbox id="BackgroundImageInput" class="uri-element" oninput="ChangeBackgroundImage()" + tooltiptext="&backgroundImage.tooltip;" flex="1"/> + <hbox align="center"> + <checkbox id="MakeRelativeCheckbox" + for="BackgroundImageInput" + label="&makeUrlRelative.label;" + accesskey="&makeUrlRelative.accessKey;" + oncommand="MakeInputValueRelativeOrAbsolute(this);" + tooltiptext="&makeUrlRelative.tooltip;"/> + <spacer flex="1"/> + <button id="ChooseFile" + oncommand="chooseFile()" + label="&chooseFileButton.label;" + accesskey="&chooseFileButton.accessKey;"/> + </hbox> + <spacer class="smallspacer"/> + <hbox> + <spacer flex="1"/> + <button id="AdvancedEditButton" + oncommand="onAdvancedEdit();" + label="&AdvancedEditButton.label;" + accesskey="&AdvancedEditButton.accessKey;" + tooltiptext="&AdvancedEditButton.tooltip;"/> + </hbox> + <separator class="groove"/> +</dialog> 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 </p> completely + str = str.replace(/\s*<\/p>\s*/g, ""); + + // Trim whitespace adjacent to <p> and <br> tags + // and replace <p> with <br> + // (which will be replaced with </tr> below) + str = str.replace(/\s*<p>\s*|\s*<br>\s*/g, "<br>"); + + // Trim leading <br>s + str = str.replace(/^(<br>)+/, ""); + + // Trim trailing <br>s + str = str.replace(/(<br>)+$/, ""); + + // Reduce multiple internal <br> to just 1 + // TODO: Maybe add a checkbox to let user decide + // str = str.replace(/(<br>)+/g, "<br>"); + + // 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 <BR> 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 = "<br>"; + } + + // 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 = "<br>"; + } + + // 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 += "<td>"; + + 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*<br>\s*/g, "</tr>\n<tr><td>"); + + // 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 = + '<table border="1" width="100%" cellpadding="2" cellspacing="2">\n<tr><td>' + + str + + "</tr>\n</table>\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 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EdConvertToTable.dtd"> + +<dialog title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload = "Startup()" + style="min-width:20em"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <!--- Element-specific methods --> + <script src="chrome://editor/content/EdConvertToTable.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + <description class="wrap" flex="1">&instructions1.label;</description> + <description class="wrap" flex="1">&instructions2.label;</description> + <radiogroup id="SepRadioGroup" persist="index character" index="0" character=""> + <radio id="comma" label="&commaRadio.label;" oncommand="SelectCharacter('0');"/> + <radio id="space" label="&spaceRadio.label;" oncommand="SelectCharacter('1');"/> + <hbox> + <spacer class="radio-spacer"/> + <checkbox id="CollapseSpaces" label="&collapseSpaces.label;" + checked="true" persist="checked" + tooltiptext="&collapseSpaces.tooltip;"/> + </hbox> + <hbox align="center"> + <radio id="other" label="&otherRadio.label;" oncommand="SelectCharacter('2');"/> + <textbox class="narrow" id="SepCharacterInput" oninput="InputSepCharacter()"/> + </hbox> + </radiogroup> + <spacer class="spacer"/> + <checkbox id="DeleteSepCharacter" label="&deleteCharCheck.label;" persist="checked"/> + <separator class="groove"/> +</dialog> 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 <br> + if (start && start.localName == "br") { + editor.deleteNode(start); + editor.insertNode(start, element, element.childNodes.length); + empty = false; + } + // Still nothing? Insert a <br> 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 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<?xul-overlay href="chrome://editor/content/EdDialogOverlay.xhtml"?> + +<!DOCTYPE dialog SYSTEM "chrome://editor/locale/Ed?????????.dtd"> +<!-- dialog containing a control requiring initial setup --> +<dialog title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload="Startup()"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/Ed?????.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> +</dialog> 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 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> +<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorPersonalDictionary.dtd"> +<dialog buttons="cancel" title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + persist="screenX screenY" + onload="Startup()"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EdDictionary.js"/> + + <grid> + <columns><column style="width: 15em" flex="1"/><column flex="1"/></columns> + <rows> + <row> + <label value="&wordEditField.label;" + control="WordInput" + accesskey="&wordEditField.accessKey;"/> + <spacer/> + </row> + <row> + <textbox id="WordInput" flex="1"/> + <button id="AddWord" oncommand="AddWord()" label="&AddButton.label;" + accesskey="&AddButton.accessKey;"/> + </row> + <row> + <label value="&DictionaryList.label;" + control="DictionaryList" + accesskey="&DictionaryList.accessKey;"/> + <spacer/> + </row> + <row> + <richlistbox id="DictionaryList" + class="theme-listbox" + flex="1" + height="150px"/> + <vbox flex="1"> + <button id="ReplaceWord" oncommand="ReplaceWord()" label="&ReplaceButton.label;" + accesskey="&ReplaceButton.accessKey;"/> + <spacer class="spacer"/> + <button id="RemoveWord" oncommand="RemoveWord()" label="&RemoveButton.label;" + accesskey="&RemoveButton.accessKey;"/> + <spacer class="spacer"/> + <spacer flex="1"/> + <button dlgtype="cancel" class="exit-dialog" id="close" label="&CloseButton.label;" + default="true" oncommand="onClose();" + accesskey="&CloseButton.accessKey;"/> + </vbox> + </row> + </rows> + </grid> +</dialog> 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 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog [ +<!ENTITY % edFieldSetProperties SYSTEM "chrome://editor/locale/EditorFieldSetProperties.dtd"> +%edFieldSetProperties; +<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd"> +%edDialogOverlay; +]> + +<dialog title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload="Startup();" + buttons="accept,cancel"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EdFieldSetProps.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <groupbox> + <hbox class="groupbox-title"> + <label class="header" accesskey="&Legend.accesskey;">&Legend.label;</label> + </hbox> + <grid><columns><column/><column/></columns> + <rows> + <row align="center"> + <checkbox id="EditText" label="&EditLegendText.label;" accesskey="&EditLegendText.accesskey;" checked="true" disabled="true" + oncommand="gDialog.legendText.disabled = !gDialog.editText.checked;"/> + <textbox id="LegendText" accesskey="&Legend.accesskey;"/> + </row> + <row align="center"> + <label control="LegendAlign" value="&LegendAlign.label;" accesskey="&LegendAlign.accesskey;"/> + <menulist id="LegendAlign"> + <menupopup> + <menuitem label="&AlignDefault.label;"/> + <menuitem label="&AlignLeft.label;" value="left"/> + <menuitem label="&AlignCenter.label;" value="center"/> + <menuitem label="&AlignRight.label;" value="right"/> + </menupopup> + </menulist> + </row> + </rows> + </grid> + </groupbox> + + <!-- from EdDialogOverlay --> + <hbox flex="1" style="margin-top: 0.2em"> + <button id="RemoveFieldSet" label="&RemoveFieldSet.label;" accesskey="&RemoveFieldSet.accesskey;" oncommand="RemoveFieldSet();"/> + <!-- This will right-align the button --> + <spacer flex="1"/> + <button id="AdvancedEditButton" + oncommand="onAdvancedEdit();" + label="&AdvancedEditButton.label;" + accesskey="&AdvancedEditButton.accessKey;" + tooltiptext="&AdvancedEditButton.tooltip;"/> + </hbox> + <separator class="groove"/> + +</dialog> 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 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> +<?xml-stylesheet href="chrome://messenger/skin/menulist.css" type="text/css"?> + +<!DOCTYPE dialog [ +<!ENTITY % edFormProperties SYSTEM "chrome://editor/locale/EditorFormProperties.dtd"> +%edFormProperties; +<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd"> +%edDialogOverlay; +]> + +<dialog title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload="Startup();" + buttons="accept,cancel"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EdFormProps.js"/> + + <script src="chrome://messenger/content/customElements.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <groupbox> + <hbox class="groupbox-title"> + <label class="header">&Settings.label;</label> + </hbox> + <grid><columns><column/><column/></columns> + <rows> + <row align="center"> + <label control="FormName" value="&FormName.label;" accesskey="&FormName.accesskey;"/> + <textbox id="FormName"/> + </row> + <row align="center"> + <label control="FormAction" value="&FormAction.label;" accesskey="&FormAction.accesskey;"/> + <textbox id="FormAction"/> + </row> + <row align="center"> + <label control="FormMethod" value="&FormMethod.label;" accesskey="&FormMethod.accesskey;"/> + <hbox> + <menulist is="menulist-editable" id="FormMethod" editable="true" autoSelectMenuitem="true"> + <menupopup> + <menuitem label="GET"/> + <menuitem label="POST"/> + </menupopup> + </menulist> + </hbox> + </row> + <hbox> + <button id="MoreFewerButton" oncommand="onMoreFewer();" persist="more"/> + </hbox> + <rows id="MoreSection"> + <row align="center"> + <label control="FormEncType" value="&FormEncType.label;" accesskey="&FormEncType.accesskey;"/> + <menulist is="menulist-editable" id="FormEncType" editable="true" autoSelectMenuitem="true"> + <menupopup> + <menuitem label="application/x-www-form-urlencoded"/> + <menuitem label="multipart/form-data"/> + <menuitem label="text/plain"/> + </menupopup> + </menulist> + </row> + <row align="center"> + <label control="FormTarget" value="&FormTarget.label;" accesskey="&FormTarget.accesskey;"/> + <menulist is="menulist-editable" id="FormTarget" editable="true" autoSelectMenuitem="true"> + <menupopup> + <menuitem label="_blank"/> + <menuitem label="_self"/> + <menuitem label="_parent"/> + <menuitem label="_top"/> + </menupopup> + </menulist> + </row> + </rows> + </rows> + </grid> + </groupbox> + + <!-- from EdDialogOverlay --> + <hbox flex="1" style="margin-top: 0.2em"> + <button id="RemoveForm" label="&RemoveForm.label;" accesskey="&RemoveForm.accesskey;" oncommand="RemoveForm();"/> + <!-- This will right-align the button --> + <spacer flex="1"/> + <button id="AdvancedEditButton" + oncommand="onAdvancedEdit();" + label="&AdvancedEditButton.label;" + accesskey="&AdvancedEditButton.accessKey;" + tooltiptext="&AdvancedEditButton.tooltip;"/> + </hbox> + <separator class="groove"/> +</dialog> 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 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog [ +<!ENTITY % edHLineProperties SYSTEM "chrome://editor/locale/EditorHLineProperties.dtd"> +%edHLineProperties; +<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd"> +%edDialogOverlay; +]> + +<dialog title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload="Startup()"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <!--- Element-specific methods --> + <script src="chrome://editor/content/EdHLineProps.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <groupbox> + <hbox class="groupbox-title"> + <label class="header">&dimensionsBox.label;</label> + </hbox> + <grid> + <columns><column/><column/><column /></columns> + <rows> + <row align="center"> + <label control="width" + value="&widthEditField.label;" + accesskey="&widthEditField.accessKey;"/> + <textbox class="narrow" id="width" flex="1" oninput="forceInteger('width')"/> + <menulist id="pixelOrPercentMenulist" /> + <!-- menupopup and menuitems added by JS --> + </row> + <row align="center"> + <label control="height" + value="&heightEditField.label;" + accesskey="&heightEditField.accessKey;"/> + <textbox class="narrow" id="height" oninput="forceInteger('height')"/> + <label value="&pixelsPopup.value;" /> + </row> + </rows> + </grid> + <checkbox id="3dShading" label="&threeDShading.label;" accesskey="&threeDShading.accessKey;"/> + </groupbox> + <groupbox> + <hbox class="groupbox-title"> + <label class="header">&alignmentBox.label;</label> + </hbox> + <radiogroup id="alignmentGroup" orient="horizontal"> + <spacer class="spacer"/> + <radio id="leftAlign" label="&leftRadio.label;" accesskey="&leftRadio.accessKey;"/> + <radio id="centerAlign" label="¢erRadio.label;" accesskey="¢erRadio.accessKey;"/> + <radio id="rightAlign" label="&rightRadio.label;" accesskey="&rightRadio.accessKey;"/> + </radiogroup> + </groupbox> + <spacer class="spacer"/> + <hbox> + <button id="SaveDefault" label="&saveSettings.label;" + accesskey="&saveSettings.accessKey;" + oncommand="onSaveDefault()" + tooltiptext="&saveSettings.tooltip;" /> + <spacer flex="1"/> + <button id="AdvancedEditButton" + oncommand="onAdvancedEdit();" + label="&AdvancedEditButton.label;" + accesskey="&AdvancedEditButton.accessKey;" + tooltiptext="&AdvancedEditButton.tooltip;"/> + </hbox> + <separator class="groove"/> +</dialog> 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 <input type="image"> + 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 @@ +<?xml version="1.0"?> +# 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/. + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog [ +<!ENTITY % edImageProperties SYSTEM "chrome://editor/locale/EditorImageProperties.dtd"> +%edImageProperties; +<!ENTITY % composeEditorOverlayDTD SYSTEM "chrome://messenger/locale/messengercompose/mailComposeEditorOverlay.dtd"> +%composeEditorOverlayDTD; +<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd"> +%edDialogOverlay; +]> + +<!-- dialog containing a control requiring initial setup --> +<dialog id="imageDlg" title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload="Startup()" + buttons="accept,cancel"> + + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EdImageProps.js"/> + <script src="chrome://editor/content/EdImageDialog.js"/> + <script src="chrome://editor/content/EdImageLinkLoader.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <tabbox id="TabBox"> + <tabs flex="1"> + <tab id="imageLocationTab" label="&imageLocationTab.label;"/> + <tab id="imageDimensionsTab" label="&imageDimensionsTab.label;"/> + <tab id="imageAppearanceTab" label="&imageAppearanceTab.label;"/> + <tab id="imageLinkTab" label="&imageLinkTab.label;"/> + </tabs> + <tabpanels> +#include edImage.inc.xhtml + <vbox> + <spacer class="spacer"/> + <vbox id="LinkLocationBox"> + <label control="hrefInput" + accesskey="&LinkURLEditField2.accessKey;" + width="1">&LinkURLEditField2.label;</label> + <textbox id="hrefInput" type="text" + class="uri-element padded" oninput="ChangeLinkLocation();"/> + <hbox align="center"> + <checkbox id="MakeRelativeLink" + for="hrefInput" + label="&makeUrlRelative.label;" + accesskey="&makeUrlRelative.accessKey;" + oncommand="MakeInputValueRelativeOrAbsolute(this);" + tooltiptext="&makeUrlRelative.tooltip;"/> + <spacer flex="1"/> + <button label="&chooseFileLinkButton.label;" accesskey="&chooseFileLinkButton.accessKey;" + oncommand="chooseLinkFile();"/> + </hbox> + </vbox> + <spacer class="spacer"/> + <hbox> + <checkbox id="showLinkBorder" + label="&showImageLinkBorder.label;" + accesskey="&showImageLinkBorder.accessKey;" + oncommand="ToggleShowLinkBorder();"/> + <spacer flex="1"/> + <button id="LinkAdvancedEditButton" + label="&LinkAdvancedEditButton.label;" + accesskey="&LinkAdvancedEditButton.accessKey;" + tooltiptext="&LinkAdvancedEditButton.tooltip;" + oncommand="onLinkAdvancedEdit();"/> + </hbox> + </vbox> + </tabpanels> + </tabbox> + + <hbox align="end"> + <groupbox id="imagePreview" orient="horizontal" flex="1"> + <hbox class="groupbox-title"> + <label class="header">&previewBox.label;</label> + </hbox> + <hbox id="preview-image-box" align="center"> + <spacer flex="1"/> + <description id="preview-image-holder"/> + <spacer flex="1"/> + </hbox> + <vbox id="PreviewSize" collapsed="true"> + <spacer flex="1"/> + <label value="&actualSize.label;"/> + <hbox> + <label value="&widthEditField.label;"/> + <spacer flex="1"/> + <label id="PreviewWidth"/> + </hbox> + <hbox> + <label value="&heightEditField.label;"/> + <spacer flex="1"/> + <label id="PreviewHeight"/> + </hbox> + <spacer flex="1"/> + </vbox> + </groupbox> + + <vbox id="AdvancedEdit"> + <hbox flex="1" style="margin-top: 0.2em" align="center"> + <!-- This will right-align the button --> + <spacer flex="1"/> + <button id="AdvancedEditButton1" oncommand="onAdvancedEdit()" label="&AdvancedEditButton.label;" + accesskey="&AdvancedEditButton.accessKey;" tooltiptext="&AdvancedEditButton.tooltip;"/> + </hbox> + </vbox> + </hbox> + <separator class="groove"/> + +</dialog> 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 @@ +<?xml version="1.0"?> +# 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/. + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog [ +<!ENTITY % edInputProperties SYSTEM "chrome://editor/locale/EditorInputProperties.dtd"> +%edInputProperties; +<!ENTITY % edImageProperties SYSTEM "chrome://editor/locale/EditorImageProperties.dtd"> +%edImageProperties; +<!ENTITY % composeEditorOverlayDTD SYSTEM "chrome://messenger/locale/messengercompose/mailComposeEditorOverlay.dtd"> +%composeEditorOverlayDTD; +<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd"> +%edDialogOverlay; +]> + +<dialog title="&windowTitleImage.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload="Startup();"> + + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EdInputImage.js"/> + <script src="chrome://editor/content/EdImageDialog.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <tabbox id="TabBox"> + <tabs flex="1"> + <tab id="imageInputTab" label="&imageInputTab.label;"/> + <tab id="imageLocationTab" label="&imageLocationTab.label;"/> + <tab id="imageDimensionsTab" label="&imageDimensionsTab.label;"/> + <tab id="imageAppearanceTab" label="&imageAppearanceTab.label;"/> + </tabs> + <tabpanels> + <groupbox> + <hbox class="groupbox-title"> + <label class="header">&InputSettings.label;</label> + </hbox> + <grid><columns><column/><column/></columns> + <rows> + <row align="center"> + <label value="&InputName.label;"/> + <textbox id="InputName"/> + </row> + <row> + <spacer/> + <checkbox id="InputDisabled" label="&InputDisabled.label;"/> + </row> + <row align="center"> + <label value="&tabIndex.label;"/> + <hbox> + <textbox id="InputTabIndex" class="narrow" oninput="forceInteger(this.id);"/> + </hbox> + </row> + </rows> + </grid> + </groupbox> +#include edImage.inc.xhtml + </tabpanels> + </tabbox> + + <hbox align="end"> + <groupbox id="imagePreview" orient="horizontal" flex="1"> + <hbox class="groupbox-title"> + <label class="header">&previewBox.label;</label> + </hbox> + <hbox id="preview-image-box" align="center"> + <spacer flex="1"/> + <description id="preview-image-holder"/> + <spacer flex="1"/> + </hbox> + <vbox id="PreviewSize" collapsed="true"> + <spacer flex="1"/> + <label value="&actualSize.label;"/> + <hbox> + <label value="&widthEditField.label;"/> + <spacer flex="1"/> + <label id="PreviewWidth"/> + </hbox> + <hbox> + <label value="&heightEditField.label;"/> + <spacer flex="1"/> + <label id="PreviewHeight"/> + </hbox> + <spacer flex="1"/> + </vbox> + </groupbox> + + <vbox id="AdvancedEdit"> + <hbox flex="1" style="margin-top: 0.2em" align="center"> + <!-- This will right-align the button --> + <spacer flex="1"/> + <button id="AdvancedEditButton1" oncommand="onAdvancedEdit()" label="&AdvancedEditButton.label;" + accesskey="&AdvancedEditButton.accessKey;" tooltiptext="&AdvancedEditButton.tooltip;"/> + </hbox> + <separator id="advancedSeparator" class="groove"/> + </vbox> + </hbox> + +</dialog> 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 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog [ +<!ENTITY % edInputProperties SYSTEM "chrome://editor/locale/EditorInputProperties.dtd"> +%edInputProperties; +<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd"> +%edDialogOverlay; +]> + +<dialog title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload="Startup();" + buttons="accept,cancel"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EdInputProps.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <groupbox> + <hbox class="groupbox-title"> + <label class="header" control="InputType" accesskey="&InputType.accesskey;">&InputType.label;</label> + </hbox> + <menulist id="InputType" oncommand="SelectInputType();"> + <menupopup> + <menuitem label="&text.value;"/> + <menuitem label="&password.value;"/> + <menuitem label="&checkbox.value;"/> + <menuitem label="&radio.value;"/> + <menuitem label="&submit.value;"/> + <menuitem label="&reset.value;"/> + <menuitem label="&file.value;"/> + <menuitem label="&hidden.value;"/> + <menuitem label="&image.value;"/> + <menuitem label="&button.value;"/> + </menupopup> + </menulist> + </groupbox> + + <groupbox> + <hbox class="groupbox-title"> + <label class="header">&InputSettings.label;</label> + </hbox> + <grid><columns><column/><column/></columns> + <rows> + <row align="center"> + <deck id="InputNameDeck"> + <label control="InputName" value="&InputName.label;" accesskey="&InputName.accesskey;"/> + <label control="InputName" value="&GroupName.label;" accesskey="&GroupName.accesskey;"/> + </deck> + <textbox id="InputName" oninput="onInput();"/> + </row> + <row align="center"> + <deck id="InputValueDeck"> + <label control="InputValue" value="&InputValue.label;" accesskey="&InputValue.accesskey;"/> + <label control="InputValue" value="&InitialValue.label;" accesskey="&InitialValue.accesskey;"/> + </deck> + <textbox id="InputValue" oninput="onInput();"/> + </row> + <row> + <spacer/> + <deck id="InputDeck" persist="index"> + <checkbox id="InputChecked" label="&InputChecked.label;" accesskey="&InputChecked.accesskey;"/> + <checkbox id="InputSelected" label="&InputSelected.label;" accesskey="&InputSelected.accesskey;"/> + <checkbox id="InputReadOnly" label="&InputReadOnly.label;" accesskey="&InputReadOnly.accesskey;"/> + </deck> + </row> + </rows> + </grid> + <hbox> + <button id="MoreFewerButton" oncommand="onMoreFewer();" persist="more"/> + </hbox> + <grid id="MoreSection" align="start"> + <columns><column/><column/></columns> + <rows> + <row> + <spacer/> + <checkbox id="InputDisabled" label="&InputDisabled.label;" accesskey="&InputDisabled.accesskey;"/> + </row> + <row align="center"> + <label control="InputTabIndex" value="&tabIndex.label;" accesskey="&tabIndex.accesskey;"/> + <hbox> + <textbox id="InputTabIndex" class="narrow" oninput="forceInteger(this.id);"/> + </hbox> + </row> + <row align="center"> + <label control="InputAccessKey" value="&AccessKey.label;" accesskey="&AccessKey.accesskey;"/> + <hbox> + <textbox id="InputAccessKey" class="narrow"/> + </hbox> + </row> + <row align="center"> + <label control="InputSize" value="&TextSize.label;" accesskey="&TextSize.accesskey;"/> + <hbox> + <textbox id="InputSize" class="narrow" oninput="forceInteger(this.id);"/> + </hbox> + </row> + <row align="center"> + <label control="InputMaxLength" value="&TextLength.label;" accesskey="&TextLength.accesskey;"/> + <hbox> + <textbox id="InputMaxLength" class="narrow" oninput="forceInteger(this.id);"/> + </hbox> + </row> + <row align="center"> + <label control="InputAccept" value="&Accept.label;" accesskey="&Accept.accesskey;"/> + <textbox id="InputAccept"/> + </row> + </rows> + </grid> + </groupbox> + + <!-- from EdDialogOverlay --> + <hbox flex="1" style="margin-top: 0.2em"> + <!-- This will right-align the button --> + <spacer flex="1"/> + <deck id="AdvancedEditDeck"> + <button id="AdvancedEditButton" + oncommand="onAdvancedEdit();" + label="&AdvancedEditButton.label;" + accesskey="&AdvancedEditButton.accessKey;" + tooltiptext="&AdvancedEditButton.tooltip;"/> + <button label="&ImageProperties.label;" accesskey="&ImageProperties.accesskey;" oncommand="doImageProperties();"/> + </deck> + </hbox> + <separator class="groove"/> + +</dialog> 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(/<body[^>]*>/, "").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 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorInsertSource.dtd"> + +<dialog title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml" + onload = "Startup()" + buttonlabelaccept="&insertButton.label;" + buttonaccesskeyaccept="&insertButton.accesskey;"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://global/content/globalOverlay.js"/> + <script src="chrome://global/content/editMenuOverlay.js"/> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EdInsSrc.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <label id="srcMessage" value="&sourceEditField.label;"/> + <vbox flex="1" style="width: 30em; height: 20em;"> + <html:textarea id="srcInput" rows="18" flex="1"/> + </vbox> + <!-- Will this accept the embedded HTML tags? --> + <hbox> + <spacer class="bigspacer"/> + <label value="&example.label;"/> + <label class="bold" value="&exampleOpenTag.label;"/> + <label class="bold italic" value="&exampleText.label;"/> + <label class="bold" value="&exampleCloseTag.label;"/> + </hbox> + <spacer class="spacer"/> + <separator class="groove"/> +</dialog> 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 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorInsertChars.dtd"> + +<dialog id="insertCharsDlg" title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload = "Startup()" + onfocus = "onFocus()" + buttonlabelaccept="&insertButton.label;" + buttonlabelcancel="&closeButton.label;" + style = "width: 20em"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EdInsertChars.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <groupbox> + <hbox class="groupbox-title"> + <label class="header">&category.label;</label> + </hbox> + <radiogroup id="CatGrp" persist="category letter_index char_index"> + <radio id="AccentUpper" label="&accentUpper.label;" oncommand="ChangeCategory(this.id)"/> + <radio id="AccentLower" label="&accentLower.label;" oncommand="ChangeCategory(this.id)"/> + <radio id="Upper" label="&otherUpper.label;" oncommand="ChangeCategory(this.id)"/> + <radio id="Lower" label="&otherLower.label;" oncommand="ChangeCategory(this.id)"/> + <radio id="Symbol" label="&commonSymbols.label;" oncommand="ChangeCategory(this.id)"/> + </radiogroup> + <spacer class="spacer"/> + </groupbox> + <hbox equalsize="always"> + <vbox flex="1"> + <!-- value is set in JS from editor.properties strings --> + <label id="LatinL_Label" control="LatinL" value="&letter.label;" accesskey="&letter.accessKey;"/> + <menulist class="larger" flex="1" id="LatinL" oncommand="SelectLatinLetter()"> + <menupopup/> + </menulist> + </vbox> + <vbox flex="1"> + <label id="LatinM_Label" control="LatinM" value="&character.label;" accesskey="&character.accessKey;"/> + <menulist class="larger" flex="1" id="LatinM" oncommand="SelectLatinModifier()"> + <menupopup/> + </menulist> + </vbox> + </hbox> + <separator class="groove"/> +</dialog> 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 <math> and initialize the dialog. + gDialog.oldMath = editor.getElementOrParentByTagName("math", null); + if (gDialog.oldMath) { + // When these attributes are absent or invalid, they default to "inline" and "ltr" respectively. + gDialog.mode.selectedIndex = + gDialog.oldMath.getAttribute("display") == "block" ? 1 : 0; + gDialog.direction.selectedIndex = + gDialog.oldMath.getAttribute("dir") == "rtl" ? 1 : 0; + gDialog.input.value = TeXZilla.getTeXSource(gDialog.oldMath); + } + + // Create the tabbox with LaTeX commands. + createCommandPanel({ + "√⅗²": [ + "{⋯}^{⋯}", + "{⋯}_{⋯}", + "{⋯}_{⋯}^{⋯}", + "\\underset{⋯}{⋯}", + "\\overset{⋯}{⋯}", + "\\underoverset{⋯}{⋯}{⋯}", + "\\left(⋯\\right)", + "\\left[⋯\\right]", + "\\frac{⋯}{⋯}", + "\\binom{⋯}{⋯}", + "\\sqrt{⋯}", + "\\sqrt[⋯]{⋯}", + "\\cos\\left({⋯}\\right)", + "\\sin\\left({⋯}\\right)", + "\\tan\\left({⋯}\\right)", + "\\exp\\left({⋯}\\right)", + "\\ln\\left({⋯}\\right)", + "\\underbrace{⋯}", + "\\underline{⋯}", + "\\overbrace{⋯}", + "\\widevec{⋯}", + "\\widetilde{⋯}", + "\\widehat{⋯}", + "\\widecheck{⋯}", + "\\widebar{⋯}", + "\\dot{⋯}", + "\\ddot{⋯}", + "\\boxed{⋯}", + "\\slash{⋯}", + ], + "(▦)": [ + "\\begin{matrix} ⋯ & ⋯ \\\\ ⋯ & ⋯ \\end{matrix}", + "\\begin{pmatrix} ⋯ & ⋯ \\\\ ⋯ & ⋯ \\end{pmatrix}", + "\\begin{bmatrix} ⋯ & ⋯ \\\\ ⋯ & ⋯ \\end{bmatrix}", + "\\begin{Bmatrix} ⋯ & ⋯ \\\\ ⋯ & ⋯ \\end{Bmatrix}", + "\\begin{vmatrix} ⋯ & ⋯ \\\\ ⋯ & ⋯ \\end{vmatrix}", + "\\begin{Vmatrix} ⋯ & ⋯ \\\\ ⋯ & ⋯ \\end{Vmatrix}", + "\\begin{cases} ⋯ \\\\ ⋯ \\end{cases}", + "\\begin{aligned} ⋯ &= ⋯ \\\\ ⋯ &= ⋯ \\end{aligned}", + ], + }); + createSymbolPanels([ + "∏∐∑∫∬∭⨌∮⊎⊕⊖⊗⊘⊙⋀⋁⋂⋃⌈⌉⌊⌋⎰⎱⟨⟩⟪⟫∥⫼⨀⨁⨂⨄⨅⨆ðıȷℏℑℓ℘ℜℵℶ", + "∀∃∄∅∉∊∋∌⊂⊃⊄⊅⊆⊇⊈⊈⊉⊊⊊⊋⊋⊏⊐⊑⊒⊓⊔⊥⋐⋑⋔⫅⫆⫋⫋⫌⫌…⋮⋯⋰⋱♭♮♯∂∇", + "±×÷†‡•∓∔∗∘∝∠∡∢∧∨∴∵∼∽≁≃≅≇≈≈≊≍≎≏≐≑≒≓≖≗≜≡≢≬⊚⊛⊞⊡⊢⊣⊤⊥", + "⊨⊩⊪⊫⊬⊭⊯⊲⊲⊳⊴⊵⊸⊻⋄⋅⋇⋈⋉⋊⋋⋌⋍⋎⋏⋒⋓⌅⌆⌣△▴▵▸▹▽▾▿◂◃◊○★♠♡♢♣⧫", + "≦≧≨≩≩≪≫≮≯≰≱≲≳≶≷≺≻≼≽≾≿⊀⊁⋖⋗⋘⋙⋚⋛⋞⋟⋦⋧⋨⋩⩽⩾⪅⪆⪇⪈⪉⪊⪋⪌⪕⪯⪰⪷⪸⪹⪺", + "←↑→↓↔↕↖↗↘↙↜↝↞↠↢↣↦↩↪↫↬↭↭↰↱↼↽↾↿⇀⇁⇂⇃⇄⇆⇇⇈⇉⇊⇋⇌⇐⇑⇒⇓⇕⇖⇗⇘⇙⟺", + "αβγδϵ϶εζηθϑικϰλμνξℴπϖρϱσςτυϕφχψωΓΔΘΛΞΠΣϒΦΨΩϝ℧", + "𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫𝔸𝔹ℂ𝔻𝔼𝔽𝔾ℍ𝕀𝕁𝕂𝕃𝕄ℕ𝕆ℙℚℝ𝕊𝕋𝕌𝕍𝕎𝕏𝕐ℤ", + "𝒶𝒷𝒸𝒹ℯ𝒻ℊ𝒽𝒾𝒿𝓀𝓁𝓂𝓃ℴ𝓅𝓆𝓇𝓈𝓉𝓊𝓋𝓌𝓍𝓎𝓏𝒜ℬ𝒞𝒟ℰℱ𝒢ℋℐ𝒥𝒦ℒℳ𝒩𝒪𝒫𝒬ℛ𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵", + "𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷𝔄𝔅ℭ𝔇𝔈𝔉𝔊ℌℑ𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔ℜ𝔖𝔗𝔘𝔙𝔚𝔛𝔜ℨ", + ]); + gDialog.tabbox.selectedIndex = 0; + + updateMath(); + + SetWindowLocation(); +} + +function insertLaTeXCommand(aButton) { + gDialog.input.focus(); + + // For a single math symbol, just use the insertText command. + if (aButton.label) { + gDialog.input.editor.insertText(aButton.label); + return; + } + + // Otherwise, it's a LaTeX command with at least one argument... + var latex = TeXZilla.getTeXSource(aButton.firstChild); + var selectionStart = gDialog.input.selectionStart; + var selectionEnd = gDialog.input.selectionEnd; + + // If the selection is not empty, we replace the first argument of the LaTeX + // command with the current selection. + var selection = gDialog.input.value.substring(selectionStart, selectionEnd); + if (selection != "") { + latex = latex.replace("⋯", selection); + } + + // Try and move to the next position. + var latexNewStart = latex.indexOf("⋯"), + latexNewEnd; + if (latexNewStart == -1) { + // This is a unary function and the selection was used as an argument above. + // We select the expression again so that one can choose to apply further + // command to it or just move the caret after that text. + latexNewStart = 0; + latexNewEnd = latex.length; + } else { + // Otherwise, select the dots representing the next argument. + latexNewEnd = latexNewStart + 1; + } + + // Update the input text and selection. + gDialog.input.editor.insertText(latex); + gDialog.input.setSelectionRange( + selectionStart + latexNewStart, + selectionStart + latexNewEnd + ); + + updateMath(); +} + +function createCommandPanel(aCommandPanelList) { + const columnCount = 10; + + for (var label in aCommandPanelList) { + var commands = aCommandPanelList[label]; + + // Create a <rows> element with some LaTeX commands. + var rows = document.createXULElement("rows"); + + var i = 0, + row; + for (var command of commands) { + if (i % columnCount == 0) { + // Create a new row. + row = document.createXULElement("row"); + rows.appendChild(row); + } + + // Create a new button to insert the symbol. + var button = document.createXULElement("toolbarbutton"); + button.setAttribute("class", "tabbable"); + button.appendChild(TeXZilla.toMathML(command)); + row.appendChild(button); + + i++; + } + + // Create a <columns> element with the desired number of columns. + var columns = document.createXULElement("columns"); + for (i = 0; i < columnCount; i++) { + var column = document.createXULElement("column"); + column.setAttribute("flex", "1"); + columns.appendChild(column); + } + + // Create the <grid> element with the <rows> and <columns> children. + var grid = document.createXULElement("grid"); + grid.appendChild(columns); + grid.appendChild(rows); + + // Create a new <tab> element. + var tab = document.createXULElement("tab"); + tab.setAttribute("label", label); + gDialog.tabbox.tabs.appendChild(tab); + + // Append the new tab panel. + gDialog.tabbox.tabpanels.appendChild(grid); + } +} + +function createSymbolPanels(aSymbolPanelList) { + const columnCount = 13, + tabLabelLength = 3; + + for (var symbols of aSymbolPanelList) { + // Create a <rows> element with the symbols of the i-th panel. + var rows = document.createXULElement("rows"); + var i = 0, + tabLabel = "", + row; + for (var symbol of symbols) { + if (i % columnCount == 0) { + // Create a new row. + row = document.createXULElement("row"); + rows.appendChild(row); + } + + // Build the tab label from the first symbols of this tab. + if (i < tabLabelLength) { + tabLabel += symbol; + } + + // Create a new button to insert the symbol. + var button = document.createXULElement("toolbarbutton"); + button.setAttribute("label", symbol); + button.setAttribute("class", "tabbable"); + row.appendChild(button); + + i++; + } + + // Create a <columns> element with the desired number of columns. + var columns = document.createXULElement("columns"); + for (i = 0; i < columnCount; i++) { + var column = document.createXULElement("column"); + column.setAttribute("flex", "1"); + columns.appendChild(column); + } + + // Create the <grid> element with the <rows> and <columns> children. + var grid = document.createXULElement("grid"); + grid.appendChild(columns); + grid.appendChild(rows); + + // Create a new <tab> element with the label determined above. + var tab = document.createXULElement("tab"); + tab.setAttribute("label", tabLabel); + gDialog.tabbox.tabs.appendChild(tab); + + // Append the new tab panel. + gDialog.tabbox.tabpanels.appendChild(grid); + } +} + +function onAccept(event) { + if (gDialog.output.firstChild) { + var editor = GetCurrentEditor(); + editor.beginTransaction(); + + try { + var newMath = editor.document.importNode(gDialog.output.firstChild, true); + if (gDialog.oldMath) { + // Replace the old <math> element with the new one. + editor.selectElement(gDialog.oldMath); + editor.insertElementAtSelection(newMath, true); + } else { + // Insert the new <math> element. + editor.insertElementAtSelection(newMath, false); + } + } catch (e) {} + + editor.endTransaction(); + } else { + dump("Null value -- not inserting in MathML Source dialog\n"); + event.preventDefault(); + } + SaveWindowLocation(); +} + +function updateMath() { + // Remove the preview, if any. + if (gDialog.output.firstChild) { + gDialog.output.firstChild.remove(); + } + + // Try to convert the LaTeX source into MathML using TeXZilla. + // We use the placeholder text if no input is provided. + try { + var input = gDialog.input.value || gDialog.input.placeholder; + var newMath = TeXZilla.toMathML( + input, + gDialog.mode.selectedIndex, + gDialog.direction.selectedIndex, + true + ); + gDialog.output.appendChild(document.importNode(newMath, true)); + gDialog.output.style.opacity = gDialog.input.value ? 1 : 0.5; + } catch (e) {} + // Disable the accept button if parsing fails or when the placeholder is used. + gDialog.accept.disabled = !gDialog.input.value || !gDialog.output.firstChild; +} + +function updateMode() { + if (gDialog.output.firstChild) { + gDialog.output.firstChild.setAttribute( + "display", + gDialog.mode.selectedIndex ? "block" : "inline" + ); + } +} + +function updateDirection() { + if (gDialog.output.firstChild) { + gDialog.output.firstChild.setAttribute( + "dir", + gDialog.direction.selectedIndex ? "rtl" : "ltr" + ); + } +} diff --git a/comm/suite/editor/components/dialogs/content/EdInsertMath.xhtml b/comm/suite/editor/components/dialogs/content/EdInsertMath.xhtml new file mode 100644 index 0000000000..9138d00846 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdInsertMath.xhtml @@ -0,0 +1,60 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorInsertMath.dtd"> + +<dialog title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml" + onload="Startup();" + buttonlabelaccept="&insertButton.label;" + buttonaccesskeyaccept="&insertButton.accesskey;"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://global/content/globalOverlay.js"/> + <script src="chrome://global/content/editMenuOverlay.js"/> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EdInsertMath.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <label id="srcMessage" value="&sourceEditField.label;"/> + <html:textarea id="input" rows="5" oninput="updateMath();" + placeholder="\sqrt{x_1} + \frac{π^3}{2}"/> + <vbox flex="1" style="overflow: auto; width: 30em; height: 5em;"> + <description id="output"/> + </vbox> + <tabbox id="tabboxInsertLaTeXCommand"> + <tabs/> + <tabpanels oncommand="insertLaTeXCommand(event.target);"/> + </tabbox> + <spacer class="spacer"/> + <groupbox> + <hbox class="groupbox-title"> + <label class="header">&options.label;</label> + </hbox> + <hbox> + <radiogroup id="optionMode" oncommand="updateMode();"> + <radio label="&optionInline.label;" + accesskey="&optionInline.accesskey;"/> + <radio label="&optionDisplay.label;" + accesskey="&optionDisplay.accesskey;"/> + </radiogroup> + <radiogroup id="optionDirection" oncommand="updateDirection();"> + <radio label="&optionLTR.label;" + accesskey="&optionLTR.accesskey;"/> + <radio label="&optionRTL.label;" + accesskey="&optionRTL.accesskey;"/> + </radiogroup> + </hbox> + </groupbox> + <spacer class="spacer"/> + <separator class="groove"/> +</dialog> diff --git a/comm/suite/editor/components/dialogs/content/EdInsertTOC.js b/comm/suite/editor/components/dialogs/content/EdInsertTOC.js new file mode 100644 index 0000000000..3ec386f7c9 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdInsertTOC.js @@ -0,0 +1,378 @@ +/* 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 */ + +// tocHeadersArray is the array containing the pairs tag/class +// defining TOC entries +var tocHeadersArray = new Array(6); + +// a global used when building the TOC +var currentHeaderLevel = 0; + +// a global set to true if the TOC is to be readonly +var readonly = false; + +// a global set to true if user wants indexes in the TOC +var orderedList = true; + +// constants +const kMozToc = "mozToc"; +const kMozTocLength = 6; +const kMozTocIdPrefix = "mozTocId"; +const kMozTocIdPrefixLength = 8; +const kMozTocClassPrefix = "mozToc"; +const kMozTocClassPrefixLength = 6; + +document.addEventListener("dialogaccept", () => BuildTOC(true)); + +// Startup() is called when EdInsertTOC.xhtml is opened +function Startup() { + // early way out if if we have no editor + if (!GetCurrentEditor()) { + window.close(); + return; + } + + var i; + // clean the table of tag/class pairs we look for + for (i = 0; i < 6; ++i) { + tocHeadersArray[i] = ["", ""]; + } + + // reset all settings + for (i = 1; i < 7; ++i) { + var menulist = document.getElementById("header" + i + "Menulist"); + var menuitem = document.getElementById("header" + i + "none"); + var textbox = document.getElementById("header" + i + "Class"); + menulist.selectedItem = menuitem; + textbox.setAttribute("disabled", "true"); + } + + var theDocument = GetCurrentEditor().document; + + // do we already have a TOC in the document ? It should have "mozToc" ID + var toc = theDocument.getElementById(kMozToc); + + // default TOC definition, use h1-h6 for TOC entry levels 1-6 + var headers = "h1 1 h2 2 h3 3 h4 4 h5 5 h6 6"; + + var orderedListCheckbox = document.getElementById("orderedListCheckbox"); + orderedListCheckbox.checked = true; + + if (toc) { + // man, there is already a TOC here + + if (toc.getAttribute("class") == "readonly") { + // and it's readonly + var checkbox = document.getElementById("readOnlyCheckbox"); + checkbox.checked = true; + readonly = true; + } + + // let's see if it's an OL or an UL + orderedList = toc.nodeName.toLowerCase() == "ol"; + orderedListCheckbox.checked = orderedList; + + var nodeList = toc.childNodes; + // let's look at the children of the TOC ; if we find a comment beginning + // with "mozToc", it contains the TOC definition + for (i = 0; i < nodeList.length; ++i) { + if ( + nodeList.item(i).nodeType == Node.COMMENT_NODE && + nodeList.item(i).data.startsWith(kMozToc) + ) { + // yep, there is already a definition here; parse it ! + headers = nodeList + .item(i) + .data.substr( + kMozTocLength + 1, + nodeList.item(i).length - kMozTocLength - 1 + ); + break; + } + } + } + + // let's get an array filled with the (tag.class, index level) pairs + var headersArray = headers.split(" "); + + for (i = 0; i < headersArray.length; i += 2) { + var tag = headersArray[i], + className = ""; + var index = headersArray[i + 1]; + menulist = document.getElementById("header" + index + "Menulist"); + if (menulist) { + var sep = tag.indexOf("."); + if (sep != -1) { + // the tag variable contains in fact "tag.className", let's parse + // the class and get the real tag name + var tmp = tag.substr(0, sep); + className = tag.substr(sep + 1, tag.length - sep - 1); + tag = tmp; + } + + // update the dialog + menuitem = document.getElementById("header" + index + tag.toUpperCase()); + textbox = document.getElementById("header" + index + "Class"); + menulist.selectedItem = menuitem; + if (tag != "") { + textbox.removeAttribute("disabled"); + } + if (className != "") { + textbox.value = className; + } + tocHeadersArray[index - 1] = [tag, className]; + } + } +} + +function BuildTOC(update) { + // controlClass() is a node filter that accepts a node if + // (a) we don't look for a class (b) we look for a class and + // node has it + function controlClass(node, index) { + currentHeaderLevel = index + 1; + if (tocHeadersArray[index][1] == "") { + // we are not looking for a specific class, this node is ok + return NodeFilter.FILTER_ACCEPT; + } + if (node.getAttribute("class")) { + // yep, we look for a class, let's look at all the classes + // the node has + var classArray = node.getAttribute("class").split(" "); + for (var j = 0; j < classArray.length; j++) { + if (classArray[j] == tocHeadersArray[index][1]) { + // hehe, we found it... + return NodeFilter.FILTER_ACCEPT; + } + } + } + return NodeFilter.FILTER_SKIP; + } + + // the main node filter for our node iterator + // it selects the tag names as specified in the dialog + // then calls the controlClass filter above + function acceptNode(node) { + switch (node.nodeName.toLowerCase()) { + case tocHeadersArray[0][0]: + return controlClass(node, 0); + case tocHeadersArray[1][0]: + return controlClass(node, 1); + case tocHeadersArray[2][0]: + return controlClass(node, 2); + case tocHeadersArray[3][0]: + return controlClass(node, 3); + case tocHeadersArray[4][0]: + return controlClass(node, 4); + case tocHeadersArray[5][0]: + return controlClass(node, 5); + default: + return NodeFilter.FILTER_SKIP; + } + } + + var editor = GetCurrentEditor(); + var theDocument = editor.document; + // let's create a TreeWalker to look for our nodes + var treeWalker = theDocument.createTreeWalker( + theDocument.documentElement, + NodeFilter.SHOW_ELEMENT, + acceptNode, + true + ); + // we need an array to store all TOC entries we find in the document + var tocArray = []; + if (treeWalker) { + var tocSourceNode = treeWalker.nextNode(); + while (tocSourceNode) { + var headerIndex = currentHeaderLevel; + + // we have a node, we need to get all its textual contents + var textTreeWalker = theDocument.createTreeWalker( + tocSourceNode, + NodeFilter.SHOW_TEXT, + null, + true + ); + var textNode = textTreeWalker.nextNode(), + headerText = ""; + while (textNode) { + headerText += textNode.data; + textNode = textTreeWalker.nextNode(); + } + + var anchor = tocSourceNode.firstChild, + id; + // do we have a named anchor as 1st child of our node ? + if ( + anchor.nodeName.toLowerCase() == "a" && + anchor.hasAttribute("name") && + anchor.getAttribute("name").startsWith(kMozTocIdPrefix) + ) { + // yep, get its name + id = anchor.getAttribute("name"); + } else { + // no we don't and we need to create one + anchor = theDocument.createElement("a"); + tocSourceNode.insertBefore(anchor, tocSourceNode.firstChild); + // let's give it a random ID + var c = 1000000 * Math.random(); + id = kMozTocIdPrefix + Math.round(c); + anchor.setAttribute("name", id); + anchor.setAttribute( + "class", + kMozTocClassPrefix + tocSourceNode.nodeName.toUpperCase() + ); + } + // and store that new entry in our array + tocArray.push(headerIndex, headerText, id); + tocSourceNode = treeWalker.nextNode(); + } + } + + /* generate the TOC itself */ + headerIndex = 0; + var item, toc; + for (var i = 0; i < tocArray.length; i += 3) { + if (!headerIndex) { + // do we need to create an ol/ul container for the first entry ? + ++headerIndex; + toc = theDocument.getElementById(kMozToc); + if (!toc || !update) { + // we need to create a list container for the table of contents + toc = GetCurrentEditor().createElementWithDefaults( + orderedList ? "ol" : "ul" + ); + // grrr, we need to create a LI inside the list otherwise + // Composer will refuse an empty list and will remove it ! + var pit = theDocument.createElement("li"); + toc.appendChild(pit); + GetCurrentEditor().insertElementAtSelection(toc, true); + // ah, now it's inserted so let's remove the useless list item... + toc.removeChild(pit); + // we need to recognize later that this list is our TOC + toc.setAttribute("id", kMozToc); + } else if (orderedList != (toc.nodeName.toLowerCase() == "ol")) { + // we have to update an existing TOC, is the existing TOC of the + // desired type (ordered or not) ? + + // nope, we have to recreate the list + var newToc = GetCurrentEditor().createElementWithDefaults( + orderedList ? "ol" : "ul" + ); + toc.parentNode.insertBefore(newToc, toc); + // and remove the old one + toc.remove(); + toc = newToc; + toc.setAttribute("id", kMozToc); + } else { + // we can keep the list itself but let's get rid of the TOC entries + while (toc.hasChildNodes()) { + toc.lastChild.remove(); + } + } + + var commentText = "mozToc "; + for (var j = 0; j < 6; j++) { + if (tocHeadersArray[j][0] != "") { + commentText += tocHeadersArray[j][0]; + if (tocHeadersArray[j][1] != "") { + commentText += "." + tocHeadersArray[j][1]; + } + commentText += " " + (j + 1) + " "; + } + } + // important, we have to remove trailing spaces + commentText = TrimStringRight(commentText); + + // forge a comment we'll insert in the TOC ; that comment will hold + // the TOC definition for us + var ct = theDocument.createComment(commentText); + toc.appendChild(ct); + + // assign a special class to the TOC top element if the TOC is readonly + // the definition of this class is in EditorOverride.css + if (readonly) { + toc.setAttribute("class", "readonly"); + } else { + toc.removeAttribute("class"); + } + + // We need a new variable to hold the local ul/ol container + // The toplevel TOC element is not the parent element of a + // TOC entry if its depth is > 1... + var tocList = toc; + // create a list item + var tocItem = theDocument.createElement("li"); + // and an anchor in this list item + var tocAnchor = theDocument.createElement("a"); + // make it target the source of the TOC entry + tocAnchor.setAttribute("href", "#" + tocArray[i + 2]); + // and put the textual contents of the TOC entry in that anchor + var tocEntry = theDocument.createTextNode(tocArray[i + 1]); + // now, insert everything where it has to be inserted + tocAnchor.appendChild(tocEntry); + tocItem.appendChild(tocAnchor); + tocList.appendChild(tocItem); + item = tocList; + } else { + if (tocArray[i] < headerIndex) { + // if the depth of the new TOC entry is less than the depth of the + // last entry we created, find the good ul/ol ancestor + for (j = headerIndex - tocArray[i]; j > 0; --j) { + if (item != toc) { + item = item.parentNode.parentNode; + } + } + tocItem = theDocument.createElement("li"); + } else if (tocArray[i] > headerIndex) { + // to the contrary, it's deeper than the last one + // we need to create sub ul/ol's and li's + for (j = tocArray[i] - headerIndex; j > 0; --j) { + tocList = theDocument.createElement(orderedList ? "ol" : "ul"); + item.lastChild.appendChild(tocList); + tocItem = theDocument.createElement("li"); + tocList.appendChild(tocItem); + item = tocList; + } + } else { + tocItem = theDocument.createElement("li"); + } + tocAnchor = theDocument.createElement("a"); + tocAnchor.setAttribute("href", "#" + tocArray[i + 2]); + tocEntry = theDocument.createTextNode(tocArray[i + 1]); + tocAnchor.appendChild(tocEntry); + tocItem.appendChild(tocAnchor); + item.appendChild(tocItem); + headerIndex = tocArray[i]; + } + } + SaveWindowLocation(); +} + +function selectHeader(elt, index) { + var tag = elt.value; + tocHeadersArray[index - 1][0] = tag; + var textbox = document.getElementById("header" + index + "Class"); + if (tag == "") { + textbox.setAttribute("disabled", "true"); + } else { + textbox.removeAttribute("disabled"); + } +} + +function changeClass(elt, index) { + tocHeadersArray[index - 1][1] = elt.value; +} + +function ToggleReadOnlyToc(elt) { + readonly = elt.checked; +} + +function ToggleOrderedList(elt) { + orderedList = elt.checked; +} diff --git a/comm/suite/editor/components/dialogs/content/EdInsertTOC.xhtml b/comm/suite/editor/components/dialogs/content/EdInsertTOC.xhtml new file mode 100644 index 0000000000..8d82bac046 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdInsertTOC.xhtml @@ -0,0 +1,225 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorInsertTOC.dtd"> + +<dialog title="&Window.title;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml" + onload="Startup();" + oncancel="window.close(); return true;"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://editor/content/editorUtilities.js"/> + + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EdInsertTOC.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + <spacer id="dummy" style="display:none"/> + <vbox flex="1"> + <groupbox> + <hbox class="groupbox-title"> + <label class="header">&buildToc.label;</label> + </hbox> + <grid> + <columns><column/><column style="min-width: 6em"/><column/></columns> + <rows> + <row align="center"> + <spacer/> + <label value="&tag.label;"/> + <label value="&class.label;"/> + </row> + <row align="center"> + <label value="&header1.label;"/> + <menulist id="header1Menulist"> + <menupopup> + <menuitem id="header1none" label="--" value="" + oncommand="selectHeader(this, 1)"/> + <menuseparator/> + <menuitem id="header1H1" label="h1" value="h1" + oncommand="selectHeader(this, 1)"/> + <menuitem id="header1H2" label="h2" value="h2" + oncommand="selectHeader(this, 1)"/> + <menuitem id="header1H3" label="h3" value="h3" + oncommand="selectHeader(this, 1)"/> + <menuitem id="header1H4" label="h4" value="h4" + oncommand="selectHeader(this, 1)"/> + <menuitem id="header1H5" label="h5" value="h5" + oncommand="selectHeader(this, 1)"/> + <menuitem id="header1H6" label="h6" value="h6" + oncommand="selectHeader(this, 1)"/> + <menuitem id="header1DIV" label="div" value="div" + oncommand="selectHeader(this, 1)"/> + <menuitem id="header1P" label="p" value="p" + oncommand="selectHeader(this, 1)"/> + </menupopup> + </menulist> + <textbox id="header1Class" size="10" + oninput="changeClass(this, 1)"/> + </row> + + <row align="center"> + <label value="&header2.label;"/> + <menulist id="header2Menulist"> + <menupopup> + <menuitem id="header2none" label="--" value="" + oncommand="selectHeader(this, 2)"/> + <menuseparator/> + <menuitem id="header2H1" label="h1" value="h1" + oncommand="selectHeader(this, 2)"/> + <menuitem id="header2H2" label="h2" value="h2" + oncommand="selectHeader(this, 2)"/> + <menuitem id="header2H3" label="h3" value="h3" + oncommand="selectHeader(this, 2)"/> + <menuitem id="header2H4" label="h4" value="h4" + oncommand="selectHeader(this, 2)"/> + <menuitem id="header2H5" label="h5" value="h5" + oncommand="selectHeader(this, 2)"/> + <menuitem id="header2H6" label="h6" value="h6" + oncommand="selectHeader(this, 2)"/> + <menuitem id="header2DIV" label="div" value="div" + oncommand="selectHeader(this, 2)"/> + <menuitem id="header2P" label="p" value="p" + oncommand="selectHeader(this, 2)"/> + </menupopup> + </menulist> + <textbox id="header2Class" size="10" + oninput="changeClass(this, 2)"/> + </row> + + <row align="center"> + <label value="&header3.label;"/> + <menulist id="header3Menulist"> + <menupopup> + <menuitem id="header3none" label="--" value="" + oncommand="selectHeader(this, 3)"/> + <menuseparator/> + <menuitem id="header3H1" label="h1" value="h1" + oncommand="selectHeader(this, 3)"/> + <menuitem id="header3H2" label="h2" value="h2" + oncommand="selectHeader(this, 3)"/> + <menuitem id="header3H3" label="h3" value="h3" + oncommand="selectHeader(this, 3)"/> + <menuitem id="header3H4" label="h4" value="h4" + oncommand="selectHeader(this, 3)"/> + <menuitem id="header3H5" label="h5" value="h5" + oncommand="selectHeader(this, 3)"/> + <menuitem id="header3H6" label="h6" value="h6" + oncommand="selectHeader(this, 3)"/> + <menuitem id="header3DIV" label="div" value="div" + oncommand="selectHeader(this, 3)"/> + <menuitem id="header3P" label="p" value="p" + oncommand="selectHeader(this, 3)"/> + </menupopup> + </menulist> + <textbox id="header3Class" size="10" + oninput="changeClass(this, 3)"/> + </row> + + <row align="center"> + <label value="&header4.label;"/> + <menulist id="header4Menulist"> + <menupopup> + <menuitem id="header4none" label="--" value="" + oncommand="selectHeader(this, 4)"/> + <menuseparator/> + <menuitem id="header4H1" label="h1" value="h1" + oncommand="selectHeader(this, 4)"/> + <menuitem id="header4H2" label="h2" value="h2" + oncommand="selectHeader(this, 4)"/> + <menuitem id="header4H3" label="h3" value="h3" + oncommand="selectHeader(this, 4)"/> + <menuitem id="header4H4" label="h4" value="h4" + oncommand="selectHeader(this, 4)"/> + <menuitem id="header4H5" label="h5" value="h5" + oncommand="selectHeader(this, 4)"/> + <menuitem id="header4H6" label="h6" value="h6" + oncommand="selectHeader(this, 4)"/> + <menuitem id="header4DIV" label="div" value="div" + oncommand="selectHeader(this, 4)"/> + <menuitem id="header4P" label="p" value="p" + oncommand="selectHeader(this, 4)"/> + </menupopup> + </menulist> + <textbox id="header4Class" size="10" + oninput="changeClass(this, 4)"/> + </row> + + <row align="center"> + <label value="&header5.label;"/> + <menulist id="header5Menulist"> + <menupopup> + <menuitem id="header5none" label="--" value="" + oncommand="selectHeader(this, 5)"/> + <menuseparator/> + <menuitem id="header5H1" label="h1" value="h1" + oncommand="selectHeader(this, 5)"/> + <menuitem id="header5H2" label="h2" value="h2" + oncommand="selectHeader(this, 5)"/> + <menuitem id="header5H3" label="h3" value="h3" + oncommand="selectHeader(this, 5)"/> + <menuitem id="header5H4" label="h4" value="h4" + oncommand="selectHeader(this, 5)"/> + <menuitem id="header5H5" label="h5" value="h5" + oncommand="selectHeader(this, 5)"/> + <menuitem id="header5H6" label="h6" value="h6" + oncommand="selectHeader(this, 5)"/> + <menuitem id="header5DIV" label="div" value="div" + oncommand="selectHeader(this, 5)"/> + <menuitem id="header5P" label="p" value="p" + oncommand="selectHeader(this, 5)"/> + </menupopup> + </menulist> + <textbox id="header5Class" size="10" + oninput="changeClass(this, 5)"/> + </row> + + <row align="center"> + <label value="&header6.label;"/> + <menulist id="header6Menulist"> + <menupopup> + <menuitem id="header6none" label="--" value="" + oncommand="selectHeader(this, 6)"/> + <menuseparator/> + <menuitem id="header6H1" label="h1" value="h1" + oncommand="selectHeader(this, 6)"/> + <menuitem id="header6H2" label="h2" value="h2" + oncommand="selectHeader(this, 6)"/> + <menuitem id="header6H3" label="h3" value="h3" + oncommand="selectHeader(this, 6)"/> + <menuitem id="header6H4" label="h4" value="h4" + oncommand="selectHeader(this, 6)"/> + <menuitem id="header6H5" label="h5" value="h5" + oncommand="selectHeader(this, 6)"/> + <menuitem id="header6H6" label="h6" value="h6" + oncommand="selectHeader(this, 6)"/> + <menuitem id="header6DIV" label="div" value="div" + oncommand="selectHeader(this, 6)"/> + <menuitem id="header6P" label="p" value="p" + oncommand="selectHeader(this, 6)"/> + </menupopup> + </menulist> + <textbox id="header6Class" size="10" + oninput="changeClass(this, 6)"/> + </row> + </rows> + </grid> + </groupbox> + <vbox> + <checkbox id="orderedListCheckbox" + label="&orderedList.label;" + oncommand="ToggleOrderedList(this)"/> + <checkbox id="readOnlyCheckbox" + label="&makeReadOnly.label;" + oncommand="ToggleReadOnlyToc(this)"/> + </vbox> + <separator class="groove"/> + </vbox> +</dialog> diff --git a/comm/suite/editor/components/dialogs/content/EdInsertTable.js b/comm/suite/editor/components/dialogs/content/EdInsertTable.js new file mode 100644 index 0000000000..0053f9fd94 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdInsertTable.js @@ -0,0 +1,254 @@ +/* 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 gTableElement = null; +var gRows; +var gColumns; +var gActiveEditor; + +// dialog initialization code + +document.addEventListener("dialogaccept", onAccept); +document.addEventListener("dialogcancel", onCancel); + +function Startup() { + gActiveEditor = GetCurrentTableEditor(); + if (!gActiveEditor) { + dump("Failed to get active editor!\n"); + window.close(); + return; + } + + try { + gTableElement = gActiveEditor.createElementWithDefaults("table"); + } catch (e) {} + + if (!gTableElement) { + dump("Failed to create a new table!\n"); + window.close(); + return; + } + gDialog.rowsInput = document.getElementById("rowsInput"); + gDialog.columnsInput = document.getElementById("columnsInput"); + gDialog.widthInput = document.getElementById("widthInput"); + gDialog.borderInput = document.getElementById("borderInput"); + gDialog.widthPixelOrPercentMenulist = document.getElementById( + "widthPixelOrPercentMenulist" + ); + gDialog.OkButton = document.documentElement.getButton("accept"); + + // Make a copy to use for AdvancedEdit + globalElement = gTableElement.cloneNode(false); + try { + if ( + Services.prefs.getBoolPref("editor.use_css") && + IsHTMLEditor() && + !(gActiveEditor.flags & Ci.nsIEditor.eEditorMailMask) + ) { + // only for Composer and not for htmlmail + globalElement.setAttribute("style", "text-align: left;"); + } + } catch (e) {} + + // Initialize all widgets with image attributes + InitDialog(); + + // Set initial number to 2 rows, 2 columns: + // Note, these are not attributes on the table, + // so don't put them in InitDialog(), + // else the user's values will be trashed when they use + // the Advanced Edit dialog + gDialog.rowsInput.value = 2; + gDialog.columnsInput.value = 2; + + // If no default value on the width, set to 100% + if (gDialog.widthInput.value.length == 0) { + gDialog.widthInput.value = "100"; + gDialog.widthPixelOrPercentMenulist.selectedIndex = 1; + } + + SetTextboxFocusById("rowsInput"); + + 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() { + // Get default attributes set on the created table: + // Get the width attribute of the element, stripping out "%" + // This sets contents of menu combobox list + // 2nd param = null: Use current selection to find if parent is table cell or window + gDialog.widthInput.value = InitPixelOrPercentMenulist( + globalElement, + null, + "width", + "widthPixelOrPercentMenulist", + gPercent + ); + gDialog.borderInput.value = globalElement.getAttribute("border"); +} + +function ChangeRowOrColumn(id) { + // Allow only integers + forceInteger(id); + + // Enable OK only if both rows and columns have a value > 0 + var enable = + gDialog.rowsInput.value.length > 0 && + gDialog.rowsInput.value > 0 && + gDialog.columnsInput.value.length > 0 && + gDialog.columnsInput.value > 0; + + SetElementEnabled(gDialog.OkButton, enable); + SetElementEnabledById("AdvancedEditButton1", enable); +} + +// Get and validate data from widgets. +// Set attributes on globalElement so they can be accessed by AdvancedEdit() +function ValidateData() { + gRows = ValidateNumber( + gDialog.rowsInput, + null, + 1, + gMaxRows, + null, + null, + true + ); + if (gValidationError) { + return false; + } + + gColumns = ValidateNumber( + gDialog.columnsInput, + null, + 1, + gMaxColumns, + null, + null, + true + ); + if (gValidationError) { + return false; + } + + // Set attributes: NOTE: These may be empty strings (last param = false) + ValidateNumber( + gDialog.borderInput, + null, + 0, + gMaxPixels, + globalElement, + "border", + false + ); + // TODO: Deal with "BORDER" without value issue + if (gValidationError) { + return false; + } + + ValidateNumber( + gDialog.widthInput, + gDialog.widthPixelOrPercentMenulist, + 1, + gMaxTableSize, + globalElement, + "width", + false + ); + if (gValidationError) { + return false; + } + + return true; +} + +function onAccept(event) { + if (ValidateData()) { + gActiveEditor.beginTransaction(); + try { + gActiveEditor.cloneAttributes(gTableElement, globalElement); + + // Create necessary rows and cells for the table + var tableBody = gActiveEditor.createElementWithDefaults("tbody"); + if (tableBody) { + gTableElement.appendChild(tableBody); + + // Create necessary rows and cells for the table + for (var i = 0; i < gRows; i++) { + var newRow = gActiveEditor.createElementWithDefaults("tr"); + if (newRow) { + tableBody.appendChild(newRow); + for (var j = 0; j < gColumns; j++) { + var newCell = gActiveEditor.createElementWithDefaults("td"); + if (newCell) { + newRow.appendChild(newCell); + } + } + } + } + } + // Detect when entire cells are selected: + // Get number of cells selected + var tagNameObj = { value: "" }; + var countObj = { value: 0 }; + var element = gActiveEditor.getSelectedOrParentTableElement( + tagNameObj, + countObj + ); + var deletePlaceholder = false; + + if (tagNameObj.value == "table") { + // Replace entire selected table with new table, so delete the table + gActiveEditor.deleteTable(); + } else if (tagNameObj.value == "td") { + if (countObj.value >= 1) { + if (countObj.value > 1) { + // Assume user wants to replace a block of + // contiguous cells with a table, so + // join the selected cells + gActiveEditor.joinTableCells(false); + + // Get the cell everything was merged into + element = gActiveEditor.getFirstSelectedCell(); + + // Collapse selection into just that cell + gActiveEditor.selection.collapse(element, 0); + } + + if (element) { + // Empty just the contents of the cell + gActiveEditor.deleteTableCellContents(); + + // Collapse selection to start of empty cell... + gActiveEditor.selection.collapse(element, 0); + // ...but it will contain a <br> placeholder + deletePlaceholder = true; + } + } + } + + // true means delete selection when inserting + gActiveEditor.insertElementAtSelection(gTableElement, true); + + if (deletePlaceholder && gTableElement && gTableElement.nextSibling) { + // Delete the placeholder <br> + gActiveEditor.deleteNode(gTableElement.nextSibling); + } + } catch (e) {} + + gActiveEditor.endTransaction(); + + SaveWindowLocation(); + return; + } + event.preventDefault(); +} diff --git a/comm/suite/editor/components/dialogs/content/EdInsertTable.xhtml b/comm/suite/editor/components/dialogs/content/EdInsertTable.xhtml new file mode 100644 index 0000000000..b376d5f0be --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdInsertTable.xhtml @@ -0,0 +1,82 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog [ +<!ENTITY % edInsertTable SYSTEM "chrome://editor/locale/EditorInsertTable.dtd"> +%edInsertTable; +<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd"> +%edDialogOverlay; +]> + +<dialog title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload = "Startup()"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EdInsertTable.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + <groupbox> + <hbox class="groupbox-title"> + <label class="header">&size.label;</label> + </hbox> + <grid> + <columns> + <column flex="1"/> + <column flex="1"/> + <column flex="6"/> + </columns> + <rows> + <row align="center"> + <label control="rowsInput" class="align-right" + value="&numRowsEditField.label;" + accesskey="&numRowsEditField.accessKey;"/> + <textbox class="narrow" id="rowsInput" oninput="ChangeRowOrColumn(this.id)" /> + <spacer/> + </row> + <row align="center"> + <label control="columnsInput" class="align-right" + value="&numColumnsEditField.label;" + accesskey="&numColumnsEditField.accessKey;"/> + <textbox class="narrow" id="columnsInput" oninput="ChangeRowOrColumn(this.id)" /> + <spacer/> + </row> + <row align="center"> + <label control="widthInput" class="align-right" + value="&widthEditField.label;" + accesskey="&widthEditField.accessKey;"/> + <textbox class="narrow" id="widthInput" oninput="forceInteger(this.id)" /> + <menulist id="widthPixelOrPercentMenulist" flex="1"/> + <!-- child elements are appended by JS --> + </row> + </rows> + </grid> + <spacer class="spacer"/> + </groupbox> + <spacer class="spacer"/> + <hbox align="center"> + <label control="borderInput" class="align-right" + value="&borderEditField.label;" + accesskey="&borderEditField.accessKey;" + tooltiptext="&borderEditField.tooltip;" /> + <textbox class="narrow" id="borderInput" oninput="forceInteger(this.id)" /> + <label value="&pixels.label;"/> + </hbox> + <vbox id="AdvancedEdit"> + <hbox flex="1" style="margin-top: 0.2em" align="center"> + <!-- This will right-align the button --> + <spacer flex="1"/> + <button id="AdvancedEditButton1" oncommand="onAdvancedEdit()" label="&AdvancedEditButton.label;" + accesskey="&AdvancedEditButton.accessKey;" tooltiptext="&AdvancedEditButton.tooltip;"/> + </hbox> + <separator id="advancedSeparator" class="groove"/> + </vbox> +</dialog> diff --git a/comm/suite/editor/components/dialogs/content/EdLabelProps.js b/comm/suite/editor/components/dialogs/content/EdLabelProps.js new file mode 100644 index 0000000000..ec96878ea6 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdLabelProps.js @@ -0,0 +1,118 @@ +/* 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 labelElement; + +// 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.labelText = document.getElementById("LabelText"); + gDialog.labelFor = document.getElementById("LabelFor"); + gDialog.labelAccessKey = document.getElementById("LabelAccessKey"); + + labelElement = window.arguments[0]; + + // Make a copy to use for AdvancedEdit + globalElement = labelElement.cloneNode(false); + + InitDialog(); + + var range = editor.document.createRange(); + range.selectNode(labelElement); + gDialog.labelText.value = range.toString(); + + if (labelElement.innerHTML.includes("<")) { + gDialog.editText.checked = false; + gDialog.editText.disabled = false; + gDialog.labelText.disabled = true; + gDialog.editText.addEventListener( + "command", + () => + Services.prompt.alert( + window, + GetString("Alert"), + GetString("EditTextWarning") + ), + { capture: false, once: true } + ); + SetTextboxFocus(gDialog.labelFor); + } else { + SetTextboxFocus(gDialog.labelText); + } + + SetWindowLocation(); +} + +function InitDialog() { + gDialog.labelFor.value = globalElement.getAttribute("for"); + gDialog.labelAccessKey.value = globalElement.getAttribute("accesskey"); +} + +function RemoveLabel() { + RemoveContainer(labelElement); + SaveWindowLocation(); + window.close(); +} + +function ValidateData() { + if (gDialog.labelFor.value) { + globalElement.setAttribute("for", gDialog.labelFor.value); + } else { + globalElement.removeAttribute("for"); + } + if (gDialog.labelAccessKey.value) { + globalElement.setAttribute("accesskey", gDialog.labelAccessKey.value); + } else { + globalElement.removeAttribute("accesskey"); + } + return true; +} + +function onAccept() { + // All values are valid - copy to actual element in doc + ValidateData(); + + var editor = GetCurrentEditor(); + + editor.beginTransaction(); + + try { + if (gDialog.editText.checked) { + editor.setShouldTxnSetSelection(false); + + while (labelElement.firstChild) { + editor.deleteNode(labelElement.firstChild); + } + if (gDialog.labelText.value) { + editor.insertNode( + editor.document.createTextNode(gDialog.labelText.value), + labelElement, + 0 + ); + } + + editor.setShouldTxnSetSelection(true); + } + + editor.cloneAttributes(labelElement, globalElement); + } catch (e) {} + + editor.endTransaction(); + + SaveWindowLocation(); +} diff --git a/comm/suite/editor/components/dialogs/content/EdLabelProps.xhtml b/comm/suite/editor/components/dialogs/content/EdLabelProps.xhtml new file mode 100644 index 0000000000..21d964f1fd --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdLabelProps.xhtml @@ -0,0 +1,66 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog [ +<!ENTITY % edLabelProperties SYSTEM "chrome://editor/locale/EditorLabelProperties.dtd"> +%edLabelProperties; +<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd"> +%edDialogOverlay; +]> + +<dialog title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload="Startup();" + buttons="accept,cancel"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EdLabelProps.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <groupbox> + <hbox class="groupbox-title"> + <label class="header" accesskey="&Settings.accesskey;">&Settings.label;</label> + </hbox> + <grid><columns><column/><column/></columns> + <rows> + <row align="center"> + <checkbox id="EditText" label="&EditLabelText.label;" accesskey="&EditLabelText.accesskey;" checked="true" disabled="true" + oncommand="gDialog.labelText.disabled = !gDialog.editText.checked;"/> + <textbox id="LabelText" accesskey="&Settings.accesskey;"/> + </row> + <row align="center"> + <label control="LabelFor" value="&LabelFor.label;" accesskey="&LabelFor.accesskey;"/> + <textbox id="LabelFor"/> + </row> + <row align="center"> + <label control="LabelAccessKey" value="&AccessKey.label;" accesskey="&AccessKey.accesskey;"/> + <hbox> + <textbox id="LabelAccessKey" class="narrow"/> + </hbox> + </row> + </rows> + </grid> + </groupbox> + + <!-- from EdDialogOverlay --> + <hbox flex="1" style="margin-top: 0.2em"> + <button id="RemoveLabel" label="&RemoveLabel.label;" accesskey="&RemoveLabel.accesskey;" oncommand="RemoveLabel();"/> + <!-- This will right-align the button --> + <spacer flex="1"/> + <button id="AdvancedEditButton" + oncommand="onAdvancedEdit();" + label="&AdvancedEditButton.label;" + accesskey="&AdvancedEditButton.accessKey;" + tooltiptext="&AdvancedEditButton.tooltip;"/> + </hbox> + <separator class="groove"/> + +</dialog> diff --git a/comm/suite/editor/components/dialogs/content/EdLinkProps.js b/comm/suite/editor/components/dialogs/content/EdLinkProps.js new file mode 100644 index 0000000000..6504496a51 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdLinkProps.js @@ -0,0 +1,331 @@ +/* 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 gActiveEditor; +var anchorElement = null; +var imageElement = null; +var insertNew = false; +var replaceExistingLink = false; +var insertLinkAtCaret; +var needLinkText = false; +var href; +var newLinkText; +var gHNodeArray = {}; +var gHaveNamedAnchors = false; +var gHaveHeadings = false; +var gCanChangeHeadingSelected = true; +var gCanChangeAnchorSelected = true; + +// NOTE: Use "href" instead of "a" to distinguish from Named Anchor +// The returned node is has an "a" tagName +var tagName = "href"; + +// dialog initialization code + +document.addEventListener("dialogaccept", onAccept); +document.addEventListener("dialogcancel", onCancel); + +function Startup() { + gActiveEditor = GetCurrentEditor(); + if (!gActiveEditor) { + dump("Failed to get active editor!\n"); + window.close(); + return; + } + // Message was wrapped in a <label> or <div>, so actual text is a child text node + gDialog.linkTextCaption = document.getElementById("linkTextCaption"); + gDialog.linkTextMessage = document.getElementById("linkTextMessage"); + gDialog.linkTextInput = document.getElementById("linkTextInput"); + gDialog.hrefInput = document.getElementById("hrefInput"); + gDialog.makeRelativeLink = document.getElementById("MakeRelativeLink"); + gDialog.AdvancedEditSection = document.getElementById("AdvancedEdit"); + + // See if we have a single selected image + imageElement = gActiveEditor.getSelectedElement("img"); + + if (imageElement) { + // Get the parent link if it exists -- more efficient than GetSelectedElement() + anchorElement = gActiveEditor.getElementOrParentByTagName( + "href", + imageElement + ); + if (anchorElement) { + if (anchorElement.childNodes.length > 1) { + // If there are other children, then we want to break + // this image away by inserting a new link around it, + // so make a new node and copy existing attributes + anchorElement = anchorElement.cloneNode(false); + // insertNew = true; + replaceExistingLink = true; + } + } + } else { + // Get an anchor element if caret or + // entire selection is within the link. + anchorElement = gActiveEditor.getSelectedElement(tagName); + + if (anchorElement) { + // Select the entire link + gActiveEditor.selectElement(anchorElement); + } else { + // If selection starts in a link, but extends beyond it, + // the user probably wants to extend existing link to new selection, + // so check if either end of selection is within a link + // POTENTIAL PROBLEM: This prevents user from selecting text in an existing + // link and making 2 links. + // Note that this isn't a problem with images, handled above + + anchorElement = gActiveEditor.getElementOrParentByTagName( + "href", + gActiveEditor.selection.anchorNode + ); + if (!anchorElement) { + anchorElement = gActiveEditor.getElementOrParentByTagName( + "href", + gActiveEditor.selection.focusNode + ); + } + + if (anchorElement) { + // But clone it for reinserting/merging around existing + // link that only partially overlaps the selection + anchorElement = anchorElement.cloneNode(false); + // insertNew = true; + replaceExistingLink = true; + } + } + } + + if (!anchorElement) { + // No existing link -- create a new one + anchorElement = gActiveEditor.createElementWithDefaults(tagName); + insertNew = true; + // Hide message about removing existing link + // document.getElementById("RemoveLinkMsg").hidden = true; + } + if (!anchorElement) { + dump("Failed to get selected element or create a new one!\n"); + window.close(); + return; + } + + // We insert at caret only when nothing is selected + insertLinkAtCaret = gActiveEditor.selection.isCollapsed; + + var selectedText; + if (insertLinkAtCaret) { + // Groupbox caption: + gDialog.linkTextCaption.setAttribute("label", GetString("LinkText")); + + // Message above input field: + gDialog.linkTextMessage.setAttribute("value", GetString("EnterLinkText")); + gDialog.linkTextMessage.setAttribute( + "accesskey", + GetString("EnterLinkTextAccessKey") + ); + } else { + if (!imageElement) { + // We get here if selection is exactly around a link node + // Check if selection has some text - use that first + selectedText = GetSelectionAsText(); + if (!selectedText) { + // No text, look for first image in the selection + var children = anchorElement.childNodes; + if (children) { + for (var i = 0; i < children.length; i++) { + var nodeName = children.item(i).nodeName.toLowerCase(); + if (nodeName == "img") { + imageElement = children.item(i); + break; + } + } + } + } + } + // Set "caption" for link source and the source text or image URL + if (imageElement) { + gDialog.linkTextCaption.setAttribute("label", GetString("LinkImage")); + // Link source string is the source URL of image + // TODO: THIS DOESN'T HANDLE MULTIPLE SELECTED IMAGES! + gDialog.linkTextMessage.setAttribute("value", imageElement.src); + } else { + gDialog.linkTextCaption.setAttribute("label", GetString("LinkText")); + if (selectedText) { + // Use just the first 60 characters and add "..." + gDialog.linkTextMessage.setAttribute( + "value", + TruncateStringAtWordEnd( + ReplaceWhitespace(selectedText, " "), + 60, + true + ) + ); + } else { + gDialog.linkTextMessage.setAttribute( + "value", + GetString("MixedSelection") + ); + } + } + } + + // Make a copy to use for AdvancedEdit and onSaveDefault + globalElement = anchorElement.cloneNode(false); + + // Get the list of existing named anchors and headings + FillLinkMenulist(gDialog.hrefInput, gHNodeArray); + + // We only need to test for this once per dialog load + gHaveDocumentUrl = GetDocumentBaseUrl(); + + // Set data for the dialog controls + InitDialog(); + + // Search for a URI pattern in the selected text + // as candidate href + selectedText = TrimString(selectedText); + if (!gDialog.hrefInput.value && TextIsURI(selectedText)) { + gDialog.hrefInput.value = selectedText; + } + + // Set initial focus + if (insertLinkAtCaret) { + // We will be using the HREF inputbox, so text message + SetTextboxFocus(gDialog.linkTextInput); + } else { + SetTextboxFocus(gDialog.hrefInput); + + // We will not insert a new link at caret, so remove link text input field + gDialog.linkTextInput.hidden = true; + gDialog.linkTextInput = null; + } + + // This sets enable state on OK button + doEnabling(); + + 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() { + // Must use getAttribute, not "globalElement.href", + // or foreign chars aren't converted correctly! + gDialog.hrefInput.value = globalElement.getAttribute("href"); + + // Set "Relativize" checkbox according to current URL state + SetRelativeCheckbox(gDialog.makeRelativeLink); +} + +function doEnabling() { + // We disable Ok button when there's no href text only if inserting a new link + var enable = insertNew + ? TrimString(gDialog.hrefInput.value).length > 0 + : true; + + // anon. content, so can't use SetElementEnabledById here + var dialogNode = document.getElementById("linkDlg"); + dialogNode.getButton("accept").disabled = !enable; + + SetElementEnabledById("AdvancedEditButton1", enable); +} + +function ChangeLinkLocation() { + SetRelativeCheckbox(gDialog.makeRelativeLink); + // Set OK button enable state + doEnabling(); +} + +// Get and validate data from widgets. +// Set attributes on globalElement so they can be accessed by AdvancedEdit() +function ValidateData() { + href = TrimString(gDialog.hrefInput.value); + if (href) { + // Set the HREF directly on the editor document's anchor node + // or on the newly-created node if insertNew is true + globalElement.setAttribute("href", href); + } else if (insertNew) { + // We must have a URL to insert a new link + // NOTE: We accept an empty HREF on existing link to indicate removing the link + ShowInputErrorMessage(GetString("EmptyHREFError")); + return false; + } + if (gDialog.linkTextInput) { + // The text we will insert isn't really an attribute, + // but it makes sense to validate it + newLinkText = TrimString(gDialog.linkTextInput.value); + if (!newLinkText) { + if (href) { + newLinkText = href; + } else { + ShowInputErrorMessage(GetString("EmptyLinkTextError")); + SetTextboxFocus(gDialog.linkTextInput); + return false; + } + } + } + return true; +} + +function onAccept(event) { + if (ValidateData()) { + if (href.length > 0) { + // Copy attributes to element we are changing or inserting + gActiveEditor.cloneAttributes(anchorElement, globalElement); + + // Coalesce into one undo transaction + gActiveEditor.beginTransaction(); + + // Get text to use for a new link + if (insertLinkAtCaret) { + // Append the link text as the last child node + // of the anchor node + var textNode = gActiveEditor.document.createTextNode(newLinkText); + if (textNode) { + anchorElement.appendChild(textNode); + } + try { + gActiveEditor.insertElementAtSelection(anchorElement, false); + } catch (e) { + dump("Exception occurred in InsertElementAtSelection\n"); + return; + } + } else if (insertNew || replaceExistingLink) { + // Link source was supplied by the selection, + // so insert a link node as parent of this + // (may be text, image, or other inline content) + try { + gActiveEditor.insertLinkAroundSelection(anchorElement); + } catch (e) { + dump("Exception occurred in InsertElementAtSelection\n"); + return; + } + } + // Check if the link was to a heading + if (href in gHNodeArray) { + var anchorNode = gActiveEditor.createElementWithDefaults("a"); + if (anchorNode) { + anchorNode.name = href.substr(1); + + // Insert the anchor into the document, + // but don't let the transaction change the selection + gActiveEditor.setShouldTxnSetSelection(false); + gActiveEditor.insertNode(anchorNode, gHNodeArray[href], 0); + gActiveEditor.setShouldTxnSetSelection(true); + } + } + gActiveEditor.endTransaction(); + } else if (!insertNew) { + // We already had a link, but empty HREF means remove it + EditorRemoveTextProperty("href", ""); + } + SaveWindowLocation(); + return; + } + event.preventDefault(); +} diff --git a/comm/suite/editor/components/dialogs/content/EdLinkProps.xhtml b/comm/suite/editor/components/dialogs/content/EdLinkProps.xhtml new file mode 100644 index 0000000000..04e147db4c --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdLinkProps.xhtml @@ -0,0 +1,79 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog [ +<!ENTITY % linkPropertiesDTD SYSTEM "chrome://editor/locale/EditorLinkProperties.dtd"> +%linkPropertiesDTD; +<!ENTITY % composeEditorOverlayDTD SYSTEM "chrome://messenger/locale/messengercompose/mailComposeEditorOverlay.dtd"> +%composeEditorOverlayDTD; +<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd"> +%edDialogOverlay; +]> + +<dialog id="linkDlg" title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload = "Startup()"> + + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EdLinkProps.js"/> + <script src="chrome://editor/content/EdImageLinkLoader.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <vbox style="min-width: 20em"> + <groupbox> + <hbox class="groupbox-title"> + <label id="linkTextCaption" class="header"/> + </hbox> + <vbox> + <label id="linkTextMessage" control="linkTextInput"/> + <textbox id="linkTextInput"/> + </vbox> + </groupbox> + + <groupbox id="LinkURLBox"> + <hbox class="groupbox-title"> + <label class="header">&LinkURLBox.label;</label> + </hbox> + <vbox id="LinkLocationBox"> + <label control="hrefInput" + accesskey="&LinkURLEditField2.accessKey;" + width="1">&LinkURLEditField2.label;</label> + <textbox id="hrefInput" type="text" + class="uri-element padded" oninput="ChangeLinkLocation();"/> + <hbox align="center"> + <checkbox id="MakeRelativeLink" + for="hrefInput" + label="&makeUrlRelative.label;" + accesskey="&makeUrlRelative.accessKey;" + oncommand="MakeInputValueRelativeOrAbsolute(this);" + tooltiptext="&makeUrlRelative.tooltip;"/> + <spacer flex="1"/> + <button label="&chooseFileLinkButton.label;" accesskey="&chooseFileLinkButton.accessKey;" + oncommand="chooseLinkFile();"/> + </hbox> + </vbox> + <checkbox id="AttachSourceToMail" + hidden="true" + label="&attachLinkSource.label;" + accesskey="&attachLinkSource.accesskey;" + oncommand="DoAttachSourceCheckbox()"/> + </groupbox> + </vbox> + <vbox id="AdvancedEdit"> + <hbox flex="1" style="margin-top: 0.2em" align="center"> + <!-- This will right-align the button --> + <spacer flex="1"/> + <button id="AdvancedEditButton1" oncommand="onAdvancedEdit()" label="&AdvancedEditButton.label;" + accesskey="&AdvancedEditButton.accessKey;" tooltiptext="&AdvancedEditButton.tooltip;"/> + </hbox> + <separator id="advancedSeparator" class="groove"/> + </vbox> +</dialog> diff --git a/comm/suite/editor/components/dialogs/content/EdListProps.js b/comm/suite/editor/components/dialogs/content/EdListProps.js new file mode 100644 index 0000000000..8d4b78536b --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdListProps.js @@ -0,0 +1,455 @@ +/* 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 gBulletStyleType = ""; +var gNumberStyleType = ""; +var gListElement; +var gOriginalListType = ""; +var gListType = ""; +var gMixedListSelection = false; +var gStyleType = ""; +var gOriginalStyleType = ""; +const gOnesArray = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"]; +const gTensArray = ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"]; +const gHundredsArray = [ + "", + "C", + "CC", + "CCC", + "CD", + "D", + "DC", + "DCC", + "DCCC", + "CM", +]; +const gThousandsArray = [ + "", + "M", + "MM", + "MMM", + "MMMM", + "MMMMM", + "MMMMMM", + "MMMMMMM", + "MMMMMMMM", + "MMMMMMMMM", +]; +const gRomanDigits = { I: 1, V: 5, X: 10, L: 50, C: 100, D: 500, M: 1000 }; +const A = "A".charCodeAt(0); +const gArabic = "1"; +const gUpperRoman = "I"; +const gLowerRoman = "i"; +const gUpperLetters = "A"; +const gLowerLetters = "a"; +const gDecimalCSS = "decimal"; +const gUpperRomanCSS = "upper-roman"; +const gLowerRomanCSS = "lower-roman"; +const gUpperAlphaCSS = "upper-alpha"; +const gLowerAlphaCSS = "lower-alpha"; + +// dialog initialization code + +document.addEventListener("dialogaccept", onAccept); +document.addEventListener("dialogcancel", onCancel); + +function Startup() { + var editor = GetCurrentEditor(); + if (!editor) { + window.close(); + return; + } + gDialog.ListTypeList = document.getElementById("ListType"); + gDialog.BulletStyleList = document.getElementById("BulletStyle"); + gDialog.BulletStyleLabel = document.getElementById("BulletStyleLabel"); + gDialog.StartingNumberInput = document.getElementById("StartingNumber"); + gDialog.StartingNumberLabel = document.getElementById("StartingNumberLabel"); + gDialog.AdvancedEditButton = document.getElementById("AdvancedEditButton1"); + gDialog.RadioGroup = document.getElementById("RadioGroup"); + gDialog.ChangeAllRadio = document.getElementById("ChangeAll"); + gDialog.ChangeSelectedRadio = document.getElementById("ChangeSelected"); + + // Try to get an existing list(s) + var mixedObj = { value: null }; + try { + gListType = editor.getListState(mixedObj, {}, {}, {}); + + // We may have mixed list and non-list, or > 1 list type in selection + gMixedListSelection = mixedObj.value; + + // Get the list element at the anchor node + gListElement = editor.getElementOrParentByTagName("list", null); + } catch (e) {} + + // The copy to use in AdvancedEdit + if (gListElement) { + globalElement = gListElement.cloneNode(false); + } + + // Show extra options for changing entire list if we have one already. + gDialog.RadioGroup.collapsed = !gListElement; + if (gListElement) { + // Radio button index is persistent + if (gDialog.RadioGroup.getAttribute("index") == "1") { + gDialog.RadioGroup.selectedItem = gDialog.ChangeSelectedRadio; + } else { + gDialog.RadioGroup.selectedItem = gDialog.ChangeAllRadio; + } + } + + InitDialog(); + + gOriginalListType = gListType; + + gDialog.ListTypeList.focus(); + + SetWindowLocation(); +} + +function InitDialog() { + // Note that if mixed, we we pay attention + // only to the anchor node's list type + // (i.e., don't confuse user with "mixed" designation) + if (gListElement) { + gListType = gListElement.nodeName.toLowerCase(); + } else { + gListType = ""; + } + + gDialog.ListTypeList.value = gListType; + gDialog.StartingNumberInput.value = ""; + + // Last param = true means attribute value is case-sensitive + var type = globalElement + ? GetHTMLOrCSSStyleValue(globalElement, "type", "list-style-type") + : null; + + if (gListType == "ul") { + if (type) { + type = type.toLowerCase(); + gBulletStyleType = type; + gOriginalStyleType = type; + } + } else if (gListType == "ol") { + // Translate CSS property strings + switch (type.toLowerCase()) { + case gDecimalCSS: + type = gArabic; + break; + case gUpperRomanCSS: + type = gUpperRoman; + break; + case gLowerRomanCSS: + type = gLowerRoman; + break; + case gUpperAlphaCSS: + type = gUpperLetters; + break; + case gLowerAlphaCSS: + type = gLowerLetters; + break; + } + if (type) { + gNumberStyleType = type; + gOriginalStyleType = type; + } + + // Convert attribute number to appropriate letter or roman numeral + gDialog.StartingNumberInput.value = ConvertStartAttrToUserString( + globalElement.getAttribute("start"), + type + ); + } + BuildBulletStyleList(); +} + +// Convert attribute number to appropriate letter or roman numeral +function ConvertStartAttrToUserString(startAttr, type) { + switch (type) { + case gUpperRoman: + startAttr = ConvertArabicToRoman(startAttr); + break; + case gLowerRoman: + startAttr = ConvertArabicToRoman(startAttr).toLowerCase(); + break; + case gUpperLetters: + startAttr = ConvertArabicToLetters(startAttr); + break; + case gLowerLetters: + startAttr = ConvertArabicToLetters(startAttr).toLowerCase(); + break; + } + return startAttr; +} + +function BuildBulletStyleList() { + gDialog.BulletStyleList.removeAllItems(); + var label; + + if (gListType == "ul") { + gDialog.BulletStyleList.removeAttribute("disabled"); + gDialog.BulletStyleLabel.removeAttribute("disabled"); + gDialog.StartingNumberInput.setAttribute("disabled", "true"); + gDialog.StartingNumberLabel.setAttribute("disabled", "true"); + + label = GetString("BulletStyle"); + + gDialog.BulletStyleList.appendItem(GetString("Automatic"), ""); + gDialog.BulletStyleList.appendItem(GetString("SolidCircle"), "disc"); + gDialog.BulletStyleList.appendItem(GetString("OpenCircle"), "circle"); + gDialog.BulletStyleList.appendItem(GetString("SolidSquare"), "square"); + + gDialog.BulletStyleList.value = gBulletStyleType; + } else if (gListType == "ol") { + gDialog.BulletStyleList.removeAttribute("disabled"); + gDialog.BulletStyleLabel.removeAttribute("disabled"); + gDialog.StartingNumberInput.removeAttribute("disabled"); + gDialog.StartingNumberLabel.removeAttribute("disabled"); + label = GetString("NumberStyle"); + + gDialog.BulletStyleList.appendItem(GetString("Automatic"), ""); + gDialog.BulletStyleList.appendItem(GetString("Style_1"), gArabic); + gDialog.BulletStyleList.appendItem(GetString("Style_I"), gUpperRoman); + gDialog.BulletStyleList.appendItem(GetString("Style_i"), gLowerRoman); + gDialog.BulletStyleList.appendItem(GetString("Style_A"), gUpperLetters); + gDialog.BulletStyleList.appendItem(GetString("Style_a"), gLowerLetters); + + gDialog.BulletStyleList.value = gNumberStyleType; + } else { + gDialog.BulletStyleList.setAttribute("disabled", "true"); + gDialog.BulletStyleLabel.setAttribute("disabled", "true"); + gDialog.StartingNumberInput.setAttribute("disabled", "true"); + gDialog.StartingNumberLabel.setAttribute("disabled", "true"); + } + + // Disable advanced edit button if changing to "normal" + if (gListType) { + gDialog.AdvancedEditButton.removeAttribute("disabled"); + } else { + gDialog.AdvancedEditButton.setAttribute("disabled", "true"); + } + + if (label) { + gDialog.BulletStyleLabel.setAttribute("label", label); + } +} + +function SelectListType() { + // Each list type is stored in the "value" of each menuitem + var NewType = gDialog.ListTypeList.value; + + if (NewType == "ol") { + SetTextboxFocus(gDialog.StartingNumberInput); + } + + if (gListType != NewType) { + gListType = NewType; + + // Create a newlist object for Advanced Editing + try { + if (gListType) { + globalElement = GetCurrentEditor().createElementWithDefaults(gListType); + } + } catch (e) {} + + BuildBulletStyleList(); + } +} + +function SelectBulletStyle() { + // Save the selected index so when user changes + // list style, restore index to associated list + // Each bullet or number type is stored in the "value" of each menuitem + if (gListType == "ul") { + gBulletStyleType = gDialog.BulletStyleList.value; + } else if (gListType == "ol") { + var type = gDialog.BulletStyleList.value; + if (gNumberStyleType != type) { + // Convert existing input value to attr number first, + // then convert to the appropriate format for the newly-selected + gDialog.StartingNumberInput.value = ConvertStartAttrToUserString( + ConvertUserStringToStartAttr(gNumberStyleType), + type + ); + + gNumberStyleType = type; + SetTextboxFocus(gDialog.StartingNumberInput); + } + } +} + +function ValidateData() { + gBulletStyleType = gDialog.BulletStyleList.value; + // globalElement should already be of the correct type + + if (globalElement) { + var editor = GetCurrentEditor(); + if (gListType == "ul") { + if (gBulletStyleType && gDialog.ChangeAllRadio.selected) { + globalElement.setAttribute("type", gBulletStyleType); + } else { + try { + editor.removeAttributeOrEquivalent(globalElement, "type", true); + } catch (e) {} + } + } else if (gListType == "ol") { + if (gBulletStyleType) { + globalElement.setAttribute("type", gBulletStyleType); + } else { + try { + editor.removeAttributeOrEquivalent(globalElement, "type", true); + } catch (e) {} + } + + var startingNumber = ConvertUserStringToStartAttr(gBulletStyleType); + if (startingNumber) { + globalElement.setAttribute("start", startingNumber); + } else { + globalElement.removeAttribute("start"); + } + } + } + return true; +} + +function ConvertUserStringToStartAttr(type) { + var startingNumber = TrimString(gDialog.StartingNumberInput.value); + + switch (type) { + case gUpperRoman: + case gLowerRoman: + // If the input isn't an integer, assume it's a roman numeral. Convert it. + if (!Number(startingNumber)) { + startingNumber = ConvertRomanToArabic(startingNumber); + } + break; + case gUpperLetters: + case gLowerLetters: + // Get the number equivalent of the letters + if (!Number(startingNumber)) { + startingNumber = ConvertLettersToArabic(startingNumber); + } + break; + } + return startingNumber; +} + +function ConvertRomanToArabic(num) { + num = num.toUpperCase(); + if (num && !/[^MDCLXVI]/i.test(num)) { + var Arabic = 0; + var last_digit = 1000; + for (var i = 0; i < num.length; i++) { + var digit = gRomanDigits[num.charAt(i)]; + if (last_digit < digit) { + Arabic -= 2 * last_digit; + } + + last_digit = digit; + Arabic += last_digit; + } + return Arabic; + } + + return ""; +} + +function ConvertArabicToRoman(num) { + if (/^\d{1,4}$/.test(num)) { + var digits = ("000" + num).substr(-4); + return ( + gThousandsArray[digits.charAt(0)] + + gHundredsArray[digits.charAt(1)] + + gTensArray[digits.charAt(2)] + + gOnesArray[digits.charAt(3)] + ); + } + return ""; +} + +function ConvertLettersToArabic(letters) { + letters = letters.toUpperCase(); + if (!letters || /[^A-Z]/.test(letters)) { + return ""; + } + + var num = 0; + for (var i = 0; i < letters.length; i++) { + num = num * 26 + letters.charCodeAt(i) - A + 1; + } + return num; +} + +function ConvertArabicToLetters(num) { + var letters = ""; + while (num) { + num--; + letters = String.fromCharCode(A + (num % 26)) + letters; + num = Math.floor(num / 26); + } + return letters; +} + +function onAccept(event) { + if (ValidateData()) { + // Coalesce into one undo transaction + var editor = GetCurrentEditor(); + + editor.beginTransaction(); + + var changeEntireList = + gDialog.RadioGroup.selectedItem == gDialog.ChangeAllRadio; + + // Remember which radio button was selected + if (gListElement) { + gDialog.RadioGroup.setAttribute("index", changeEntireList ? "0" : "1"); + } + + var changeList; + if (gListElement && gDialog.ChangeAllRadio.selected) { + changeList = true; + } else { + changeList = + gMixedListSelection || + gListType != gOriginalListType || + gBulletStyleType != gOriginalStyleType; + } + if (changeList) { + try { + if (gListType) { + editor.makeOrChangeList( + gListType, + changeEntireList, + gBulletStyleType != gOriginalStyleType ? gBulletStyleType : null + ); + + // Get the new list created: + gListElement = editor.getElementOrParentByTagName(gListType, null); + + editor.cloneAttributes(gListElement, globalElement); + } else { + // Remove all existing lists + if (gListElement && changeEntireList) { + editor.selectElement(gListElement); + } + + editor.removeList("ol"); + editor.removeList("ul"); + editor.removeList("dl"); + } + } catch (e) {} + } + + editor.endTransaction(); + + SaveWindowLocation(); + + return; + } + event.preventDefault(); +} diff --git a/comm/suite/editor/components/dialogs/content/EdListProps.xhtml b/comm/suite/editor/components/dialogs/content/EdListProps.xhtml new file mode 100644 index 0000000000..cae9829c97 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdListProps.xhtml @@ -0,0 +1,73 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog [ +<!ENTITY % edListProperties SYSTEM "chrome://editor/locale/EditorListProperties.dtd"> +%edListProperties; +<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd"> +%edDialogOverlay; +]> + +<dialog title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload="Startup()"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EdListProps.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <groupbox flex="1"> + <hbox class="groupbox-title"> + <label class="header">&ListType.label;</label> + </hbox> + <menulist id="ListType" oncommand="SelectListType()"> + <menupopup> + <menuitem label="&none.value;"/> + <menuitem value="ul" label="&bulletList.value;"/> + <menuitem value="ol" label="&numberList.value;"/> + <menuitem value="dl" label="&definitionList.value;"/> + </menupopup> + </menulist> + </groupbox> + <spacer class="spacer"/> + + <!-- message text and list items are set in JS + text value should be identical to string with id=BulletStyle in editor.properties + --> + <groupbox flex="1"> + <hbox class="groupbox-title"> + <label id="BulletStyleLabel" class="header">&bulletStyle.label;</label> + </hbox> + <menulist class="MinWidth10em" id="BulletStyle" oncommand="SelectBulletStyle()"> + <menupopup/> + </menulist> + <spacer class="spacer"/> + <hbox> + <label id="StartingNumberLabel" control="StartingNumber" + value="&startingNumber.label;" accesskey="&startingNumber.accessKey;"/> + <textbox class="narrow" id="StartingNumber"/> + <spacer/> + </hbox> + </groupbox> + <radiogroup id="RadioGroup" index="0" persist="index"> + <radio id="ChangeAll" label="&changeEntireListRadio.label;" accesskey="&changeEntireListRadio.accessKey;"/> + <radio id="ChangeSelected" label="&changeSelectedRadio.label;" accesskey="&changeSelectedRadio.accessKey;"/> + </radiogroup> + <vbox id="AdvancedEdit"> + <hbox flex="1" style="margin-top: 0.2em" align="center"> + <!-- This will right-align the button --> + <spacer flex="1"/> + <button id="AdvancedEditButton1" oncommand="onAdvancedEdit()" label="&AdvancedEditButton.label;" + accesskey="&AdvancedEditButton.accessKey;" tooltiptext="&AdvancedEditButton.tooltip;"/> + </hbox> + <separator id="advancedSeparator" class="groove"/> + </vbox> +</dialog> diff --git a/comm/suite/editor/components/dialogs/content/EdNamedAnchorProps.js b/comm/suite/editor/components/dialogs/content/EdNamedAnchorProps.js new file mode 100644 index 0000000000..0433e58872 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdNamedAnchorProps.js @@ -0,0 +1,159 @@ +/* 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 gInsertNew = true; +var gAnchorElement = null; +var gOriginalName = ""; +const kTagName = "anchor"; + +// dialog initialization code + +document.addEventListener("dialogaccept", onAccept); +document.addEventListener("dialogcancel", onCancel); + +function Startup() { + var editor = GetCurrentEditor(); + if (!editor) { + window.close(); + return; + } + + gDialog.OkButton = document.documentElement.getButton("accept"); + gDialog.NameInput = document.getElementById("nameInput"); + + // Get a single selected element of the desired type + gAnchorElement = editor.getSelectedElement(kTagName); + + if (gAnchorElement) { + // We found an element and don't need to insert one + gInsertNew = false; + + // Make a copy to use for AdvancedEdit + globalElement = gAnchorElement.cloneNode(false); + gOriginalName = ConvertToCDATAString(gAnchorElement.name); + } else { + gInsertNew = true; + // We don't have an element selected, + // so create one with default attributes + gAnchorElement = editor.createElementWithDefaults(kTagName); + if (gAnchorElement) { + // Use the current selection as suggested name + var name = GetSelectionAsText(); + // Get 40 characters of the selected text and don't add "...", + // replace whitespace with "_" and strip non-word characters + name = ConvertToCDATAString(TruncateStringAtWordEnd(name, 40, false)); + // Be sure the name is unique to the document + if (AnchorNameExists(name)) { + name += "_"; + } + + // Make a copy to use for AdvancedEdit + globalElement = gAnchorElement.cloneNode(false); + globalElement.setAttribute("name", name); + } + } + if (!gAnchorElement) { + dump("Failed to get selected element or create a new one!\n"); + window.close(); + return; + } + + InitDialog(); + + DoEnabling(); + SetTextboxFocus(gDialog.NameInput); + SetWindowLocation(); +} + +function InitDialog() { + gDialog.NameInput.value = globalElement.getAttribute("name"); +} + +function ChangeName() { + if (gDialog.NameInput.value.length > 0) { + // Replace spaces with "_" and strip other non-URL characters + // Note: we could use ConvertAndEscape, but then we'd + // have to UnEscapeAndConvert beforehand - too messy! + gDialog.NameInput.value = ConvertToCDATAString(gDialog.NameInput.value); + } + DoEnabling(); +} + +function DoEnabling() { + var enable = gDialog.NameInput.value.length > 0; + SetElementEnabled(gDialog.OkButton, enable); + SetElementEnabledById("AdvancedEditButton1", enable); +} + +function AnchorNameExists(name) { + var anchorList; + try { + anchorList = GetCurrentEditor().document.anchors; + } catch (e) {} + + if (anchorList) { + for (var i = 0; i < anchorList.length; i++) { + if (anchorList[i].name == name) { + return true; + } + } + } + return false; +} + +// Get and validate data from widgets. +// Set attributes on globalElement so they can be accessed by AdvancedEdit() +function ValidateData() { + var name = TrimString(gDialog.NameInput.value); + if (!name) { + ShowInputErrorMessage(GetString("MissingAnchorNameError")); + SetTextboxFocus(gDialog.NameInput); + return false; + } + // Replace spaces with "_" and strip other characters + // Note: we could use ConvertAndEscape, but then we'd + // have to UnConverAndEscape beforehand - too messy! + name = ConvertToCDATAString(name); + + if (gOriginalName != name && AnchorNameExists(name)) { + ShowInputErrorMessage( + GetString("DuplicateAnchorNameError").replace(/%name%/, name) + ); + SetTextboxFocus(gDialog.NameInput); + return false; + } + globalElement.name = name; + + return true; +} + +function onAccept(event) { + if (ValidateData()) { + if (gOriginalName != globalElement.name) { + var editor = GetCurrentEditor(); + editor.beginTransaction(); + + try { + // "false" = don't delete selected text when inserting + if (gInsertNew) { + // We must insert element before copying CSS style attribute, + // but we must set the name else it won't insert at all + gAnchorElement.name = globalElement.name; + editor.insertElementAtSelection(gAnchorElement, false); + } + + // Copy attributes to element we are changing or inserting + editor.cloneAttributes(gAnchorElement, globalElement); + } catch (e) {} + + editor.endTransaction(); + } + SaveWindowLocation(); + return; + } + event.preventDefault(); +} diff --git a/comm/suite/editor/components/dialogs/content/EdNamedAnchorProps.xhtml b/comm/suite/editor/components/dialogs/content/EdNamedAnchorProps.xhtml new file mode 100644 index 0000000000..3a38256222 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdNamedAnchorProps.xhtml @@ -0,0 +1,43 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog [ +<!ENTITY % edNamedAnchorProperties SYSTEM "chrome://editor/locale/EdNamedAnchorProperties.dtd"> +%edNamedAnchorProperties; +<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd"> +%edDialogOverlay; +]> + +<dialog title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload="Startup()"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EdNamedAnchorProps.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <label control="nameInput" + value="&anchorNameEditField.label;" + accesskey="&anchorNameEditField.accessKey;"/> + <textbox class="MinWidth20em" id="nameInput" oninput="ChangeName()" + tooltiptext="&nameInput.tooltip;"/> + <spacer class="spacer"/> + <vbox id="AdvancedEdit"> + <hbox flex="1" style="margin-top: 0.2em" align="center"> + <!-- This will right-align the button --> + <spacer flex="1"/> + <button id="AdvancedEditButton1" oncommand="onAdvancedEdit()" label="&AdvancedEditButton.label;" + accesskey="&AdvancedEditButton.accessKey;" tooltiptext="&AdvancedEditButton.tooltip;"/> + </hbox> + <separator id="advancedSeparator" class="groove"/> + </vbox> +</dialog> diff --git a/comm/suite/editor/components/dialogs/content/EdPageProps.js b/comm/suite/editor/components/dialogs/content/EdPageProps.js new file mode 100644 index 0000000000..568eff66ec --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdPageProps.js @@ -0,0 +1,159 @@ +/* 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 gNewTitle = ""; +var gAuthor = ""; +var gDescription = ""; +var gAuthorElement; +var gDescriptionElement; +var gInsertNewAuthor = false; +var gInsertNewDescription = false; +var gTitleWasEdited = false; +var gAuthorWasEdited = false; +var gDescWasEdited = false; + +// Cancel() is in EdDialogCommon.js +// dialog initialization code + +document.addEventListener("dialogaccept", onAccept); +document.addEventListener("dialogcancel", onCancel); + +function Startup() { + var editor = GetCurrentEditor(); + if (!editor) { + window.close(); + return; + } + + gDialog.PageLocation = document.getElementById("PageLocation"); + gDialog.PageModDate = document.getElementById("PageModDate"); + gDialog.TitleInput = document.getElementById("TitleInput"); + gDialog.AuthorInput = document.getElementById("AuthorInput"); + gDialog.DescriptionInput = document.getElementById("DescriptionInput"); + + // Default string for new page is set from DTD string in XUL, + // so set only if not new doc URL + var location = GetDocumentUrl(); + var lastmodString = GetString("Unknown"); + + if (!IsUrlAboutBlank(location)) { + // NEVER show username and password in clear text + gDialog.PageLocation.setAttribute("value", StripPassword(location)); + + // Get last-modified file date+time + // TODO: Convert this to local time? + var lastmod; + try { + lastmod = editor.document.lastModified; // get string of last modified date + } catch (e) {} + // Convert modified string to date (0 = unknown date or January 1, 1970 GMT) + if (Date.parse(lastmod)) { + try { + const dateTimeFormatter = new Services.intl.DateTimeFormat(undefined, { + dateStyle: "long", + timeStyle: "short", + }); + + var lastModDate = new Date(); + lastModDate.setTime(Date.parse(lastmod)); + lastmodString = dateTimeFormatter.format(lastModDate); + } catch (e) {} + } + } + gDialog.PageModDate.value = lastmodString; + + gAuthorElement = GetMetaElementByAttribute("name", "author"); + if (!gAuthorElement) { + gAuthorElement = CreateMetaElementWithAttribute("name", "author"); + if (!gAuthorElement) { + window.close(); + return; + } + gInsertNewAuthor = true; + } + + gDescriptionElement = GetMetaElementByAttribute("name", "description"); + if (!gDescriptionElement) { + gDescriptionElement = CreateMetaElementWithAttribute("name", "description"); + if (!gDescriptionElement) { + window.close(); + } + + gInsertNewDescription = true; + } + + InitDialog(); + + SetTextboxFocus(gDialog.TitleInput); + + SetWindowLocation(); +} + +function InitDialog() { + gDialog.TitleInput.value = GetDocumentTitle(); + + var gAuthor = TrimString(gAuthorElement.getAttribute("content")); + if (!gAuthor) { + // Fill in with value from editor prefs + gAuthor = Services.prefs.getCharPref("editor.author"); + } + gDialog.AuthorInput.value = gAuthor; + gDialog.DescriptionInput.value = gDescriptionElement.getAttribute("content"); +} + +function TextboxChanged(ID) { + switch (ID) { + case "TitleInput": + gTitleWasEdited = true; + break; + case "AuthorInput": + gAuthorWasEdited = true; + break; + case "DescriptionInput": + gDescWasEdited = true; + break; + } +} + +function ValidateData() { + gNewTitle = TrimString(gDialog.TitleInput.value); + gAuthor = TrimString(gDialog.AuthorInput.value); + gDescription = TrimString(gDialog.DescriptionInput.value); + return true; +} + +function onAccept(event) { + if (ValidateData()) { + var editor = GetCurrentEditor(); + editor.beginTransaction(); + + // Set title contents even if string is empty + // because TITLE is a required HTML element + if (gTitleWasEdited) { + SetDocumentTitle(gNewTitle); + } + + if (gAuthorWasEdited) { + SetMetaElementContent(gAuthorElement, gAuthor, gInsertNewAuthor, false); + } + + if (gDescWasEdited) { + SetMetaElementContent( + gDescriptionElement, + gDescription, + gInsertNewDescription, + false + ); + } + + editor.endTransaction(); + + SaveWindowLocation(); + return; // do close the window + } + event.preventDefault(); +} diff --git a/comm/suite/editor/components/dialogs/content/EdPageProps.xhtml b/comm/suite/editor/components/dialogs/content/EdPageProps.xhtml new file mode 100644 index 0000000000..4891baa04a --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdPageProps.xhtml @@ -0,0 +1,50 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorPageProperties.dtd"> + +<dialog title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload="Startup();"> + + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EdPageProps.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + <grid> + <columns><column flex="1"/><column flex="2"/></columns> + <rows> + <row> + <label value="&location.label;"/> + <label value="&locationNewPage.label;" id="PageLocation"/> + </row> + <row> + <label value="&lastModified.label;"/> + <label id="PageModDate"/> + </row> + <spacer class="spacer"/> + <row align="center"> + <label value="&titleInput.label;" accesskey="&titleInput.accessKey;" control="TitleInput"/> + <textbox class="MinWidth20em" id="TitleInput" oninput="TextboxChanged(this.id)"/> + </row> + <row align="center"> + <label value="&authorInput.label;" accesskey="&authorInput.accessKey;" control="AuthorInput"/> + <textbox class="MinWidth20em" id="AuthorInput" oninput="TextboxChanged(this.id)"/> + </row> + <row align="center"> + <label value="&descriptionInput.label;" accesskey="&descriptionInput.accessKey;" control="DescriptionInput"/> + <textbox class="MinWidth20em" id="DescriptionInput" oninput="TextboxChanged(this.id)"/> + </row> + </rows> + </grid> + <spacer class="bigspacer"/> + <label value="&EditHEADSource1.label;"/> + <description class="wrap" flex="1">&EditHEADSource2.label;</description> + <separator class="groove"/> +</dialog> diff --git a/comm/suite/editor/components/dialogs/content/EdReplace.js b/comm/suite/editor/components/dialogs/content/EdReplace.js new file mode 100644 index 0000000000..c0daea29de --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdReplace.js @@ -0,0 +1,382 @@ +/* -*- 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 gReplaceDialog; // Quick access to document/form elements. +var gFindInst; // nsIWebBrowserFind that we're going to use +var gFindService; // Global service which remembers find params +var gEditor; // the editor we're using + +document.addEventListener("dialogaccept", event => { + onFindNext(); + event.preventDefault(); +}); + +function initDialogObject() { + // Create gReplaceDialog object and initialize. + gReplaceDialog = {}; + gReplaceDialog.findInput = document.getElementById("dialog.findInput"); + gReplaceDialog.replaceInput = document.getElementById("dialog.replaceInput"); + gReplaceDialog.caseSensitive = document.getElementById( + "dialog.caseSensitive" + ); + gReplaceDialog.wrap = document.getElementById("dialog.wrap"); + gReplaceDialog.searchBackwards = document.getElementById( + "dialog.searchBackwards" + ); + gReplaceDialog.findNext = document.getElementById("findNext"); + gReplaceDialog.replace = document.getElementById("replace"); + gReplaceDialog.replaceAndFind = document.getElementById("replaceAndFind"); + gReplaceDialog.replaceAll = document.getElementById("replaceAll"); +} + +function loadDialog() { + // Set initial dialog field contents. + // Set initial dialog field contents. Use the gFindInst attributes first, + // this is necessary for window.find() + gReplaceDialog.findInput.value = gFindInst.searchString + ? gFindInst.searchString + : gFindService.searchString; + gReplaceDialog.replaceInput.value = gFindService.replaceString; + gReplaceDialog.caseSensitive.checked = gFindInst.matchCase + ? gFindInst.matchCase + : gFindService.matchCase; + gReplaceDialog.wrap.checked = gFindInst.wrapFind + ? gFindInst.wrapFind + : gFindService.wrapFind; + gReplaceDialog.searchBackwards.checked = gFindInst.findBackwards + ? gFindInst.findBackwards + : gFindService.findBackwards; + + doEnabling(); +} + +function onLoad() { + // Get the xul <editor> element: + var editorElement = window.arguments[0]; + + // If we don't get the editor, then we won't allow replacing. + gEditor = editorElement.getEditor(editorElement.contentWindow); + if (!gEditor) { + window.close(); + return; + } + + // Get the nsIWebBrowserFind service: + gFindInst = editorElement.webBrowserFind; + + try { + // get the find service, which stores global find state + gFindService = Cc["@mozilla.org/find/find_service;1"].getService( + Ci.nsIFindService + ); + } catch (e) { + dump("No find service!\n"); + gFindService = 0; + } + + // Init gReplaceDialog. + initDialogObject(); + + // Change "OK" to "Find". + // dialog.find.label = document.getElementById("fBLT").getAttribute("label"); + + // Fill dialog. + loadDialog(); + + if (gReplaceDialog.findInput.value) { + gReplaceDialog.findInput.select(); + } else { + gReplaceDialog.findInput.focus(); + } +} + +function saveFindData() { + // Set data attributes per user input. + if (gFindService) { + gFindService.searchString = gReplaceDialog.findInput.value; + gFindService.matchCase = gReplaceDialog.caseSensitive.checked; + gFindService.wrapFind = gReplaceDialog.wrap.checked; + gFindService.findBackwards = gReplaceDialog.searchBackwards.checked; + } +} + +function setUpFindInst() { + gFindInst.searchString = gReplaceDialog.findInput.value; + gFindInst.matchCase = gReplaceDialog.caseSensitive.checked; + gFindInst.wrapFind = gReplaceDialog.wrap.checked; + gFindInst.findBackwards = gReplaceDialog.searchBackwards.checked; +} + +function onFindNext() { + // Transfer dialog contents to the find service. + saveFindData(); + // set up the find instance + setUpFindInst(); + + // Search. + var result = gFindInst.findNext(); + + if (!result) { + var bundle = document.getElementById("findBundle"); + Services.prompt.alert( + window, + GetString("Alert"), + bundle.getString("notFoundWarning") + ); + SetTextboxFocus(gReplaceDialog.findInput); + gReplaceDialog.findInput.select(); + gReplaceDialog.findInput.focus(); + return false; + } + return true; +} + +function onReplace() { + if (!gEditor) { + return false; + } + + // Does the current selection match the find string? + var selection = gEditor.selection; + + var selStr = selection.toString(); + var specStr = gReplaceDialog.findInput.value; + if (!gReplaceDialog.caseSensitive.checked) { + selStr = selStr.toLowerCase(); + specStr = specStr.toLowerCase(); + } + // Unfortunately, because of whitespace we can't just check + // whether (selStr == specStr), but have to loop ourselves. + // N chars of whitespace in specStr can match any M >= N in selStr. + var matches = true; + var specLen = specStr.length; + var selLen = selStr.length; + if (selLen < specLen) { + matches = false; + } else { + var specArray = specStr.match(/\S+|\s+/g); + var selArray = selStr.match(/\S+|\s+/g); + if (specArray.length != selArray.length) { + matches = false; + } else { + for (var i = 0; i < selArray.length; i++) { + if (selArray[i] != specArray[i]) { + if (/\S/.test(selArray[i][0]) || /\S/.test(specArray[i][0])) { + // not a space chunk -- match fails + matches = false; + break; + } else if (selArray[i].length < specArray[i].length) { + // if it's a space chunk then we only care that sel be + // at least as long as spec + matches = false; + break; + } + } + } + } + } + + // If the current selection doesn't match the pattern, + // then we want to find the next match, but not do the replace. + // That's what most other apps seem to do. + // So here, just return. + if (!matches) { + return false; + } + + // Transfer dialog contents to the find service. + saveFindData(); + + // For reverse finds, need to remember the caret position + // before current selection + var newRange; + if (gReplaceDialog.searchBackwards.checked && selection.rangeCount > 0) { + newRange = selection.getRangeAt(0).cloneRange(); + newRange.collapse(true); + } + + // nsPlaintextEditor::InsertText fails if the string is empty, + // so make that a special case: + var replStr = gReplaceDialog.replaceInput.value; + if (replStr == "") { + gEditor.deleteSelection(gEditor.eNone, gEditor.eStrip); + } else { + gEditor.insertText(replStr); + } + + // For reverse finds, need to move caret just before the replaced text + if (gReplaceDialog.searchBackwards.checked && newRange) { + gEditor.selection.removeAllRanges(); + gEditor.selection.addRange(newRange); + } + + return true; +} + +function onReplaceAll() { + if (!gEditor) { + return; + } + + var findStr = gReplaceDialog.findInput.value; + var repStr = gReplaceDialog.replaceInput.value; + + // Transfer dialog contents to the find service. + saveFindData(); + + var finder = Cc["@mozilla.org/embedcomp/rangefind;1"] + .createInstance() + .QueryInterface(Ci.nsIFind); + + finder.caseSensitive = gReplaceDialog.caseSensitive.checked; + finder.findBackwards = gReplaceDialog.searchBackwards.checked; + + // We want the whole operation to be undoable in one swell foop, + // so start a transaction: + gEditor.beginTransaction(); + + // and to make sure we close the transaction, guard against exceptions: + try { + // Make a range containing the current selection, + // so we don't go past it when we wrap. + var selection = gEditor.selection; + var selecRange; + if (selection.rangeCount > 0) { + selecRange = selection.getRangeAt(0); + } + var origRange = selecRange.cloneRange(); + + // We'll need a range for the whole document: + var wholeDocRange = gEditor.document.createRange(); + var rootNode = gEditor.rootElement; + wholeDocRange.selectNodeContents(rootNode); + + // And start and end points: + var endPt = gEditor.document.createRange(); + + if (gReplaceDialog.searchBackwards.checked) { + endPt.setStart(wholeDocRange.startContainer, wholeDocRange.startOffset); + endPt.setEnd(wholeDocRange.startContainer, wholeDocRange.startOffset); + } else { + endPt.setStart(wholeDocRange.endContainer, wholeDocRange.endOffset); + endPt.setEnd(wholeDocRange.endContainer, wholeDocRange.endOffset); + } + + // Find and replace from here to end (start) of document: + var foundRange; + var searchRange = wholeDocRange.cloneRange(); + while ( + (foundRange = finder.Find(findStr, searchRange, selecRange, endPt)) != + null + ) { + gEditor.selection.removeAllRanges(); + gEditor.selection.addRange(foundRange); + + // The editor will leave the caret at the end of the replaced text. + // For reverse finds, we need it at the beginning, + // so save the next position now. + if (gReplaceDialog.searchBackwards.checked) { + selecRange = foundRange.cloneRange(); + selecRange.setEnd(selecRange.startContainer, selecRange.startOffset); + } + + // nsPlaintextEditor::InsertText fails if the string is empty, + // so make that a special case: + if (repStr == "") { + gEditor.deleteSelection(gEditor.eNone, gEditor.eStrip); + } else { + gEditor.insertText(repStr); + } + + // If we're going forward, we didn't save selecRange before, so do it now: + if (!gReplaceDialog.searchBackwards.checked) { + selection = gEditor.selection; + if (selection.rangeCount <= 0) { + gEditor.endTransaction(); + return; + } + selecRange = selection.getRangeAt(0).cloneRange(); + } + } + + // If no wrapping, then we're done + if (!gReplaceDialog.wrap.checked) { + gEditor.endTransaction(); + return; + } + + // If wrapping, find from start/end of document back to start point. + if (gReplaceDialog.searchBackwards.checked) { + // Collapse origRange to end + origRange.setStart(origRange.endContainer, origRange.endOffset); + // Set current position to document end + selecRange.setEnd(wholeDocRange.endContainer, wholeDocRange.endOffset); + selecRange.setStart(wholeDocRange.endContainer, wholeDocRange.endOffset); + } else { + // Collapse origRange to start + origRange.setEnd(origRange.startContainer, origRange.startOffset); + // Set current position to document start + selecRange.setStart( + wholeDocRange.startContainer, + wholeDocRange.startOffset + ); + selecRange.setEnd( + wholeDocRange.startContainer, + wholeDocRange.startOffset + ); + } + + while ( + (foundRange = finder.Find( + findStr, + wholeDocRange, + selecRange, + origRange + )) != null + ) { + gEditor.selection.removeAllRanges(); + gEditor.selection.addRange(foundRange); + + // Save insert point for backward case + if (gReplaceDialog.searchBackwards.checked) { + selecRange = foundRange.cloneRange(); + selecRange.setEnd(selecRange.startContainer, selecRange.startOffset); + } + + // nsPlaintextEditor::InsertText fails if the string is empty, + // so make that a special case: + if (repStr == "") { + gEditor.deleteSelection(gEditor.eNone, gEditor.eStrip); + } else { + gEditor.insertText(repStr); + } + + // Get insert point for forward case + if (!gReplaceDialog.searchBackwards.checked) { + selection = gEditor.selection; + if (selection.rangeCount <= 0) { + gEditor.endTransaction(); + return; + } + selecRange = selection.getRangeAt(0); + } + } + } catch (e) {} + + gEditor.endTransaction(); +} + +function doEnabling() { + var findStr = gReplaceDialog.findInput.value; + gReplaceDialog.enabled = findStr; + gReplaceDialog.findNext.disabled = !findStr; + gReplaceDialog.replace.disabled = !findStr; + gReplaceDialog.replaceAndFind.disabled = !findStr; + gReplaceDialog.replaceAll.disabled = !findStr; +} diff --git a/comm/suite/editor/components/dialogs/content/EdReplace.xhtml b/comm/suite/editor/components/dialogs/content/EdReplace.xhtml new file mode 100644 index 0000000000..54a9d81ba3 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdReplace.xhtml @@ -0,0 +1,65 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorReplace.dtd"> + +<dialog id="replaceDlg" title="&replaceDialog.title;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + persist="screenX screenY" + buttons="cancel" + onload="onLoad()"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EdReplace.js"/> + <stringbundle id="findBundle" src="chrome://global/locale/finddialog.properties"/> + + <hbox> + <vbox> + <spacer class="spacer"/> + <grid align="start"> + <columns><column/><column/></columns> + <rows> + <row align="center"> + <label value="&findField.label;" accesskey="&findField.accesskey;" control="dialog.findInput"/> + <textbox id="dialog.findInput" oninput="doEnabling();"/> + </row> + <row align="center"> + <label value="&replaceField.label;" accesskey="&replaceField.accesskey;" control="dialog.replaceInput"/> + <textbox id="dialog.replaceInput" oninput="doEnabling();"/> + </row> + <row align="start"> + <spacer/> + <vbox align="start"> + <spacer class="bigspacer"/> + <checkbox id="dialog.caseSensitive" label="&caseSensitiveCheckbox.label;" + accesskey="&caseSensitiveCheckbox.accesskey;"/> + <checkbox id="dialog.wrap" label="&wrapCheckbox.label;" + accesskey="&wrapCheckbox.accesskey;"/> + <checkbox id="dialog.searchBackwards" label="&backwardsCheckbox.label;" + accesskey="&backwardsCheckbox.accesskey;"/> + </vbox> + </row> + </rows> + </grid> + </vbox> + <vbox> + <button id="findNext" label="&findNextButton.label;" accesskey="&findNextButton.accesskey;" + oncommand="onFindNext();" default="true"/> + <button id="replace" label="&replaceButton.label;" accesskey="&replaceButton.accesskey;" + oncommand="onReplace();"/> + <button id="replaceAndFind" label="&replaceAndFindButton.label;" + accesskey="&replaceAndFindButton.accesskey;" oncommand="onReplace(); onFindNext();"/> + <button id="replaceAll" label="&replaceAllButton.label;" + accesskey="&replaceAllButton.accesskey;" oncommand="onReplaceAll();"/> + <button dlgtype="cancel" label="&closeButton.label;" accesskey="&closeButton.accesskey;"/> + </vbox> + </hbox> +</dialog> diff --git a/comm/suite/editor/components/dialogs/content/EdSelectProps.js b/comm/suite/editor/components/dialogs/content/EdSelectProps.js new file mode 100644 index 0000000000..c03fd73a67 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdSelectProps.js @@ -0,0 +1,770 @@ +/* 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 */ + +// Global variables + +var hasValue; +var oldValue; +var insertNew; +var itemArray; +var theTree; +var treeSelection; +var selectElement; +var currentItem = null; +var selectedOption = null; +var selectedOptionCount = 0; + +document.addEventListener("dialogaccept", onAccept); +document.addEventListener("dialogcancel", onCancel); + +// Utility functions + +function getParentIndex(index) { + switch (itemArray[index].level) { + case 0: + return -1; + case 1: + return 0; + } + // eslint-disable-next-line curly + while (itemArray[--index].level > 1); + return index; +} + +function UpdateSelectMultiple() { + if (selectedOptionCount > 1) { + gDialog.selectMultiple.checked = true; + gDialog.selectMultiple.disabled = true; + } else { + gDialog.selectMultiple.disabled = false; + } +} + +/* wrapper objects: + * readonly attribute Node element; // DOM node (select/optgroup/option) + * readonly attribute int level; // tree depth + * readonly attribute boolean container; // can contain options + * string getCellText(string col); // tree view helper + * string cycleCell(int currentIndex); // tree view helper + * void onFocus(); // load data into deck + * void onBlur(); // save data from deck + * boolean canDestroy(boolean prompt); // NB prompt not used + * void destroy(); // post remove callback + * void moveUp(); + * boolean canMoveDown(); + * void moveDown(); + * void appendOption(newElement, currentIndex); + */ + +// OPTION element wrapper object + +// Create a wrapper for the given element at the given level +function optionObject(option, level) { + // select an added option (when loading from document) + if (option.hasAttribute("selected")) { + selectedOptionCount++; + } + this.level = level; + this.element = option; +} + +optionObject.prototype.container = false; + +optionObject.prototype.getCellText = function(column) { + if (column.id == "SelectSelCol") { + return ""; + } + if (column.id == "SelectValCol" && this.element.hasAttribute("value")) { + return this.element.getAttribute("value"); + } + return this.element.text; +}; + +optionObject.prototype.cycleCell = function(index) { + if (this.element.hasAttribute("selected")) { + this.element.removeAttribute("selected"); + selectedOptionCount--; + selectedOption = null; + } else { + // Different handling for multiselect lists + if (gDialog.selectMultiple.checked || !selectedOption) { + selectedOptionCount++; + } else if (selectedOption) { + selectedOption.removeAttribute("selected"); + let column = theTree.columns.SelectSelCol; + theTree.invalidateColumn(column); + selectedOption = null; + } + this.element.setAttribute("selected", ""); + selectedOption = this.element; + let column = theTree.columns.SelectSelCol; + theTree.invalidateCell(index, column); + } + if (currentItem == this) { + // Also update the deck + gDialog.optionSelected.setAttribute( + "checked", + this.element.hasAttribute("selected") + ); + } + UpdateSelectMultiple(); +}; + +optionObject.prototype.onFocus = function() { + gDialog.optionText.value = this.element.text; + hasValue = this.element.hasAttribute("value"); + oldValue = this.element.value; + gDialog.optionHasValue.checked = hasValue; + gDialog.optionValue.value = hasValue ? this.element.value : this.element.text; + gDialog.optionSelected.checked = this.element.hasAttribute("selected"); + gDialog.optionDisabled.checked = this.element.hasAttribute("disabled"); + gDialog.selectDeck.setAttribute("selectedIndex", "2"); +}; + +optionObject.prototype.onBlur = function() { + this.element.text = gDialog.optionText.value; + if (gDialog.optionHasValue.checked) { + this.element.value = gDialog.optionValue.value; + } else { + this.element.removeAttribute("value"); + } + if (gDialog.optionSelected.checked) { + this.element.setAttribute("selected", ""); + } else { + this.element.removeAttribute("selected"); + } + if (gDialog.optionDisabled.checked) { + this.element.setAttribute("disabled", ""); + } else { + this.element.removeAttribute("disabled"); + } +}; + +optionObject.prototype.canDestroy = function(prompt) { + return true; + /* return !prompt || + ConfirmWithTitle(GetString("DeleteOption"), + GetString("DeleteOptionMsg"), + GetString("DeleteOption"));*/ +}; + +optionObject.prototype.destroy = function() { + // Deselect a removed option + if (this.element.hasAttribute("selected")) { + selectedOptionCount--; + selectedOption = null; + UpdateSelectMultiple(); + } +}; + +/* 4 cases: + * a) optgroup -> optgroup + * ... ... + * option option + * b) optgroup -> option + * option optgroup + * ... ... + * c) option + * option + * d) option + * option + */ + +optionObject.prototype.moveUp = function() { + var index = treeSelection.currentIndex; + if ( + itemArray[index].level < + itemArray[index - 1].level + itemArray[index - 1].container + ) { + // we need to repaint the tree's lines + theTree.invalidateRange(getParentIndex(index), index); + // a) option is just after an optgroup, so it becomes the last child + itemArray[index].level = 2; + theTree.view.selectionChanged(); + } else { + // otherwise new option level is now the same as the previous item + itemArray[index].level = itemArray[index - 1].level; + // swap the option with the previous item + itemArray.splice(index, 0, itemArray.splice(--index, 1)[0]); + } + selectTreeIndex(index, true); +}; + +optionObject.prototype.canMoveDown = function() { + // move down is not allowed on the last option if its level is 1 + return this.level > 1 || itemArray.length - treeSelection.currentIndex > 1; +}; + +optionObject.prototype.moveDown = function() { + var index = treeSelection.currentIndex; + if ( + index + 1 == itemArray.length || + itemArray[index].level > itemArray[index + 1].level + ) { + // we need to repaint the tree's lines + theTree.invalidateRange(getParentIndex(index), index); + // a) option is last child of an optgroup, so it moves just after + itemArray[index].level = 1; + theTree.view.selectionChanged(); + } else { + // level increases if the option was preceding an optgroup + itemArray[index].level += itemArray[index + 1].container; + // swap the option with the next item + itemArray.splice(index, 0, itemArray.splice(++index, 1)[0]); + } + selectTreeIndex(index, true); +}; + +optionObject.prototype.appendOption = function(child, parent) { + // special case quick check + if (this.level == 1) { + return gDialog.appendOption(child, 0); + } + + // append the option to the parent element + parent = getParentIndex(parent); + return itemArray[parent].appendOption(child, parent); +}; + +// OPTGROUP element wrapper object + +function optgroupObject(optgroup) { + this.element = optgroup; +} + +optgroupObject.prototype.level = 1; + +optgroupObject.prototype.container = true; + +optgroupObject.prototype.getCellText = function(column) { + return column.id == "SelectTextCol" ? this.element.label : ""; +}; + +optgroupObject.prototype.cycleCell = function(index) {}; + +optgroupObject.prototype.onFocus = function() { + gDialog.optgroupLabel.value = this.element.label; + gDialog.optgroupDisabled.checked = this.element.disabled; + gDialog.selectDeck.setAttribute("selectedIndex", "1"); +}; + +optgroupObject.prototype.onBlur = function() { + this.element.label = gDialog.optgroupLabel.value; + this.element.disabled = gDialog.optgroupDisabled.checked; +}; + +optgroupObject.prototype.canDestroy = function(prompt) { + // Only removing empty option groups for now + return ( + gDialog.nextChild(treeSelection.currentIndex) - + treeSelection.currentIndex == + 1 + ); + /* && (!prompt || + ConfirmWithTitle(GetString("DeleteOptGroup"), + GetString("DeleteOptGroupMsg"), + GetString("DeleteOptGroup"))); +*/ +}; + +optgroupObject.prototype.destroy = function() {}; + +optgroupObject.prototype.moveUp = function() { + // Find the index of the previous and next elements at the same level + var index = treeSelection.currentIndex; + var i = index; + // eslint-disable-next-line curly + while (itemArray[--index].level > 1); + var j = gDialog.nextChild(i); + // Cut out the element, cut the array in two, then join together + var movedItems = itemArray.splice(i, j - i); + var endItems = itemArray.splice(index); + itemArray = itemArray.concat(movedItems).concat(endItems); + // Repaint the lot + theTree.invalidateRange(index, j); + selectTreeIndex(index, true); +}; + +optgroupObject.prototype.canMoveDown = function() { + return gDialog.lastChild() > treeSelection.currentIndex; +}; + +optgroupObject.prototype.moveDown = function() { + // Find the index of the next two elements at the same level + var index = treeSelection.currentIndex; + var i = gDialog.nextChild(index); + var j = gDialog.nextChild(i); + // Cut out the element, cut the array in two, then join together + var movedItems = itemArray.splice(i, j - 1); + var endItems = itemArray.splice(index); + itemArray = itemArray.concat(movedItems).concat(endItems); + // Repaint the lot + theTree.invalidateRange(index, j); + index += j - i; + selectTreeIndex(index, true); +}; + +optgroupObject.prototype.appendOption = function(child, parent) { + var index = gDialog.nextChild(parent); + // XXX need to repaint the lines, tree won't do this + var primaryCol = theTree.columns.getPrimaryColumn(); + theTree.invalidateCell(index - 1, primaryCol); + // insert the wrapped object as the last child + itemArray.splice(index, 0, new optionObject(child, 2)); + theTree.rowCountChanged(index, 1); + selectTreeIndex(index, false); +}; + +// dialog initialization code + +function Startup() { + var editor = GetCurrentEditor(); + if (!editor) { + dump("Failed to get active editor!\n"); + window.close(); + return; + } + + // Get a single selected select element + const kTagName = "select"; + try { + selectElement = editor.getSelectedElement(kTagName); + } catch (e) {} + + if (selectElement) { + // 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 { + selectElement = editor.createElementWithDefaults(kTagName); + } catch (e) {} + + if (!selectElement) { + dump("Failed to get selected element or create a new one!\n"); + window.close(); + return; + } + } + + // SELECT element wrapper object + gDialog = { + // useful elements + accept: document.documentElement.getButton("accept"), + selectDeck: document.getElementById("SelectDeck"), + selectName: document.getElementById("SelectName"), + selectSize: document.getElementById("SelectSize"), + selectMultiple: document.getElementById("SelectMultiple"), + selectDisabled: document.getElementById("SelectDisabled"), + selectTabIndex: document.getElementById("SelectTabIndex"), + optgroupLabel: document.getElementById("OptGroupLabel"), + optgroupDisabled: document.getElementById("OptGroupDisabled"), + optionText: document.getElementById("OptionText"), + optionHasValue: document.getElementById("OptionHasValue"), + optionValue: document.getElementById("OptionValue"), + optionSelected: document.getElementById("OptionSelected"), + optionDisabled: document.getElementById("OptionDisabled"), + removeButton: document.getElementById("RemoveButton"), + previousButton: document.getElementById("PreviousButton"), + nextButton: document.getElementById("NextButton"), + tree: document.getElementById("SelectTree"), + // wrapper methods (except MoveUp and MoveDown) + element: selectElement.cloneNode(false), + level: 0, + container: true, + getCellText(column) { + return column.id == "SelectTextCol" + ? this.element.getAttribute("name") + : ""; + }, + cycleCell(index) {}, + onFocus() { + gDialog.selectName.value = this.element.getAttribute("name"); + gDialog.selectSize.value = this.element.getAttribute("size"); + gDialog.selectMultiple.checked = this.element.hasAttribute("multiple"); + gDialog.selectDisabled.checked = this.element.hasAttribute("disabled"); + gDialog.selectTabIndex.value = this.element.getAttribute("tabindex"); + this.selectDeck.setAttribute("selectedIndex", "0"); + onNameInput(); + }, + onBlur() { + this.element.setAttribute("name", gDialog.selectName.value); + if (gDialog.selectSize.value) { + this.element.setAttribute("size", gDialog.selectSize.value); + } else { + this.element.removeAttribute("size"); + } + if (gDialog.selectMultiple.checked) { + this.element.setAttribute("multiple", ""); + } else { + this.element.removeAttribute("multiple"); + } + if (gDialog.selectDisabled.checked) { + this.element.setAttribute("disabled", ""); + } else { + this.element.removeAttribute("disabled"); + } + if (gDialog.selectTabIndex.value) { + this.element.setAttribute("tabindex", gDialog.selectTabIndex.value); + } else { + this.element.removeAttribute("tabindex"); + } + }, + appendOption(child, parent) { + var index = itemArray.length; + // XXX need to repaint the lines, tree won't do this + theTree.invalidateRange(this.lastChild(), index); + // append the wrapped object + itemArray.push(new optionObject(child, 1)); + theTree.rowCountChanged(index, 1); + selectTreeIndex(index, false); + }, + canDestroy(prompt) { + return false; + }, + canMoveDown() { + return false; + }, + // helper methods + // Find the index of the next immediate child of the select + nextChild(index) { + // eslint-disable-next-line curly + while (++index < itemArray.length && itemArray[index].level > 1); + return index; + }, + // Find the index of the last immediate child of the select + lastChild() { + var index = itemArray.length; + // eslint-disable-next-line curly + while (itemArray[--index].level > 1); + return index; + }, + }; + // Start with the <select> wrapper + itemArray = [gDialog]; + + // We modify the actual option and optgroup elements so clone them first + for (var child = selectElement.firstChild; child; child = child.nextSibling) { + if (child.tagName == "OPTION") { + itemArray.push(new optionObject(child.cloneNode(true), 1)); + } else if (child.tagName == "OPTGROUP") { + itemArray.push(new optgroupObject(child.cloneNode(false))); + for ( + var grandchild = child.firstChild; + grandchild; + grandchild = grandchild.nextSibling + ) { + if (grandchild.tagName == "OPTION") { + itemArray.push(new optionObject(grandchild.cloneNode(true), 2)); + } + } + } + } + + UpdateSelectMultiple(); + + // Define a custom view for the tree + theTree = gDialog.tree; + theTree.view = { + QueryInterface: ChromeUtils.generateQI([ + "nsITreeView", + "nsISupportsWeakReference", + ]), + // useful for debugging + get wrappedJSObject() { + return this; + }, + get rowCount() { + return itemArray.length; + }, + get selection() { + return treeSelection; + }, + set selection(selection) { + return (treeSelection = selection); + }, + getRowProperties(index) { + return ""; + }, + // could have used a wrapper for this + getCellProperties(index, column) { + if (column.id == "SelectSelCol" && !itemArray[index].container) { + return "checked-" + itemArray[index].element.hasAttribute("selected"); + } + return ""; + }, + getColumnProperties(column) { + return ""; + }, + // get info from wrapper + isContainer(index) { + return itemArray[index].container; + }, + isContainerOpen(index) { + return true; + }, + isContainerEmpty(index) { + return true; + }, + isSeparator(index) { + return false; + }, + isSorted() { + return false; + }, + // d&d not implemented yet! + canDrop(index, orientation) { + return false; + }, + drop(index, orientation) { + alert("drop:" + index + "," + orientation); + }, + // same as the global helper + getParentIndex, + // tree needs to know when to paint lines + hasNextSibling(index, after) { + if (!index) { + return false; + } + var level = itemArray[index].level; + while (++after < itemArray.length) { + switch (level - itemArray[after].level) { + case 1: + return false; + case 0: + return true; + } + } + return false; + }, + getLevel(index) { + return itemArray[index].level; + }, + getImageSrc(index, column) {}, + getProgressMode(index, column) {}, + getCellValue(index, column) {}, + getCellText(index, column) { + return itemArray[index].getCellText(column); + }, + setTree(tree) { + this.tree = tree; + }, + toggleOpenState(index) {}, + cycleHeader(col) {}, + selectionChanged() { + // Save current values and update buttons and deck + if (currentItem) { + currentItem.onBlur(); + } + var currentIndex = treeSelection.currentIndex; + currentItem = itemArray[currentIndex]; + gDialog.removeButton.disabled = !currentItem.canDestroy(); + gDialog.previousButton.disabled = currentIndex < 2; + gDialog.nextButton.disabled = !currentItem.canMoveDown(); + // For Advanced Edit + globalElement = currentItem.element; + currentItem.onFocus(); + }, + cycleCell(index, column) { + itemArray[index].cycleCell(index); + }, + isEditable(index, column) { + return false; + }, + }; + treeSelection.select(0); + currentItem = gDialog; + // onNameInput(); + + SetTextboxFocus(gDialog.selectName); + + SetWindowLocation(); +} + +// Called from Advanced Edit +function InitDialog() { + currentItem.onFocus(); +} + +// Called from Advanced Edit +function ValidateData() { + currentItem.onBlur(); + return true; +} + +function onAccept() { + // All values are valid - copy to actual element in doc or + // element created to insert + ValidateData(); + + var editor = GetCurrentEditor(); + + // Coalesce into one undo transaction + editor.beginTransaction(); + + try { + editor.cloneAttributes(selectElement, gDialog.element); + + if (insertNew) { + // 'true' means delete the selection before inserting + editor.insertElementAtSelection(selectElement, true); + } + + editor.setShouldTxnSetSelection(false); + + while (selectElement.lastChild) { + editor.deleteNode(selectElement.lastChild); + } + + var offset = 0; + for (var i = 1; i < itemArray.length; i++) { + if (itemArray[i].level > 1) { + selectElement.lastChild.appendChild(itemArray[i].element); + } else { + editor.insertNode(itemArray[i].element, selectElement, offset++); + } + } + + editor.setShouldTxnSetSelection(true); + } finally { + editor.endTransaction(); + } + + SaveWindowLocation(); +} + +// Button actions +function AddOption() { + currentItem.appendOption( + GetCurrentEditor().createElementWithDefaults("option"), + treeSelection.currentIndex + ); + SetTextboxFocus(gDialog.optionText); +} + +function AddOptGroup() { + var optgroupElement = GetCurrentEditor().createElementWithDefaults( + "optgroup" + ); + var index = itemArray.length; + // XXX need to repaint the lines, tree won't do this + theTree.invalidateRange(gDialog.lastChild(), index); + // append the wrapped object + itemArray.push(new optgroupObject(optgroupElement)); + theTree.rowCountChanged(index, 1); + selectTreeIndex(index, false); + SetTextboxFocus(gDialog.optgroupLabel); +} + +function RemoveElement() { + if (currentItem.canDestroy(true)) { + // Only removing empty option groups for now + var index = treeSelection.currentIndex; + var level = itemArray[index].level; + // Perform necessary cleanup and remove the wrapper + itemArray[index].destroy(); + itemArray.splice(index, 1); + --index; + // XXX need to repaint the lines, tree won't do this + if (level == 1) { + var last = gDialog.lastChild(); + if (index > last) { + theTree.invalidateRange(last, index); + } + } + selectTreeIndex(index, true); + theTree.rowCountChanged(++index, -1); + } +} + +// Event handler +function onTreeKeyUp(event) { + if (event.keyCode == event.DOM_VK_SPACE) { + currentItem.cycleCell(); + } +} + +function onNameInput() { + var disabled = !gDialog.selectName.value; + if (gDialog.accept.disabled != disabled) { + gDialog.accept.disabled = disabled; + } + gDialog.element.setAttribute("name", gDialog.selectName.value); + // repaint the tree + var primaryCol = theTree.columns.getPrimaryColumn(); + theTree.invalidateCell(treeSelection.currentIndex, primaryCol); +} + +function onLabelInput() { + currentItem.element.setAttribute("label", gDialog.optgroupLabel.value); + // repaint the tree + var primaryCol = theTree.columns.getPrimaryColumn(); + theTree.invalidateCell(treeSelection.currentIndex, primaryCol); +} + +function onTextInput() { + currentItem.element.text = gDialog.optionText.value; + // repaint the tree + if (hasValue) { + var primaryCol = theTree.columns.getPrimaryColumn(); + theTree.invalidateCell(treeSelection.currentIndex, primaryCol); + } else { + gDialog.optionValue.value = gDialog.optionText.value; + theTree.invalidateRow(treeSelection.currentIndex); + } +} + +function onValueInput() { + gDialog.optionHasValue.checked = hasValue = true; + oldValue = gDialog.optionValue.value; + currentItem.element.setAttribute("value", oldValue); + // repaint the tree + var column = theTree.columns.SelectValCol; + theTree.invalidateCell(treeSelection.currentIndex, column); +} + +function onHasValueClick() { + hasValue = gDialog.optionHasValue.checked; + if (hasValue) { + gDialog.optionValue.value = oldValue; + currentItem.element.setAttribute("value", oldValue); + } else { + oldValue = gDialog.optionValue.value; + gDialog.optionValue.value = gDialog.optionText.value; + currentItem.element.removeAttribute("value"); + } + // repaint the tree + var column = theTree.columns.SelectValCol; + theTree.invalidateCell(treeSelection.currentIndex, column); +} + +function onSelectMultipleClick() { + // Recalculate the unique selected option if we need it and have lost it + if ( + !gDialog.selectMultiple.checked && + selectedOptionCount == 1 && + !selectedOption + ) { + // eslint-disable-next-line curly + for ( + var i = 1; + !(selectedOption = itemArray[i].element).hasAttribute("selected"); + i++ + ); + } +} + +function selectTreeIndex(index, focus) { + treeSelection.select(index); + theTree.ensureRowIsVisible(index); + if (focus) { + gDialog.tree.focus(); + } +} diff --git a/comm/suite/editor/components/dialogs/content/EdSelectProps.xhtml b/comm/suite/editor/components/dialogs/content/EdSelectProps.xhtml new file mode 100644 index 0000000000..ff91b02e28 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdSelectProps.xhtml @@ -0,0 +1,143 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog [ +<!ENTITY % edSelectProperties SYSTEM "chrome://editor/locale/EditorSelectProperties.dtd"> +%edSelectProperties; +<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd"> +%edDialogOverlay; +]> + +<dialog title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload="Startup();" + buttons="accept,cancel"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EdSelectProps.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <!-- Setting rows="7" on tree isn't working, equalsize vbox sets tree height. --> + <vbox equalsize="always"> + <tree id="SelectTree" onselect="treeBoxObject.view.selectionChanged();" onkeyup="onTreeKeyUp(event);"> + <treecols id="SelectCols"> + <treecol id="SelectTextCol" flex="3" label="&TextHeader.label;" primary="true"/> + <splitter class="tree-splitter"/> + <treecol id="SelectValCol" flex="2" label="&ValueHeader.label;"/> + <treecol id="SelectSelCol" label="&SelectedHeader.label;" cycler="true"/> + </treecols> + + <treechildren id="SelectTreeChildren"/> + </tree> + + <hbox flex="1"> + <deck flex="1" id="SelectDeck" index="0"> + <groupbox flex="1"> + <hbox class="groupbox-title"> + <label class="header">&Select.label;</label> + </hbox> + <grid flex="1"><columns><column flex="1"/><column/></columns> + <rows> + <row align="center"> + <label control="SelectName" value="&SelectName.label;" accesskey="&SelectName.accesskey;"/> + <textbox id="SelectName" flex="1" oninput="onNameInput();"/> + </row> + <row align="center"> + <label control="SelectSize" value="&SelectSize.label;" accesskey="&SelectSize.accesskey;"/> + <hbox> + <textbox id="SelectSize" class="narrow" oninput="forceInteger(this.id);"/> + </hbox> + </row> + <row> + <spacer/> + <checkbox id="SelectMultiple" flex="1" label="&SelectMultiple.label;" accesskey="&SelectMultiple.accesskey;" oncommand="onSelectMultipleClick();"/> + </row> + <row> + <spacer/> + <checkbox id="SelectDisabled" flex="1" label="&SelectDisabled.label;" accesskey="&SelectDisabled.accesskey;"/> + </row> + <row align="center"> + <label control="SelectTabIndex" value="&SelectTabIndex.label;" accesskey="&SelectTabIndex.accesskey;"/> + <hbox> + <textbox id="SelectTabIndex" class="narrow" oninput="forceInteger(this.id);"/> + </hbox> + </row> + </rows> + </grid> + </groupbox> + + <groupbox flex="1"> + <hbox class="groupbox-title"> + <label class="header">&OptGroup.label;</label> + </hbox> + <grid flex="1"><columns><column flex="1"/><column/></columns> + <rows> + <row align="center"> + <label control="OptGroupLabel" value="&OptGroupLabel.label;" accesskey="&OptGroupLabel.accesskey;"/> + <textbox id="OptGroupLabel" oninput="onLabelInput();"/> + </row> + <row> + <spacer/> + <checkbox id="OptGroupDisabled" label="&OptGroupDisabled.label;" accesskey="&OptGroupDisabled.accesskey;"/> + </row> + </rows> + </grid> + </groupbox> + + <groupbox flex="1"> + <hbox class="groupbox-title"> + <label class="header">&Option.label;</label> + </hbox> + <grid flex="1"><columns><column flex="1"/><column/></columns> + <rows> + <row align="center"> + <label control="OptionText" value="&OptionText.label;" accesskey="&OptionText.accesskey;"/> + <textbox id="OptionText" oninput="onTextInput();"/> + </row> + <row align="center"> + <checkbox id="OptionHasValue" label="&OptionValue.label;" accesskey="&OptionValue.accesskey;" oncommand="onHasValueClick();"/> + <textbox id="OptionValue" oninput="onValueInput();"/> + </row> + <row> + <spacer/> + <checkbox id="OptionSelected" label="&OptionSelected.label;" accesskey="&OptionSelected.accesskey;" oncommand="currentItem.cycleCell();"/> + </row> + <row> + <spacer/> + <checkbox id="OptionDisabled" label="&OptionDisabled.label;" accesskey="&OptionDisabled.accesskey;"/> + </row> + </rows> + </grid> + </groupbox> + </deck> + + <vbox> + <button label="&AddOption.label;" accesskey="&AddOption.accesskey;" oncommand="AddOption();"/> + <button label="&AddOptGroup.label;" accesskey="&AddOptGroup.accesskey;" oncommand="AddOptGroup();"/> + <button id="RemoveButton" label="&RemoveElement.label;" accesskey="&RemoveElement.accesskey;" + oncommand="RemoveElement();" disabled="true"/> + <button id="PreviousButton" label="&MoveElementUp.label;" accesskey="&MoveElementUp.accesskey;" + oncommand="currentItem.moveUp();" disabled="true" type="row"/> + <button id="NextButton" label="&MoveElementDown.label;" accesskey="&MoveElementDown.accesskey;" + oncommand="currentItem.moveDown();" disabled="true" type="row"/> + <spacer flex="1"/> + <button id="AdvancedEditButton" + oncommand="onAdvancedEdit();" + label="&AdvancedEditButton.label;" + accesskey="&AdvancedEditButton.accessKey;" + tooltiptext="&AdvancedEditButton.tooltip;"/> + </vbox> + </hbox> + </vbox> + + <separator class="groove"/> + +</dialog> diff --git a/comm/suite/editor/components/dialogs/content/EdSnapToGrid.js b/comm/suite/editor/components/dialogs/content/EdSnapToGrid.js new file mode 100644 index 0000000000..ed546d3540 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdSnapToGrid.js @@ -0,0 +1,62 @@ +/* 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/. */ + +var gEditor; + +// dialog initialization code + +document.addEventListener("dialogaccept", onAccept); +document.addEventListener("dialogcancel", onCancel); + +function Startup() +{ + gEditor = GetCurrentEditor(); + if (!gEditor) + { + window.close(); + return; + } + + gEditor instanceof Ci.nsIHTMLAbsPosEditor; + + gDialog.enableSnapToGrid = document.getElementById("enableSnapToGrid"); + gDialog.sizeInput = document.getElementById("size"); + gDialog.sizeLabel = document.getElementById("sizeLabel"); + gDialog.unitLabel = document.getElementById("unitLabel"); + + // Initialize control values based on existing attributes + InitDialog() + + // SET FOCUS TO FIRST CONTROL + SetTextboxFocus(gDialog.sizeInput); + + // 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() +{ + gDialog.enableSnapToGrid.checked = gEditor.snapToGridEnabled; + toggleSnapToGrid(); + + gDialog.sizeInput.value = gEditor.gridSize; +} + +function onAccept() +{ + gEditor.snapToGridEnabled = gDialog.enableSnapToGrid.checked; + gEditor.gridSize = gDialog.sizeInput.value; +} + +function toggleSnapToGrid() +{ + SetElementEnabledById("size", gDialog.enableSnapToGrid.checked) + SetElementEnabledById("sizeLabel", gDialog.enableSnapToGrid.checked) + SetElementEnabledById("unitLabel", gDialog.enableSnapToGrid.checked) +} diff --git a/comm/suite/editor/components/dialogs/content/EdSnapToGrid.xhtml b/comm/suite/editor/components/dialogs/content/EdSnapToGrid.xhtml new file mode 100644 index 0000000000..9ae6643975 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdSnapToGrid.xhtml @@ -0,0 +1,47 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorSnapToGrid.dtd"> + +<dialog title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload="Startup()"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <!--- Element-specific methods --> + <script src="chrome://editor/content/EdSnapToGrid.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <checkbox id="enableSnapToGrid" + label="&enableSnapToGrid.label;" + accesskey="&enableSnapToGrid.accessKey;" + oncommand="toggleSnapToGrid();"/> + + <spacer class="spacer"/> + + <grid> + <columns><column/><column/><column /></columns> + <rows> + <row align="center"> + <label value="&sizeEditField.label;" + id="sizeLabel" + control="size" + accesskey="&sizeEditField.accessKey;"/> + <textbox class="narrow" id="size" oninput="forceInteger('size')"/> + <label id="unitLabel" + value="&pixelsLabel.value;" /> + </row> + </rows> + </grid> + + <spacer class="spacer"/> + <separator class="groove"/> +</dialog> diff --git a/comm/suite/editor/components/dialogs/content/EdSpellCheck.js b/comm/suite/editor/components/dialogs/content/EdSpellCheck.js new file mode 100644 index 0000000000..ddb23726cd --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdSpellCheck.js @@ -0,0 +1,495 @@ +/* -*- 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 ../../../../mail/base/content/utilityOverlay.js */ +/* import-globals-from ../../composer/content/editorUtilities.js */ +/* import-globals-from EdDialogCommon.js */ + +var { InlineSpellChecker } = ChromeUtils.import( + "resource://gre/modules/InlineSpellChecker.jsm" +); + +var gMisspelledWord; +var gSpellChecker = null; +var gAllowSelectWord = true; +var gPreviousReplaceWord = ""; +var gFirstTime = true; +var gLastSelectedLang = null; +var gDictCount = 0; + +document.addEventListener("dialogaccept", doDefault); +document.addEventListener("dialogcancel", CancelSpellCheck); + +function Startup() { + var editor = GetCurrentEditor(); + if (!editor) { + window.close(); + return; + } + + // Get the spellChecker shell + gSpellChecker = Cu.createSpellChecker(); + if (!gSpellChecker) { + dump("SpellChecker not found!!!\n"); + window.close(); + return; + } + + // Start the spell checker module. + try { + var skipBlockQuotes = window.arguments[1]; + var enableSelectionChecking = window.arguments[2]; + + gSpellChecker.setFilterType( + skipBlockQuotes + ? Ci.nsIEditorSpellCheck.FILTERTYPE_MAIL + : Ci.nsIEditorSpellCheck.FILTERTYPE_NORMAL + ); + gSpellChecker.InitSpellChecker( + editor, + enableSelectionChecking, + spellCheckStarted + ); + } catch (ex) { + dump("*** Exception error: InitSpellChecker\n"); + window.close(); + } +} + +function spellCheckStarted() { + gDialog.MisspelledWordLabel = document.getElementById("MisspelledWordLabel"); + gDialog.MisspelledWord = document.getElementById("MisspelledWord"); + gDialog.ReplaceButton = document.getElementById("Replace"); + gDialog.IgnoreButton = document.getElementById("Ignore"); + gDialog.StopButton = document.getElementById("Stop"); + gDialog.CloseButton = document.getElementById("Close"); + gDialog.ReplaceWordInput = document.getElementById("ReplaceWordInput"); + gDialog.SuggestedList = document.getElementById("SuggestedList"); + gDialog.LanguageMenulist = document.getElementById("LanguageMenulist"); + + // Fill in the language menulist and sync it up + // with the spellchecker's current language. + + var curLang; + + try { + curLang = gSpellChecker.GetCurrentDictionary(); + } catch (ex) { + curLang = ""; + } + + InitLanguageMenu(curLang); + + // Get the first misspelled word and setup all UI + NextWord(); + + // When startup param is true, setup different UI when spell checking + // just before sending mail message + if (window.arguments[0]) { + // If no misspelled words found, simply close dialog and send message + if (!gMisspelledWord) { + onClose(); + return; + } + + // Hide "Close" button and use "Send" instead + gDialog.CloseButton.hidden = true; + gDialog.CloseButton = document.getElementById("Send"); + gDialog.CloseButton.hidden = false; + } else { + // Normal spell checking - hide the "Stop" button + // (Note that this button is the "Cancel" button for + // Esc keybinding and related window close actions) + gDialog.StopButton.hidden = true; + } + + // Clear flag that determines message when + // no misspelled word is found + // (different message when used for the first time) + gFirstTime = false; + + window.sizeToContent(); +} + +function InitLanguageMenu(aCurLang) { + // Get the list of dictionaries from + // the spellchecker. + + var dictList; + try { + dictList = gSpellChecker.GetDictionaryList(); + } catch (ex) { + dump("Failed to get DictionaryList!\n"); + return; + } + + // If we're not just starting up and dictionary count + // hasn't changed then no need to update the menu. + if (gDictCount == dictList.length) { + return; + } + + // Store current dictionary count. + gDictCount = dictList.length; + + var inlineSpellChecker = new InlineSpellChecker(); + var sortedList = inlineSpellChecker.sortDictionaryList(dictList); + + // Remove any languages from the list. + var languageMenuPopup = gDialog.LanguageMenulist.menupopup; + while (languageMenuPopup.firstChild.localName != "menuseparator") { + languageMenuPopup.firstChild.remove(); + } + + var defaultItem = null; + + for (var i = 0; i < gDictCount; i++) { + let item = document.createXULElement("menuitem"); + item.setAttribute("label", sortedList[i].displayName); + item.setAttribute("value", sortedList[i].localeCode); + let beforeItem = gDialog.LanguageMenulist.getItemAtIndex(i); + languageMenuPopup.insertBefore(item, beforeItem); + + if (aCurLang && sortedList[i].localeCode == aCurLang) { + defaultItem = item; + } + } + + // Now make sure the correct item in the menu list is selected. + if (defaultItem) { + gDialog.LanguageMenulist.selectedItem = defaultItem; + gLastSelectedLang = defaultItem; + } +} + +function DoEnabling() { + if (!gMisspelledWord) { + // No more misspelled words + gDialog.MisspelledWord.setAttribute( + "value", + GetString(gFirstTime ? "NoMisspelledWord" : "CheckSpellingDone") + ); + + gDialog.ReplaceButton.removeAttribute("default"); + gDialog.IgnoreButton.removeAttribute("default"); + + gDialog.CloseButton.setAttribute("default", "true"); + // Shouldn't have to do this if "default" is true? + gDialog.CloseButton.focus(); + + SetElementEnabledById("MisspelledWordLabel", false); + SetElementEnabledById("ReplaceWordLabel", false); + SetElementEnabledById("ReplaceWordInput", false); + SetElementEnabledById("CheckWord", false); + SetElementEnabledById("SuggestedListLabel", false); + SetElementEnabledById("SuggestedList", false); + SetElementEnabledById("Ignore", false); + SetElementEnabledById("IgnoreAll", false); + SetElementEnabledById("Replace", false); + SetElementEnabledById("ReplaceAll", false); + SetElementEnabledById("AddToDictionary", false); + } else { + SetElementEnabledById("MisspelledWordLabel", true); + SetElementEnabledById("ReplaceWordLabel", true); + SetElementEnabledById("ReplaceWordInput", true); + SetElementEnabledById("CheckWord", true); + SetElementEnabledById("SuggestedListLabel", true); + SetElementEnabledById("SuggestedList", true); + SetElementEnabledById("Ignore", true); + SetElementEnabledById("IgnoreAll", true); + SetElementEnabledById("AddToDictionary", true); + + gDialog.CloseButton.removeAttribute("default"); + SetReplaceEnable(); + } +} + +function NextWord() { + gMisspelledWord = gSpellChecker.GetNextMisspelledWord(); + SetWidgetsForMisspelledWord(); +} + +function SetWidgetsForMisspelledWord() { + gDialog.MisspelledWord.setAttribute("value", gMisspelledWord); + + // Initial replace word is misspelled word + gDialog.ReplaceWordInput.value = gMisspelledWord; + gPreviousReplaceWord = gMisspelledWord; + + // This sets gDialog.ReplaceWordInput to first suggested word in list + FillSuggestedList(gMisspelledWord); + + DoEnabling(); + + if (gMisspelledWord) { + SetTextboxFocus(gDialog.ReplaceWordInput); + } +} + +function CheckWord() { + var word = gDialog.ReplaceWordInput.value; + if (word) { + if (gSpellChecker.CheckCurrentWord(word)) { + FillSuggestedList(word); + SetReplaceEnable(); + } else { + ClearListbox(gDialog.SuggestedList); + var item = gDialog.SuggestedList.appendItem( + GetString("CorrectSpelling"), + "" + ); + if (item) { + item.setAttribute("disabled", "true"); + } + // Suppress being able to select the message text + gAllowSelectWord = false; + } + } +} + +function SelectSuggestedWord() { + if (gAllowSelectWord) { + if (gDialog.SuggestedList.selectedItem) { + var selValue = gDialog.SuggestedList.selectedItem.label; + gDialog.ReplaceWordInput.value = selValue; + gPreviousReplaceWord = selValue; + } else { + gDialog.ReplaceWordInput.value = gPreviousReplaceWord; + } + SetReplaceEnable(); + } +} + +function ChangeReplaceWord() { + // Calling this triggers SelectSuggestedWord(), + // so temporarily suppress the effect of that + var saveAllow = gAllowSelectWord; + gAllowSelectWord = false; + + // Select matching word in list + var newSelectedItem; + var replaceWord = TrimString(gDialog.ReplaceWordInput.value); + if (replaceWord) { + for (var i = 0; i < gDialog.SuggestedList.getRowCount(); i++) { + var item = gDialog.SuggestedList.getItemAtIndex(i); + if (item.label == replaceWord) { + newSelectedItem = item; + break; + } + } + } + gDialog.SuggestedList.selectedItem = newSelectedItem; + + gAllowSelectWord = saveAllow; + + // Remember the new word + gPreviousReplaceWord = gDialog.ReplaceWordInput.value; + + SetReplaceEnable(); +} + +function Ignore() { + NextWord(); +} + +function IgnoreAll() { + if (gMisspelledWord) { + gSpellChecker.IgnoreWordAllOccurrences(gMisspelledWord); + } + NextWord(); +} + +function Replace(newWord) { + if (!newWord) { + return; + } + + if (gMisspelledWord && gMisspelledWord != newWord) { + var editor = GetCurrentEditor(); + editor.beginTransaction(); + try { + gSpellChecker.ReplaceWord(gMisspelledWord, newWord, false); + } catch (e) {} + editor.endTransaction(); + } + NextWord(); +} + +function ReplaceAll() { + var newWord = gDialog.ReplaceWordInput.value; + if (gMisspelledWord && gMisspelledWord != newWord) { + var editor = GetCurrentEditor(); + editor.beginTransaction(); + try { + gSpellChecker.ReplaceWord(gMisspelledWord, newWord, true); + } catch (e) {} + editor.endTransaction(); + } + NextWord(); +} + +function AddToDictionary() { + if (gMisspelledWord) { + gSpellChecker.AddWordToDictionary(gMisspelledWord); + } + NextWord(); +} + +function EditDictionary() { + window.openDialog( + "chrome://editor/content/EdDictionary.xhtml", + "_blank", + "chrome,close,titlebar,modal", + "", + gMisspelledWord + ); +} + +function SelectLanguage() { + var item = gDialog.LanguageMenulist.selectedItem; + if (item.value != "more-cmd") { + gSpellChecker.SetCurrentDictionary(item.value); + // For compose windows we need to set the "lang" attribute so the + // core editor uses the correct dictionary for the inline spell check. + if (window.arguments[1]) { + if ("ComposeChangeLanguage" in window.opener) { + // We came here from a compose window. + window.opener.ComposeChangeLanguage(item.value); + } else { + window.opener.document.documentElement.setAttribute("lang", item.value); + } + } + gLastSelectedLang = item; + } else { + openDictionaryList(); + + if (gLastSelectedLang) { + gDialog.LanguageMenulist.selectedItem = gLastSelectedLang; + } + } +} + +function Recheck() { + var recheckLanguage; + + function finishRecheck() { + gSpellChecker.SetCurrentDictionary(recheckLanguage); + gMisspelledWord = gSpellChecker.GetNextMisspelledWord(); + SetWidgetsForMisspelledWord(); + } + + // TODO: Should we bother to add a "Recheck" method to interface? + try { + recheckLanguage = gSpellChecker.GetCurrentDictionary(); + gSpellChecker.UninitSpellChecker(); + // Clear the ignore all list. + Cc["@mozilla.org/spellchecker/personaldictionary;1"] + .getService(Ci.mozIPersonalDictionary) + .endSession(); + gSpellChecker.InitSpellChecker(GetCurrentEditor(), false, finishRecheck); + } catch (ex) { + Cu.reportError(ex); + } +} + +function FillSuggestedList(misspelledWord) { + var list = gDialog.SuggestedList; + + // Clear the current contents of the list + gAllowSelectWord = false; + ClearListbox(list); + var item; + + if (misspelledWord.length > 0) { + // Get suggested words until an empty string is returned + var count = 0; + do { + var word = gSpellChecker.GetSuggestedWord(); + if (word.length > 0) { + list.appendItem(word, ""); + count++; + } + } while (word.length > 0); + + if (count == 0) { + // No suggestions - show a message but don't let user select it + item = list.appendItem(GetString("NoSuggestedWords")); + if (item) { + item.setAttribute("disabled", "true"); + } + gAllowSelectWord = false; + } else { + gAllowSelectWord = true; + // Initialize with first suggested list by selecting it + gDialog.SuggestedList.selectedIndex = 0; + } + } else { + item = list.appendItem("", ""); + if (item) { + item.setAttribute("disabled", "true"); + } + } +} + +function SetReplaceEnable() { + // Enable "Change..." buttons only if new word is different than misspelled + var newWord = gDialog.ReplaceWordInput.value; + var enable = newWord.length > 0 && newWord != gMisspelledWord; + SetElementEnabledById("Replace", enable); + SetElementEnabledById("ReplaceAll", enable); + if (enable) { + gDialog.ReplaceButton.setAttribute("default", "true"); + gDialog.IgnoreButton.removeAttribute("default"); + } else { + gDialog.IgnoreButton.setAttribute("default", "true"); + gDialog.ReplaceButton.removeAttribute("default"); + } +} + +function doDefault(event) { + if (gDialog.ReplaceButton.getAttribute("default") == "true") { + Replace(gDialog.ReplaceWordInput.value); + } else if (gDialog.IgnoreButton.getAttribute("default") == "true") { + Ignore(); + } else if (gDialog.CloseButton.getAttribute("default") == "true") { + onClose(); + } + + event.preventDefault(); +} + +function ExitSpellChecker() { + if (gSpellChecker) { + try { + gSpellChecker.UninitSpellChecker(); + // now check the document over again with the new dictionary + // if we have an inline spellchecker + if ( + "InlineSpellCheckerUI" in window.opener && + window.opener.InlineSpellCheckerUI.enabled + ) { + window.opener.InlineSpellCheckerUI.mInlineSpellChecker.spellCheckRange( + null + ); + } + } finally { + gSpellChecker = null; + } + } +} + +function CancelSpellCheck() { + ExitSpellChecker(); + + // Signal to calling window that we canceled + window.opener.cancelSendMessage = true; +} + +function onClose() { + ExitSpellChecker(); + + window.opener.cancelSendMessage = false; + window.close(); +} diff --git a/comm/suite/editor/components/dialogs/content/EdSpellCheck.xhtml b/comm/suite/editor/components/dialogs/content/EdSpellCheck.xhtml new file mode 100644 index 0000000000..4cb9f49198 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdSpellCheck.xhtml @@ -0,0 +1,113 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> +<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorSpellCheck.dtd"> + +<!-- dialog containing a control requiring initial setup --> +<dialog id="spellCheckDlg" buttons="cancel" title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + persist="screenX screenY" + onload="Startup()"> + + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://communicator/content/utilityOverlay.js"/> + <script src="chrome://editor/content/EdSpellCheck.js"/> + <script src="chrome://global/content/contentAreaUtils.js"/> + + <stringbundle id="languageBundle" src="chrome://global/locale/languageNames.properties"/> + <stringbundle id="regionBundle" src="chrome://global/locale/regionNames.properties"/> + + <grid> + <columns> + <column class="spell-check"/> + <column class="spell-check" flex="1"/> + <column class="spell-check"/> + </columns> + <rows> + <row align="center"> + <label id="MisspelledWordLabel" value="&misspelledWord.label;"/> + <label class="bold" id="MisspelledWord" crop="end"/> + <button class="spell-check" label="&recheckButton2.label;" oncommand="Recheck();" + accesskey="&recheckButton2.accessKey;"/> + </row> + <row align="center"> + <label id="ReplaceWordLabel" value="&wordEditField.label;" + control="ReplaceWordInput" + accesskey="&wordEditField.accessKey;"/> + <textbox id="ReplaceWordInput" oninput="ChangeReplaceWord()" flex="1"/> + <button id="CheckWord" oncommand="CheckWord()" label="&checkwordButton.label;" + accesskey="&checkwordButton.accessKey;"/> + </row> + </rows> + </grid> + <label id="SuggestedListLabel" value="&suggestions.label;" + control="SuggestedList" + accesskey="&suggestions.accessKey;"/> + <grid flex="1"> + <columns><column flex="1"/><column/></columns> + <rows> + <row flex="1"> + <!-- BUG! setting class="MinWidth20em" on tree doesn't work (width=0) --> + <richlistbox id="SuggestedList" + class="theme-listbox" + onselect="SelectSuggestedWord()" + ondblclick="if (gAllowSelectWord) { Replace(event.target.value); }"/> + <vbox> + <grid> + <columns><column class="spell-check" flex="1"/><column class="spell-check" flex="1"/></columns> + <rows> + <row> + <button id="Replace" label="&replaceButton.label;" + oncommand="Replace(gDialog.ReplaceWordInput.value);" + accesskey="&replaceButton.accessKey;"/> + <button id="Ignore" oncommand="Ignore();" label="&ignoreButton.label;" + accesskey="&ignoreButton.accessKey;"/> + </row> + <row> + <button id="ReplaceAll" oncommand="ReplaceAll();" label="&replaceAllButton.label;" + accesskey="&replaceAllButton.accessKey;"/> + <button id="IgnoreAll" oncommand="IgnoreAll();" label="&ignoreAllButton.label;" + accesskey="&ignoreAllButton.accessKey;"/> + </row> + </rows> + </grid> + <separator/> + <label value="&userDictionary.label;"/> + <hbox align="start"> + <button class="spell-check" id="AddToDictionary" oncommand="AddToDictionary()" label="&addToUserDictionaryButton.label;" + accesskey="&addToUserDictionaryButton.accessKey;"/> + <button class="spell-check" id="EditDictionary" oncommand="EditDictionary()" label="&editUserDictionaryButton.label;" + accesskey="&editUserDictionaryButton.accessKey;"/> + </hbox> + </vbox> + </row> + <label value ="&languagePopup.label;" + control="LanguageMenulist" + accesskey="&languagePopup.accessKey;"/> + <row> + <menulist id="LanguageMenulist" oncommand="SelectLanguage()"> + <menupopup onpopupshowing="InitLanguageMenu(gDialog.LanguageMenulist.selectedItem.value);"> + <!-- dynamic content populated by JS --> + <menuseparator/> + <menuitem value="more-cmd" label="&moreDictionaries.label;"/> + </menupopup> + </menulist> + <hbox flex="1"> + <button class="spell-check" dlgtype="cancel" id="Stop" label="&stopButton.label;" oncommand="CancelSpellCheck();" + accesskey="&stopButton.accessKey;"/> + <spacer flex="1"/> + <button class="spell-check" id="Close" label="&closeButton.label;" oncommand="onClose();" + accesskey="&closeButton.accessKey;"/> + <button class="spell-check" id="Send" label="&sendButton.label;" oncommand="onClose();" + accesskey="&sendButton.accessKey;" hidden="true"/> + </hbox> + </row> + </rows> + </grid> +</dialog> diff --git a/comm/suite/editor/components/dialogs/content/EdTableProps.js b/comm/suite/editor/components/dialogs/content/EdTableProps.js new file mode 100644 index 0000000000..e78a89bc41 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdTableProps.js @@ -0,0 +1,1439 @@ +/* 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 gTableElement; +var gCellElement; +var gTableCaptionElement; +var globalCellElement; +var globalTableElement; +var gValidateTab; +const defHAlign = "left"; +const centerStr = "center"; // Index=1 +const rightStr = "right"; // 2 +const justifyStr = "justify"; // 3 +const charStr = "char"; // 4 +const defVAlign = "middle"; +const topStr = "top"; +const bottomStr = "bottom"; +const bgcolor = "bgcolor"; +var gTableColor; +var gCellColor; + +const cssBackgroundColorStr = "background-color"; + +var gRowCount = 1; +var gColCount = 1; +var gLastRowIndex; +var gLastColIndex; +var gNewRowCount; +var gNewColCount; +var gCurRowIndex; +var gCurColIndex; +var gCurColSpan; +var gSelectedCellsType = 1; +const SELECT_CELL = 1; +const SELECT_ROW = 2; +const SELECT_COLUMN = 3; +const RESET_SELECTION = 0; +var gCellData = { + value: null, + startRowIndex: 0, + startColIndex: 0, + rowSpan: 0, + colSpan: 0, + actualRowSpan: 0, + actualColSpan: 0, + isSelected: false, +}; +var gAdvancedEditUsed; +var gAlignWasChar = false; + +/* +From C++: + 0 TABLESELECTION_TABLE + 1 TABLESELECTION_CELL There are 1 or more cells selected + but complete rows or columns are not selected + 2 TABLESELECTION_ROW All cells are in 1 or more rows + and in each row, all cells selected + Note: This is the value if all rows (thus all cells) are selected + 3 TABLESELECTION_COLUMN All cells are in 1 or more columns +*/ + +var gSelectedCellCount = 0; +var gApplyUsed = false; +var gSelection; +var gCellDataChanged = false; +var gCanDelete = false; +var gUseCSS = true; +var gActiveEditor; + +// dialog initialization code + +document.addEventListener("dialogaccept", onAccept); +document.addEventListener("dialogextra1", Apply); +document.addEventListener("dialogcancel", onCancel); + +function Startup() { + gActiveEditor = GetCurrentTableEditor(); + if (!gActiveEditor) { + window.close(); + return; + } + + try { + gSelection = gActiveEditor.selection; + } catch (e) {} + if (!gSelection) { + return; + } + + // Get dialog widgets - Table Panel + gDialog.TableRowsInput = document.getElementById("TableRowsInput"); + gDialog.TableColumnsInput = document.getElementById("TableColumnsInput"); + gDialog.TableWidthInput = document.getElementById("TableWidthInput"); + gDialog.TableWidthUnits = document.getElementById("TableWidthUnits"); + gDialog.TableHeightInput = document.getElementById("TableHeightInput"); + gDialog.TableHeightUnits = document.getElementById("TableHeightUnits"); + try { + if ( + !Services.prefs.getBoolPref("editor.use_css") || + gActiveEditor.flags & 1 + ) { + gUseCSS = false; + var tableHeightLabel = document.getElementById("TableHeightLabel"); + tableHeightLabel.remove(); + gDialog.TableHeightInput.remove(); + gDialog.TableHeightUnits.remove(); + } + } catch (e) {} + gDialog.BorderWidthInput = document.getElementById("BorderWidthInput"); + gDialog.SpacingInput = document.getElementById("SpacingInput"); + gDialog.PaddingInput = document.getElementById("PaddingInput"); + gDialog.TableAlignList = document.getElementById("TableAlignList"); + gDialog.TableCaptionList = document.getElementById("TableCaptionList"); + gDialog.TableInheritColor = document.getElementById("TableInheritColor"); + gDialog.TabBox = document.getElementById("TabBox"); + + // Cell Panel + gDialog.SelectionList = document.getElementById("SelectionList"); + gDialog.PreviousButton = document.getElementById("PreviousButton"); + gDialog.NextButton = document.getElementById("NextButton"); + // Currently, we always apply changes and load new attributes when changing selection + // (Let's keep this for possible future use) + // gDialog.ApplyBeforeMove = document.getElementById("ApplyBeforeMove"); + // gDialog.KeepCurrentData = document.getElementById("KeepCurrentData"); + + gDialog.CellHeightInput = document.getElementById("CellHeightInput"); + gDialog.CellHeightUnits = document.getElementById("CellHeightUnits"); + gDialog.CellWidthInput = document.getElementById("CellWidthInput"); + gDialog.CellWidthUnits = document.getElementById("CellWidthUnits"); + gDialog.CellHAlignList = document.getElementById("CellHAlignList"); + gDialog.CellVAlignList = document.getElementById("CellVAlignList"); + gDialog.CellInheritColor = document.getElementById("CellInheritColor"); + gDialog.CellStyleList = document.getElementById("CellStyleList"); + gDialog.TextWrapList = document.getElementById("TextWrapList"); + + // In cell panel, user must tell us which attributes to apply via checkboxes, + // else we would apply values from one cell to ALL in selection + // and that's probably not what they expect! + gDialog.CellHeightCheckbox = document.getElementById("CellHeightCheckbox"); + gDialog.CellWidthCheckbox = document.getElementById("CellWidthCheckbox"); + gDialog.CellHAlignCheckbox = document.getElementById("CellHAlignCheckbox"); + gDialog.CellVAlignCheckbox = document.getElementById("CellVAlignCheckbox"); + gDialog.CellStyleCheckbox = document.getElementById("CellStyleCheckbox"); + gDialog.TextWrapCheckbox = document.getElementById("TextWrapCheckbox"); + gDialog.CellColorCheckbox = document.getElementById("CellColorCheckbox"); + gDialog.TableTab = document.getElementById("TableTab"); + gDialog.CellTab = document.getElementById("CellTab"); + gDialog.AdvancedEditCell = document.getElementById("AdvancedEditButton2"); + // Save "normal" tooltip message for Advanced Edit button + gDialog.AdvancedEditCellToolTipText = gDialog.AdvancedEditCell.getAttribute( + "tooltiptext" + ); + + try { + gTableElement = gActiveEditor.getElementOrParentByTagName("table", null); + } catch (e) {} + if (!gTableElement) { + dump("Failed to get table element!\n"); + window.close(); + return; + } + globalTableElement = gTableElement.cloneNode(false); + + var tagNameObj = { value: "" }; + var countObj = { value: 0 }; + var tableOrCellElement; + try { + tableOrCellElement = gActiveEditor.getSelectedOrParentTableElement( + tagNameObj, + countObj + ); + } catch (e) {} + + if (tagNameObj.value == "td") { + // We are in a cell + gSelectedCellCount = countObj.value; + gCellElement = tableOrCellElement; + globalCellElement = gCellElement.cloneNode(false); + + // Tells us whether cell, row, or column is selected + try { + gSelectedCellsType = gActiveEditor.getSelectedCellsType(gTableElement); + } catch (e) {} + + // Ignore types except Cell, Row, and Column + if ( + gSelectedCellsType < SELECT_CELL || + gSelectedCellsType > SELECT_COLUMN + ) { + gSelectedCellsType = SELECT_CELL; + } + + // Be sure at least 1 cell is selected. + // (If the count is 0, then we were inside the cell.) + if (gSelectedCellCount == 0) { + DoCellSelection(); + } + + // Get location in the cell map + var rowIndexObj = { value: 0 }; + var colIndexObj = { value: 0 }; + try { + gActiveEditor.getCellIndexes(gCellElement, rowIndexObj, colIndexObj); + } catch (e) {} + gCurRowIndex = rowIndexObj.value; + gCurColIndex = colIndexObj.value; + + // We save the current colspan to quickly + // move selection from from cell to cell + if (GetCellData(gCurRowIndex, gCurColIndex)) { + gCurColSpan = gCellData.colSpan; + } + + // Starting TabPanel name is passed in + if (window.arguments[1] == "CellPanel") { + gDialog.TabBox.selectedTab = gDialog.CellTab; + } + } + + if (gDialog.TabBox.selectedTab == gDialog.TableTab) { + // We may call this with table selected, but no cell, + // so disable the Cell Properties tab + if (!gCellElement) { + // XXX: Disabling of tabs is currently broken, so for + // now we'll just remove the tab completely. + // gDialog.CellTab.disabled = true; + gDialog.CellTab.remove(); + } + } + + // Note: we must use gTableElement, not globalTableElement for these, + // thus we should not put this in InitDialog. + // Instead, monitor desired counts with separate globals + var rowCountObj = { value: 0 }; + var colCountObj = { value: 0 }; + try { + gActiveEditor.getTableSize(gTableElement, rowCountObj, colCountObj); + } catch (e) {} + + gRowCount = rowCountObj.value; + gLastRowIndex = gRowCount - 1; + gColCount = colCountObj.value; + gLastColIndex = gColCount - 1; + + // Set appropriate icons and enable state for the Previous/Next buttons + SetSelectionButtons(); + + // If only one cell in table, disable change-selection widgets + if (gRowCount == 1 && gColCount == 1) { + gDialog.SelectionList.setAttribute("disabled", "true"); + } + + // User can change these via textboxes + gNewRowCount = gRowCount; + gNewColCount = gColCount; + + // This flag is used to control whether set check state + // on "set attribute" checkboxes + // (Advanced Edit dialog use calls InitDialog when done) + gAdvancedEditUsed = false; + InitDialog(); + gAdvancedEditUsed = true; + + // If first initializing, we really aren't changing anything + gCellDataChanged = false; + + SetWindowLocation(); +} + +function InitDialog() { + // Get Table attributes + gDialog.TableRowsInput.value = gRowCount; + gDialog.TableColumnsInput.value = gColCount; + gDialog.TableWidthInput.value = InitPixelOrPercentMenulist( + globalTableElement, + gTableElement, + "width", + "TableWidthUnits", + gPercent + ); + if (gUseCSS) { + gDialog.TableHeightInput.value = InitPixelOrPercentMenulist( + globalTableElement, + gTableElement, + "height", + "TableHeightUnits", + gPercent + ); + } + gDialog.BorderWidthInput.value = globalTableElement.border; + gDialog.SpacingInput.value = globalTableElement.cellSpacing; + gDialog.PaddingInput.value = globalTableElement.cellPadding; + + var marginLeft = GetHTMLOrCSSStyleValue( + globalTableElement, + "align", + "margin-left" + ); + var marginRight = GetHTMLOrCSSStyleValue( + globalTableElement, + "align", + "margin-right" + ); + var halign = marginLeft.toLowerCase() + " " + marginRight.toLowerCase(); + if (halign == "center center" || halign == "auto auto") { + gDialog.TableAlignList.value = "center"; + } else if (halign == "right right" || halign == "auto 0px") { + gDialog.TableAlignList.value = "right"; + } else { + // Default is left. + gDialog.TableAlignList.value = "left"; + } + + // Be sure to get caption from table in doc, not the copied "globalTableElement" + gTableCaptionElement = gTableElement.caption; + if (gTableCaptionElement) { + var align = GetHTMLOrCSSStyleValue( + gTableCaptionElement, + "align", + "caption-side" + ); + if (align != "bottom" && align != "left" && align != "right") { + align = "top"; + } + gDialog.TableCaptionList.value = align; + } + + gTableColor = GetHTMLOrCSSStyleValue( + globalTableElement, + bgcolor, + cssBackgroundColorStr + ); + gTableColor = ConvertRGBColorIntoHEXColor(gTableColor); + SetColor("tableBackgroundCW", gTableColor); + + InitCellPanel(); +} + +function InitCellPanel() { + // Get cell attributes + if (globalCellElement) { + // This assumes order of items is Cell, Row, Column + gDialog.SelectionList.value = gSelectedCellsType; + + var previousValue = gDialog.CellHeightInput.value; + gDialog.CellHeightInput.value = InitPixelOrPercentMenulist( + globalCellElement, + gCellElement, + "height", + "CellHeightUnits", + gPixel + ); + gDialog.CellHeightCheckbox.checked = + gAdvancedEditUsed && previousValue != gDialog.CellHeightInput.value; + + previousValue = gDialog.CellWidthInput.value; + gDialog.CellWidthInput.value = InitPixelOrPercentMenulist( + globalCellElement, + gCellElement, + "width", + "CellWidthUnits", + gPixel + ); + gDialog.CellWidthCheckbox.checked = + gAdvancedEditUsed && previousValue != gDialog.CellWidthInput.value; + + var previousIndex = gDialog.CellVAlignList.selectedIndex; + var valign = GetHTMLOrCSSStyleValue( + globalCellElement, + "valign", + "vertical-align" + ).toLowerCase(); + if (valign == topStr || valign == bottomStr) { + gDialog.CellVAlignList.value = valign; + } else { + // Default is middle. + gDialog.CellVAlignList.value = defVAlign; + } + + gDialog.CellVAlignCheckbox.checked = + gAdvancedEditUsed && + previousIndex != gDialog.CellVAlignList.selectedIndex; + + previousIndex = gDialog.CellHAlignList.selectedIndex; + + gAlignWasChar = false; + + var halign = GetHTMLOrCSSStyleValue( + globalCellElement, + "align", + "text-align" + ).toLowerCase(); + switch (halign) { + case centerStr: + case rightStr: + case justifyStr: + gDialog.CellHAlignList.value = halign; + break; + case charStr: + // We don't support UI for this because layout doesn't work: bug 2212. + // Remember that's what they had so we don't change it + // unless they change the alignment by using the menulist + gAlignWasChar = true; + // Fall through to use show default alignment in menu + default: + // Default depends on cell type (TH is "center", TD is "left") + gDialog.CellHAlignList.value = + globalCellElement.nodeName.toLowerCase() == "th" ? "center" : "left"; + break; + } + + gDialog.CellHAlignCheckbox.checked = + gAdvancedEditUsed && + previousIndex != gDialog.CellHAlignList.selectedIndex; + + previousIndex = gDialog.CellStyleList.selectedIndex; + gDialog.CellStyleList.value = globalCellElement.nodeName.toLowerCase(); + gDialog.CellStyleCheckbox.checked = + gAdvancedEditUsed && previousIndex != gDialog.CellStyleList.selectedIndex; + + previousIndex = gDialog.TextWrapList.selectedIndex; + if ( + GetHTMLOrCSSStyleValue(globalCellElement, "nowrap", "white-space") == + "nowrap" + ) { + gDialog.TextWrapList.value = "nowrap"; + } else { + gDialog.TextWrapList.value = "wrap"; + } + gDialog.TextWrapCheckbox.checked = + gAdvancedEditUsed && previousIndex != gDialog.TextWrapList.selectedIndex; + + previousValue = gCellColor; + gCellColor = GetHTMLOrCSSStyleValue( + globalCellElement, + bgcolor, + cssBackgroundColorStr + ); + gCellColor = ConvertRGBColorIntoHEXColor(gCellColor); + SetColor("cellBackgroundCW", gCellColor); + gDialog.CellColorCheckbox.checked = + gAdvancedEditUsed && previousValue != gCellColor; + + // We want to set this true in case changes came + // from Advanced Edit dialog session (must assume something changed) + gCellDataChanged = true; + } +} + +function GetCellData(rowIndex, colIndex) { + // Get actual rowspan and colspan + var startRowIndexObj = { value: 0 }; + var startColIndexObj = { value: 0 }; + var rowSpanObj = { value: 0 }; + var colSpanObj = { value: 0 }; + var actualRowSpanObj = { value: 0 }; + var actualColSpanObj = { value: 0 }; + var isSelectedObj = { value: false }; + + try { + gActiveEditor.getCellDataAt( + gTableElement, + rowIndex, + colIndex, + gCellData, + startRowIndexObj, + startColIndexObj, + rowSpanObj, + colSpanObj, + actualRowSpanObj, + actualColSpanObj, + isSelectedObj + ); + // We didn't find a cell + if (!gCellData.value) { + return false; + } + } catch (ex) { + return false; + } + + gCellData.startRowIndex = startRowIndexObj.value; + gCellData.startColIndex = startColIndexObj.value; + gCellData.rowSpan = rowSpanObj.value; + gCellData.colSpan = colSpanObj.value; + gCellData.actualRowSpan = actualRowSpanObj.value; + gCellData.actualColSpan = actualColSpanObj.value; + gCellData.isSelected = isSelectedObj.value; + return true; +} + +function SelectCellHAlign() { + SetCheckbox("CellHAlignCheckbox"); + // Once user changes the alignment, + // we lose their original "CharAt" alignment" + gAlignWasChar = false; +} + +function GetColorAndUpdate(ColorWellID) { + var colorWell = document.getElementById(ColorWellID); + if (!colorWell) { + return; + } + + var colorObj = { + Type: "", + TableColor: 0, + CellColor: 0, + NoDefault: false, + Cancel: false, + BackgroundColor: 0, + }; + + switch (ColorWellID) { + case "tableBackgroundCW": + colorObj.Type = "Table"; + colorObj.TableColor = gTableColor; + break; + case "cellBackgroundCW": + colorObj.Type = "Cell"; + colorObj.CellColor = gCellColor; + break; + } + window.openDialog( + "chrome://editor/content/EdColorPicker.xhtml", + "_blank", + "chrome,close,titlebar,modal", + "", + colorObj + ); + + // User canceled the dialog + if (colorObj.Cancel) { + return; + } + + switch (ColorWellID) { + case "tableBackgroundCW": + gTableColor = colorObj.BackgroundColor; + SetColor(ColorWellID, gTableColor); + break; + case "cellBackgroundCW": + gCellColor = colorObj.BackgroundColor; + SetColor(ColorWellID, gCellColor); + SetCheckbox("CellColorCheckbox"); + break; + } +} + +function SetColor(ColorWellID, color) { + // Save the color + if (ColorWellID == "cellBackgroundCW") { + if (color) { + try { + gActiveEditor.setAttributeOrEquivalent( + globalCellElement, + bgcolor, + color, + true + ); + } catch (e) {} + gDialog.CellInheritColor.collapsed = true; + } else { + try { + gActiveEditor.removeAttributeOrEquivalent( + globalCellElement, + bgcolor, + true + ); + } catch (e) {} + // Reveal addition message explaining "default" color + gDialog.CellInheritColor.collapsed = false; + } + } else { + if (color) { + try { + gActiveEditor.setAttributeOrEquivalent( + globalTableElement, + bgcolor, + color, + true + ); + } catch (e) {} + gDialog.TableInheritColor.collapsed = true; + } else { + try { + gActiveEditor.removeAttributeOrEquivalent( + globalTableElement, + bgcolor, + true + ); + } catch (e) {} + gDialog.TableInheritColor.collapsed = false; + } + SetCheckbox("CellColorCheckbox"); + } + + setColorWell(ColorWellID, color); +} + +function ChangeSelectionToFirstCell() { + if (!GetCellData(0, 0)) { + dump("Can't find first cell in table!\n"); + return; + } + gCellElement = gCellData.value; + globalCellElement = gCellElement; + + gCurRowIndex = 0; + gCurColIndex = 0; + ChangeSelection(RESET_SELECTION); +} + +function ChangeSelection(newType) { + newType = Number(newType); + + if (gSelectedCellsType == newType) { + return; + } + + if (newType == RESET_SELECTION) { + // Restore selection to existing focus cell + gSelection.collapse(gCellElement, 0); + } else { + gSelectedCellsType = newType; + } + + // Keep the same focus gCellElement, just change the type + DoCellSelection(); + SetSelectionButtons(); + + // Note: globalCellElement should still be a clone of gCellElement +} + +function MoveSelection(forward) { + var newRowIndex = gCurRowIndex; + var newColIndex = gCurColIndex; + var inRow = false; + + if (gSelectedCellsType == SELECT_ROW) { + newRowIndex += forward ? 1 : -1; + + // Wrap around if before first or after last row + if (newRowIndex < 0) { + newRowIndex = gLastRowIndex; + } else if (newRowIndex > gLastRowIndex) { + newRowIndex = 0; + } + inRow = true; + + // Use first cell in row for focus cell + newColIndex = 0; + } else { + // Cell or column: + if (!forward) { + newColIndex--; + } + + if (gSelectedCellsType == SELECT_CELL) { + // Skip to next cell + if (forward) { + newColIndex += gCurColSpan; + } + } else { + // SELECT_COLUMN + // Use first cell in column for focus cell + newRowIndex = 0; + + // Don't skip by colspan, + // but find first cell in next cellmap column + if (forward) { + newColIndex++; + } + } + + if (newColIndex < 0) { + // Request is before the first cell in column + + // Wrap to last cell in column + newColIndex = gLastColIndex; + + if (gSelectedCellsType == SELECT_CELL) { + // If moving by cell, also wrap to previous... + if (newRowIndex > 0) { + newRowIndex -= 1; + } else { + // ...or the last row. + newRowIndex = gLastRowIndex; + } + + inRow = true; + } + } else if (newColIndex > gLastColIndex) { + // Request is after the last cell in column + + // Wrap to first cell in column + newColIndex = 0; + + if (gSelectedCellsType == SELECT_CELL) { + // If moving by cell, also wrap to next... + if (newRowIndex < gLastRowIndex) { + newRowIndex++; + } else { + // ...or the first row. + newRowIndex = 0; + } + + inRow = true; + } + } + } + + // Get the cell at the new location + do { + if (!GetCellData(newRowIndex, newColIndex)) { + dump("MoveSelection: CELL NOT FOUND\n"); + return; + } + if (inRow) { + if (gCellData.startRowIndex == newRowIndex) { + break; + } else { + // Cell spans from a row above, look for the next cell in row. + newRowIndex += gCellData.actualRowSpan; + } + } else if (gCellData.startColIndex == newColIndex) { + break; + } else { + // Cell spans from a Col above, look for the next cell in column + newColIndex += gCellData.actualColSpan; + } + } while (true); + + // Save data for current selection before changing + if (gCellDataChanged) { + // && gDialog.ApplyBeforeMove.checked) + if (!ValidateCellData()) { + return; + } + + gActiveEditor.beginTransaction(); + // Apply changes to all selected cells + ApplyCellAttributes(); + gActiveEditor.endTransaction(); + + SetCloseButton(); + } + + // Set cell and other data for new selection + gCellElement = gCellData.value; + + // Save globals for new current cell + gCurRowIndex = gCellData.startRowIndex; + gCurColIndex = gCellData.startColIndex; + gCurColSpan = gCellData.actualColSpan; + + // Copy for new global cell + globalCellElement = gCellElement.cloneNode(false); + + // Change the selection + DoCellSelection(); + + // Scroll page so new selection is visible + // Using SELECTION_ANCHOR_REGION makes the upper-left corner of first selected cell + // the point to bring into view. + try { + var selectionController = gActiveEditor.selectionController; + selectionController.scrollSelectionIntoView( + selectionController.SELECTION_NORMAL, + selectionController.SELECTION_ANCHOR_REGION, + true + ); + } catch (e) {} + + // Reinitialize dialog using new cell + // if (!gDialog.KeepCurrentData.checked) + // Setting this false unchecks all "set attributes" checkboxes + gAdvancedEditUsed = false; + InitCellPanel(); + gAdvancedEditUsed = true; +} + +function DoCellSelection() { + // Collapse selection into to the focus cell + // so editor uses that as start cell + gSelection.collapse(gCellElement, 0); + + var tagNameObj = { value: "" }; + var countObj = { value: 0 }; + try { + switch (gSelectedCellsType) { + case SELECT_CELL: + gActiveEditor.selectTableCell(); + break; + case SELECT_ROW: + gActiveEditor.selectTableRow(); + break; + default: + gActiveEditor.selectTableColumn(); + break; + } + // Get number of cells selected + gActiveEditor.getSelectedOrParentTableElement(tagNameObj, countObj); + } catch (e) {} + + if (tagNameObj.value == "td") { + gSelectedCellCount = countObj.value; + } else { + gSelectedCellCount = 0; + } + + // Currently, we can only allow advanced editing on ONE cell element at a time + // else we ignore CSS, JS, and HTML attributes not already in dialog + SetElementEnabled(gDialog.AdvancedEditCell, gSelectedCellCount == 1); + + gDialog.AdvancedEditCell.setAttribute( + "tooltiptext", + gSelectedCellCount > 1 + ? GetString("AdvancedEditForCellMsg") + : gDialog.AdvancedEditCellToolTipText + ); +} + +function SetSelectionButtons() { + if (gSelectedCellsType == SELECT_ROW) { + // Trigger CSS to set images of up and down arrows + gDialog.PreviousButton.setAttribute("type", "row"); + gDialog.NextButton.setAttribute("type", "row"); + } else { + // or images of left and right arrows + gDialog.PreviousButton.setAttribute("type", "col"); + gDialog.NextButton.setAttribute("type", "col"); + } + DisableSelectionButtons( + (gSelectedCellsType == SELECT_ROW && gRowCount == 1) || + (gSelectedCellsType == SELECT_COLUMN && gColCount == 1) || + (gRowCount == 1 && gColCount == 1) + ); +} + +function DisableSelectionButtons(disable) { + gDialog.PreviousButton.setAttribute("disabled", disable ? "true" : "false"); + gDialog.NextButton.setAttribute("disabled", disable ? "true" : "false"); +} + +function SwitchToValidatePanel() { + if (gDialog.TabBox.selectedTab != gValidateTab) { + gDialog.TabBox.selectedTab = gValidateTab; + } +} + +function SetAlign(listID, defaultValue, element, attName) { + var value = document.getElementById(listID).value; + if (value == defaultValue) { + try { + gActiveEditor.removeAttributeOrEquivalent(element, attName, true); + } catch (e) {} + } else { + try { + gActiveEditor.setAttributeOrEquivalent(element, attName, value, true); + } catch (e) {} + } +} + +function ValidateTableData() { + gValidateTab = gDialog.TableTab; + gNewRowCount = Number( + ValidateNumber(gDialog.TableRowsInput, null, 1, gMaxRows, null, true, true) + ); + if (gValidationError) { + return false; + } + + gNewColCount = Number( + ValidateNumber( + gDialog.TableColumnsInput, + null, + 1, + gMaxColumns, + null, + true, + true + ) + ); + if (gValidationError) { + return false; + } + + // If user is deleting any cells, get confirmation + // (This is a global to the dialog and we ask only once per dialog session) + if (!gCanDelete && (gNewRowCount < gRowCount || gNewColCount < gColCount)) { + if ( + ConfirmWithTitle( + GetString("DeleteTableTitle"), + GetString("DeleteTableMsg"), + GetString("DeleteCells") + ) + ) { + gCanDelete = true; + } else { + SetTextboxFocus( + gNewRowCount < gRowCount + ? gDialog.TableRowsInput + : gDialog.TableColumnsInput + ); + return false; + } + } + + ValidateNumber( + gDialog.TableWidthInput, + gDialog.TableWidthUnits, + 1, + gMaxTableSize, + globalTableElement, + "width" + ); + if (gValidationError) { + return false; + } + + if (gUseCSS) { + ValidateNumber( + gDialog.TableHeightInput, + gDialog.TableHeightUnits, + 1, + gMaxTableSize, + globalTableElement, + "height" + ); + if (gValidationError) { + return false; + } + } + + ValidateNumber( + gDialog.BorderWidthInput, + null, + 0, + gMaxPixels, + globalTableElement, + "border" + ); + // TODO: Deal with "BORDER" without value issue + if (gValidationError) { + return false; + } + + ValidateNumber( + gDialog.SpacingInput, + null, + 0, + gMaxPixels, + globalTableElement, + "cellspacing" + ); + if (gValidationError) { + return false; + } + + ValidateNumber( + gDialog.PaddingInput, + null, + 0, + gMaxPixels, + globalTableElement, + "cellpadding" + ); + if (gValidationError) { + return false; + } + + SetAlign("TableAlignList", defHAlign, globalTableElement, "align"); + + // Color is set on globalCellElement immediately + return true; +} + +function ValidateCellData() { + gValidateTab = gDialog.CellTab; + + if (gDialog.CellHeightCheckbox.checked) { + ValidateNumber( + gDialog.CellHeightInput, + gDialog.CellHeightUnits, + 1, + gMaxTableSize, + globalCellElement, + "height" + ); + if (gValidationError) { + return false; + } + } + + if (gDialog.CellWidthCheckbox.checked) { + ValidateNumber( + gDialog.CellWidthInput, + gDialog.CellWidthUnits, + 1, + gMaxTableSize, + globalCellElement, + "width" + ); + if (gValidationError) { + return false; + } + } + + if (gDialog.CellHAlignCheckbox.checked) { + var hAlign = gDialog.CellHAlignList.value; + + // Horizontal alignment is complicated by "char" type + // We don't change current values if user didn't edit alignment + if (!gAlignWasChar) { + globalCellElement.removeAttribute(charStr); + + // Always set "align" attribute, + // so the default "left" is effective in a cell + // when parent row has align set. + globalCellElement.setAttribute("align", hAlign); + } + } + + if (gDialog.CellVAlignCheckbox.checked) { + // Always set valign (no default in 2nd param) so + // the default "middle" is effective in a cell + // when parent row has valign set. + SetAlign("CellVAlignList", "", globalCellElement, "valign"); + } + + if (gDialog.TextWrapCheckbox.checked) { + if (gDialog.TextWrapList.value == "nowrap") { + try { + gActiveEditor.setAttributeOrEquivalent( + globalCellElement, + "nowrap", + "nowrap", + true + ); + } catch (e) {} + } else { + try { + gActiveEditor.removeAttributeOrEquivalent( + globalCellElement, + "nowrap", + true + ); + } catch (e) {} + } + } + + return true; +} + +function ValidateData() { + var result; + + // Validate current panel first + if (gDialog.TabBox.selectedTab == gDialog.TableTab) { + result = ValidateTableData(); + if (result) { + result = ValidateCellData(); + } + } else { + result = ValidateCellData(); + if (result) { + result = ValidateTableData(); + } + } + if (!result) { + return false; + } + + // Set global element for AdvancedEdit + if (gDialog.TabBox.selectedTab == gDialog.TableTab) { + globalElement = globalTableElement; + } else { + globalElement = globalCellElement; + } + + return true; +} + +function ChangeCellTextbox(textboxID) { + // Filter input for just integers + forceInteger(textboxID); + + if (gDialog.TabBox.selectedTab == gDialog.CellTab) { + gCellDataChanged = true; + } +} + +// Call this when a textbox or menulist is changed +// so the checkbox is automatically set +function SetCheckbox(checkboxID) { + if (checkboxID && checkboxID.length > 0) { + // Set associated checkbox + document.getElementById(checkboxID).checked = true; + } + gCellDataChanged = true; +} + +function ChangeIntTextbox(textboxID, checkboxID) { + // Filter input for just integers + forceInteger(textboxID); + + // Set associated checkbox + SetCheckbox(checkboxID); +} + +function CloneAttribute(destElement, srcElement, attr) { + var value = srcElement.getAttribute(attr); + // Use editor methods since we are always + // modifying a table in the document and + // we need transaction system for undo + try { + if (!value || value.length == 0) { + gActiveEditor.removeAttributeOrEquivalent(destElement, attr, false); + } else { + gActiveEditor.setAttributeOrEquivalent(destElement, attr, value, false); + } + } catch (e) {} +} + +/* eslint-disable complexity */ +function ApplyTableAttributes() { + var newAlign = gDialog.TableCaptionList.value; + if (!newAlign) { + newAlign = ""; + } + + if (gTableCaptionElement) { + // Get current alignment + var align = GetHTMLOrCSSStyleValue( + gTableCaptionElement, + "align", + "caption-side" + ).toLowerCase(); + // This is the default + if (!align) { + align = "top"; + } + + if (newAlign == "") { + // Remove existing caption + try { + gActiveEditor.deleteNode(gTableCaptionElement); + } catch (e) {} + gTableCaptionElement = null; + } else if (newAlign != align) { + try { + if (newAlign == "top") { + // This is default, so don't explicitly set it + gActiveEditor.removeAttributeOrEquivalent( + gTableCaptionElement, + "align", + false + ); + } else { + gActiveEditor.setAttributeOrEquivalent( + gTableCaptionElement, + "align", + newAlign, + false + ); + } + } catch (e) {} + } + } else if (newAlign != "") { + // Create and insert a caption: + try { + gTableCaptionElement = gActiveEditor.createElementWithDefaults("caption"); + } catch (e) {} + if (gTableCaptionElement) { + if (newAlign != "top") { + gTableCaptionElement.setAttribute("align", newAlign); + } + + // Insert it into the table - caption is always inserted as first child + try { + gActiveEditor.insertNode(gTableCaptionElement, gTableElement, 0); + } catch (e) {} + + // Put selection back where it was + ChangeSelection(RESET_SELECTION); + } + } + + var countDelta; + var foundCell; + var i; + + if (gNewRowCount != gRowCount) { + countDelta = gNewRowCount - gRowCount; + if (gNewRowCount > gRowCount) { + // Append new rows + // Find first cell in last row + if (GetCellData(gLastRowIndex, 0)) { + try { + // Move selection to the last cell + gSelection.collapse(gCellData.value, 0); + // Insert new rows after it + gActiveEditor.insertTableRow(countDelta, true); + gRowCount = gNewRowCount; + gLastRowIndex = gRowCount - 1; + // Put selection back where it was + ChangeSelection(RESET_SELECTION); + } catch (ex) { + dump("FAILED TO FIND FIRST CELL IN LAST ROW\n"); + } + } + } else if (gCanDelete) { + // Delete rows + // Find first cell starting in first row we delete + var firstDeleteRow = gRowCount + countDelta; + foundCell = false; + for (i = 0; i <= gLastColIndex; i++) { + if (!GetCellData(firstDeleteRow, i)) { + // We failed to find a cell. + break; + } + + if (gCellData.startRowIndex == firstDeleteRow) { + foundCell = true; + break; + } + } + if (foundCell) { + try { + // Move selection to the cell we found + gSelection.collapse(gCellData.value, 0); + gActiveEditor.deleteTableRow(-countDelta); + gRowCount = gNewRowCount; + gLastRowIndex = gRowCount - 1; + if (gCurRowIndex > gLastRowIndex) { + // We are deleting our selection + // move it to start of table + ChangeSelectionToFirstCell(); + } else { + // Put selection back where it was. + ChangeSelection(RESET_SELECTION); + } + } catch (ex) { + dump("FAILED TO FIND FIRST CELL IN LAST ROW\n"); + } + } + } + } + + if (gNewColCount != gColCount) { + countDelta = gNewColCount - gColCount; + + if (gNewColCount > gColCount) { + // Append new columns + // Find last cell in first column + if (GetCellData(0, gLastColIndex)) { + try { + // Move selection to the last cell + gSelection.collapse(gCellData.value, 0); + gActiveEditor.insertTableColumn(countDelta, true); + gColCount = gNewColCount; + gLastColIndex = gColCount - 1; + // Restore selection + ChangeSelection(RESET_SELECTION); + } catch (ex) { + dump("FAILED TO FIND FIRST CELL IN LAST COLUMN\n"); + } + } + } else if (gCanDelete) { + // Delete columns + var firstDeleteCol = gColCount + countDelta; + foundCell = false; + for (i = 0; i <= gLastRowIndex; i++) { + // Find first cell starting in first column we delete + if (!GetCellData(i, firstDeleteCol)) { + // We failed to find a cell. + break; + } + + if (gCellData.startColIndex == firstDeleteCol) { + foundCell = true; + break; + } + } + if (foundCell) { + try { + // Move selection to the cell we found + gSelection.collapse(gCellData.value, 0); + gActiveEditor.deleteTableColumn(-countDelta); + gColCount = gNewColCount; + gLastColIndex = gColCount - 1; + if (gCurColIndex > gLastColIndex) { + ChangeSelectionToFirstCell(); + } else { + ChangeSelection(RESET_SELECTION); + } + } catch (ex) { + dump("FAILED TO FIND FIRST CELL IN LAST ROW\n"); + } + } + } + } + + // Clone all remaining attributes to pick up + // anything changed by Advanced Edit Dialog + try { + gActiveEditor.cloneAttributes(gTableElement, globalTableElement); + } catch (e) {} +} +/* eslint-enable complexity */ + +function ApplyCellAttributes() { + var rangeObj = { value: null }; + var selectedCell; + try { + selectedCell = gActiveEditor.getFirstSelectedCell(rangeObj); + } catch (e) {} + + if (!selectedCell) { + return; + } + + if (gSelectedCellCount == 1) { + // When only one cell is selected, simply clone entire element, + // thus CSS and JS from Advanced edit is copied + try { + gActiveEditor.cloneAttributes(selectedCell, globalCellElement); + } catch (e) {} + + if (gDialog.CellStyleCheckbox.checked) { + var currentStyleIndex = + selectedCell.nodeName.toLowerCase() == "th" ? 1 : 0; + if (gDialog.CellStyleList.selectedIndex != currentStyleIndex) { + // Switch cell types + // (replaces with new cell and copies attributes and contents) + try { + selectedCell = gActiveEditor.switchTableCellHeaderType(selectedCell); + } catch (e) {} + } + } + } else { + // Apply changes to all selected cells + // XXX THIS DOESN'T COPY ADVANCED EDIT CHANGES! + try { + while (selectedCell) { + ApplyAttributesToOneCell(selectedCell); + selectedCell = gActiveEditor.getNextSelectedCell(rangeObj); + } + } catch (e) {} + } + gCellDataChanged = false; +} + +function ApplyAttributesToOneCell(destElement) { + if (gDialog.CellHeightCheckbox.checked) { + CloneAttribute(destElement, globalCellElement, "height"); + } + + if (gDialog.CellWidthCheckbox.checked) { + CloneAttribute(destElement, globalCellElement, "width"); + } + + if (gDialog.CellHAlignCheckbox.checked) { + CloneAttribute(destElement, globalCellElement, "align"); + CloneAttribute(destElement, globalCellElement, charStr); + } + + if (gDialog.CellVAlignCheckbox.checked) { + CloneAttribute(destElement, globalCellElement, "valign"); + } + + if (gDialog.TextWrapCheckbox.checked) { + CloneAttribute(destElement, globalCellElement, "nowrap"); + } + + if (gDialog.CellStyleCheckbox.checked) { + var newStyleIndex = gDialog.CellStyleList.selectedIndex; + var currentStyleIndex = destElement.nodeName.toLowerCase() == "th" ? 1 : 0; + + if (newStyleIndex != currentStyleIndex) { + // Switch cell types + // (replaces with new cell and copies attributes and contents) + try { + destElement = gActiveEditor.switchTableCellHeaderType(destElement); + } catch (e) {} + } + } + + if (gDialog.CellColorCheckbox.checked) { + CloneAttribute(destElement, globalCellElement, "bgcolor"); + } +} + +function SetCloseButton() { + // Change text on "Cancel" button after Apply is used + if (!gApplyUsed) { + document.documentElement.setAttribute( + "buttonlabelcancel", + document.documentElement.getAttribute("buttonlabelclose") + ); + gApplyUsed = true; + } +} + +function Apply() { + if (ValidateData()) { + gActiveEditor.beginTransaction(); + + ApplyTableAttributes(); + + // We may have just a table, so check for cell element + if (globalCellElement) { + ApplyCellAttributes(); + } + + gActiveEditor.endTransaction(); + + SetCloseButton(); + return true; + } + return false; +} + +function onAccept(event) { + // Do same as Apply and close window if ValidateData succeeded + var retVal = Apply(); + if (retVal) { + SaveWindowLocation(); + } else { + event.preventDefault(); + } +} diff --git a/comm/suite/editor/components/dialogs/content/EdTableProps.xhtml b/comm/suite/editor/components/dialogs/content/EdTableProps.xhtml new file mode 100644 index 0000000000..30979acb4d --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdTableProps.xhtml @@ -0,0 +1,287 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog [ +<!ENTITY % edTableProperties SYSTEM "chrome://editor/locale/EditorTableProperties.dtd"> +%edTableProperties; +<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd"> +%edDialogOverlay; +]> + +<dialog title="&tableWindow.title;" + id="tableDlg" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload="Startup()" + buttons="accept,extra1,cancel" + buttonlabelclose="&closeButton.label;" + buttonlabelextra1="&applyButton.label;" + buttonaccesskeyextra1="&applyButton.accesskey;"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EdTableProps.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <tabbox id="TabBox"> + <tabs flex="1"> + <tab id="TableTab" label="&tableTab.label;"/> + <tab id="CellTab" label="&cellTab.label;"/> + </tabs> + <tabpanels> + + <!-- TABLE PANEL --> + <vbox> + <groupbox orient="horizontal"> + <hbox class="groupbox-title"> + <label class="header">&size.label;</label> + </hbox> + <grid> + <columns><column/><column/><column/><column/><column/></columns> + <rows> + <row align="center"> + <label value="&tableRows.label;" accesskey="&tableRows.accessKey;" control="TableRowsInput"/> + <textbox class="narrow" id="TableRowsInput" oninput="forceInteger(this.id);"/> + <spring class="bigspacer"/> + <label value="&tableHeight.label;" accesskey="&tableHeight.accessKey;" + id="TableHeightLabel" control="TableHeightInput"/> + <textbox class="narrow" id="TableHeightInput" oninput="forceInteger(this.id);"/> + <menulist id="TableHeightUnits"/> + </row> + <row align="center"> + <label value="&tableColumns.label;" accesskey="&tableColumns.accessKey;" control="TableColumnsInput"/> + <textbox class="narrow" id="TableColumnsInput" oninput="forceInteger(this.id);"/> + <spring class="bigspacer"/> + <label value="&tableWidth.label;" accesskey="&tableWidth.accessKey;" control="TableWidthInput"/> + <textbox class="narrow" id="TableWidthInput" oninput="forceInteger(this.id);"/> + <menulist id="TableWidthUnits"/> + </row> + </rows> + <!-- KEEP GRID LAYOUT here since we will be adding back support for table HEIGHT via CSS --> + </grid> + </groupbox> + <groupbox> + <hbox class="groupbox-title"> + <label class="header">&tableBorderSpacing.label;</label> + </hbox> + <grid> + <columns><column/><column/><column/></columns> + <rows> + <row align="center"> + <label control="BorderWidthInput" + value="&tableBorderWidth.label;" + accesskey="&tableBorderWidth.accessKey;"/> + <textbox class="narrow" id="BorderWidthInput" oninput="forceInteger(this.id);"/> + <label align="left" value="&pixels.label;"/> + </row> + <row align="center"> + <label control="SpacingInput" + value="&tableSpacing.label;" + accesskey="&tableSpacing.accessKey;"/> + <textbox class="narrow" id="SpacingInput" oninput="forceInteger(this.id);"/> + <label value="&tablePxBetwCells.label;"/> + </row> + <row align="center"> + <label control="PaddingInput" + value="&tablePadding.label;" + accesskey="&tablePadding.accessKey;"/> + <textbox class="narrow" id="PaddingInput" oninput="forceInteger(this.id);"/> + <label value="&tablePxBetwBrdrCellContent.label;"/> + </row> + </rows> + </grid> + </groupbox> + <!-- Table Alignment and Caption --> + <hbox flex="1" align="center"> + <label control="TableAlignList" + value="&tableAlignment.label;" + accesskey="&tableAlignment.accessKey;"/> + <menulist id="TableAlignList"> + <menupopup> + <menuitem label="&AlignLeft.label;" value="left"/> + <menuitem label="&AlignCenter.label;" value="center"/> + <menuitem label="&AlignRight.label;" value="right"/> + </menupopup> + </menulist> + <spacer class="spacer"/> + <label control="TableCaptionList" + value="&tableCaption.label;" + accesskey="&tableCaption.accessKey;"/> + <menulist id="TableCaptionList"> + <menupopup> + <menuitem label="&tableCaptionNone.label;" value=""/> + <menuitem label="&tableCaptionAbove.label;" value="top"/> + <menuitem label="&tableCaptionBelow.label;" value="bottom"/> + <menuitem label="&tableCaptionLeft.label;" value="left"/> + <menuitem label="&tableCaptionRight.label;" value="right"/> + </menupopup> + </menulist> + </hbox> + <separator class="groove"/> + <hbox align="center"> + <label value="&backgroundColor.label;"/> + <button id="tableBackground" class="color-button" oncommand="GetColorAndUpdate('tableBackgroundCW');"> + <spacer id="tableBackgroundCW" class="color-well"/> + </button> + <spacer class="spacer"/> + <label id="TableInheritColor" value="&tableInheritColor.label;" collapsed="true"/> + </hbox> + <separator class="groove"/> + <hbox flex="1" align="center"> + <spacer flex="1"/> + <button id="AdvancedEditButton" + oncommand="onAdvancedEdit();" + label="&AdvancedEditButton.label;" + accesskey="&AdvancedEditButton.accessKey;" + tooltiptext="&AdvancedEditButton.tooltip;"/> + </hbox> + <spacer flex="1"/> + </vbox><!-- Table Panel --> + + <!-- CELL PANEL --> + <vbox> + <groupbox orient="horizontal" align="center"> + <hbox class="groupbox-title"> + <label class="header">&cellSelection.label;</label> + </hbox> + <vbox> + <menulist id="SelectionList" oncommand="ChangeSelection(event.target.value)" flex="1"> + <menupopup> + <!-- JS code assumes order is Cell, Row, Column --> + <menuitem label="&cellSelectCell.label;" value="1"/> + <menuitem label="&cellSelectRow.label;" value="2"/> + <menuitem label="&cellSelectColumn.label;" value="3"/> + </menupopup> + </menulist> + <hbox flex="1"> + <button id="PreviousButton" + oncommand="MoveSelection(0)" + flex="1" + align="center"> + <image/> + <label value="&cellSelectPrevious.label;" + accesskey="&cellSelectPrevious.accessKey;" + control="PreviousButton"/> + </button> + <button id="NextButton" + oncommand="MoveSelection(1)" + class="align-right" + flex="1" + align="center"> + <image/> + <label value="&cellSelectNext.label;" + accesskey="&cellSelectNext.accessKey;" + control="NextButton"/> + </button> + </hbox> + </vbox> + <spacer class="bigspacer"/> + <description class="wrap" flex="1">&applyBeforeChange.label;</description> + </groupbox> + <hbox align="center"> + <!-- cell size groupbox --> + <groupbox> + <hbox class="groupbox-title"> + <label class="header">&size.label;</label> + </hbox> + <grid> + <columns><column/><column/><column flex="1"/></columns> + <rows> + <row align="center"> + <checkbox id="CellHeightCheckbox" label="&tableHeight.label;" accesskey="&tableHeight.accessKey;"/> + <textbox class="narrow" id="CellHeightInput" + oninput="ChangeIntTextbox(this.id, 'CellHeightCheckbox');"/> + <menulist id="CellHeightUnits" oncommand="SetCheckbox('CellHeightCheckbox');"/> + </row> + <row align="center"> + <checkbox id="CellWidthCheckbox" label="&tableWidth.label;" accesskey="&tableWidth.accessKey;"/> + <textbox class="narrow" id="CellWidthInput" + oninput="ChangeIntTextbox(this.id, 'CellWidthCheckbox');"/> + <menulist id="CellWidthUnits" oncommand="SetCheckbox('CellWidthCheckbox');"/> + </row> + </rows> + </grid> + <spacer class="bigspacer"/> + </groupbox> + <!-- Alignment --> + <groupbox> + <hbox class="groupbox-title"> + <label class="header">&cellContentAlignment.label;</label> + </hbox> + <grid> + <columns><column/><column flex="1"/><column/></columns> + <rows> + <row align="center"> + <checkbox id="CellVAlignCheckbox" label="&cellVertical.label;" accesskey="&cellVertical.accessKey;"/> + <menulist id="CellVAlignList" oncommand="SetCheckbox('CellVAlignCheckbox');"> + <menupopup> + <menuitem label="&cellAlignTop.label;" value="top"/> + <menuitem label="&cellAlignMiddle.label;" value="middle"/> + <menuitem label="&cellAlignBottom.label;" value="bottom"/> + </menupopup> + </menulist> + </row> + <row align="center"> + <checkbox id="CellHAlignCheckbox" label="&cellHorizontal.label;" accesskey="&cellHorizontal.accessKey;"/> + <menulist id="CellHAlignList" oncommand="SelectCellHAlign()"> + <menupopup> + <menuitem label="&AlignLeft.label;" value="left"/> + <menuitem label="&AlignCenter.label;" value="center"/> + <menuitem label="&AlignRight.label;" value="right"/> + <menuitem label="&cellAlignJustify.label;" value="justify"/> + </menupopup> + </menulist> + </row> + </rows> + </grid> + </groupbox> + </hbox> + <spacer class="spacer"/> + <hbox align="center"> + <checkbox id="CellStyleCheckbox" label="&cellStyle.label;" accesskey="&cellStyle.accessKey;"/> + <menulist id="CellStyleList" oncommand="SetCheckbox('CellStyleCheckbox');"> + <menupopup> + <menuitem label="&cellNormal.label;" value="td"/> + <menuitem label="&cellHeader.label;" value="th"/> + </menupopup> + </menulist> + <spacer class="bigspacer"/> + <checkbox id="TextWrapCheckbox" label="&cellTextWrap.label;" accesskey="&cellTextWrap.accessKey;"/> + <menulist id="TextWrapList" oncommand="SetCheckbox('TextWrapCheckbox');"> + <menupopup> + <menuitem label="&cellWrap.label;" value="wrap"/> + <menuitem label="&cellNoWrap.label;" value="nowrap"/> + </menupopup> + </menulist> + </hbox> + <separator class="groove"/> + <hbox align="center"> + <checkbox id="CellColorCheckbox" label="&backgroundColor.label;" accesskey="&backgroundColor.accessKey;"/> + <button class="color-button" oncommand="GetColorAndUpdate('cellBackgroundCW');"> + <spacer id="cellBackgroundCW" class="color-well"/> + </button> + <spacer class="spacer"/> + <label id="CellInheritColor" value="&cellInheritColor.label;" collapsed="true"/> + </hbox> + <separator class="groove"/> + <hbox align="center"> + <description class="wrap" flex="1" style="width: 1em">&cellUseCheckboxHelp.label;</description> + <button id="AdvancedEditButton2" + oncommand="onAdvancedEdit()" + label="&AdvancedEditButton.label;" + accesskey="&AdvancedEditButton.accessKey;" + tooltiptext="&AdvancedEditButton.tooltip;"/> + </hbox> + <spacer flex="1"/> + </vbox><!-- Cell Panel --> + </tabpanels> + </tabbox> + <spacer class="spacer"/> +</dialog> diff --git a/comm/suite/editor/components/dialogs/content/EdTextAreaProps.js b/comm/suite/editor/components/dialogs/content/EdTextAreaProps.js new file mode 100644 index 0000000000..da33ab60c9 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdTextAreaProps.js @@ -0,0 +1,171 @@ +/* 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 textareaElement; + +// 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"), + textareaName: document.getElementById("TextAreaName"), + textareaRows: document.getElementById("TextAreaRows"), + textareaCols: document.getElementById("TextAreaCols"), + textareaWrap: document.getElementById("TextAreaWrap"), + textareaReadOnly: document.getElementById("TextAreaReadOnly"), + textareaDisabled: document.getElementById("TextAreaDisabled"), + textareaTabIndex: document.getElementById("TextAreaTabIndex"), + textareaAccessKey: document.getElementById("TextAreaAccessKey"), + textareaValue: document.getElementById("TextAreaValue"), + MoreSection: document.getElementById("MoreSection"), + MoreFewerButton: document.getElementById("MoreFewerButton"), + }; + + // Get a single selected text area element + const kTagName = "textarea"; + try { + textareaElement = editor.getSelectedElement(kTagName); + } catch (e) {} + + if (textareaElement) { + // We found an element and don't need to insert one + insertNew = false; + + gDialog.textareaValue.value = textareaElement.value; + } else { + insertNew = true; + + // We don't have an element selected, + // so create one with default attributes + try { + textareaElement = editor.createElementWithDefaults(kTagName); + } catch (e) {} + + if (!textareaElement) { + dump("Failed to get selected element or create a new one!\n"); + window.close(); + return; + } + gDialog.textareaValue.value = GetSelectionAsText(); + } + + // Make a copy to use for AdvancedEdit + globalElement = textareaElement.cloneNode(false); + + InitDialog(); + + InitMoreFewer(); + + SetTextboxFocus(gDialog.textareaName); + + SetWindowLocation(); +} + +function InitDialog() { + gDialog.textareaName.value = globalElement.getAttribute("name"); + gDialog.textareaRows.value = globalElement.getAttribute("rows"); + gDialog.textareaCols.value = globalElement.getAttribute("cols"); + gDialog.textareaWrap.value = GetHTMLOrCSSStyleValue( + globalElement, + "wrap", + "white-space" + ); + gDialog.textareaReadOnly.checked = globalElement.hasAttribute("readonly"); + gDialog.textareaDisabled.checked = globalElement.hasAttribute("disabled"); + gDialog.textareaTabIndex.value = globalElement.getAttribute("tabindex"); + gDialog.textareaAccessKey.value = globalElement.getAttribute("accesskey"); + onInput(); +} + +function onInput() { + var disabled = + !gDialog.textareaName.value || + !gDialog.textareaRows.value || + !gDialog.textareaCols.value; + if (gDialog.accept.disabled != disabled) { + gDialog.accept.disabled = disabled; + } +} + +function ValidateData() { + var attributes = { + name: gDialog.textareaName.value, + rows: gDialog.textareaRows.value, + cols: gDialog.textareaCols.value, + wrap: gDialog.textareaWrap.value, + tabindex: gDialog.textareaTabIndex.value, + accesskey: gDialog.textareaAccessKey.value, + }; + var flags = { + readonly: gDialog.textareaReadOnly.checked, + disabled: gDialog.textareaDisabled.checked, + }; + 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() { + // All values are valid - copy to actual element in doc or + // element created to insert + ValidateData(); + + var editor = GetCurrentEditor(); + + editor.beginTransaction(); + + try { + editor.cloneAttributes(textareaElement, globalElement); + + if (insertNew) { + editor.insertElementAtSelection(textareaElement, true); + } + + // undoably set value + var initialText = gDialog.textareaValue.value; + if (initialText != textareaElement.value) { + editor.setShouldTxnSetSelection(false); + + while (textareaElement.hasChildNodes()) { + editor.deleteNode(textareaElement.lastChild); + } + if (initialText) { + var textNode = editor.document.createTextNode(initialText); + editor.insertNode(textNode, textareaElement, 0); + } + + editor.setShouldTxnSetSelection(true); + } + } finally { + editor.endTransaction(); + } + + SaveWindowLocation(); +} diff --git a/comm/suite/editor/components/dialogs/content/EdTextAreaProps.xhtml b/comm/suite/editor/components/dialogs/content/EdTextAreaProps.xhtml new file mode 100644 index 0000000000..9ab91664e8 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EdTextAreaProps.xhtml @@ -0,0 +1,115 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog [ +<!ENTITY % edTextAreaProperties SYSTEM "chrome://editor/locale/EditorTextAreaProperties.dtd"> +%edTextAreaProperties; +<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd"> +%edDialogOverlay; +]> + +<dialog title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml" + onload="Startup();" + buttons="accept,cancel"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EdTextAreaProps.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <groupbox> + <hbox class="groupbox-title"> + <label class="header">&Settings.label;</label> + </hbox> + <grid><columns><column/><column/></columns> + <rows> + <row align="center"> + <label control="TextAreaName" value="&TextAreaName.label;" accesskey="&TextAreaName.accessKey;"/> + <textbox id="TextAreaName" oninput="onInput();"/> + </row> + <row align="center"> + <label control="TextAreaRows" value="&TextAreaRows.label;" accesskey="&TextAreaRows.accessKey;"/> + <hbox> + <textbox id="TextAreaRows" class="narrow" oninput="forceInteger(this.id); onInput();"/> + </hbox> + </row> + <row align="center"> + <label control="TextAreaCols" value="&TextAreaCols.label;" accesskey="&TextAreaCols.accessKey;"/> + <hbox> + <textbox id="TextAreaCols" class="narrow" oninput="forceInteger(this.id); onInput();"/> + </hbox> + </row> + </rows> + </grid> + <hbox> + <button id="MoreFewerButton" oncommand="onMoreFewer();" persist="more"/> + </hbox> + <grid id="MoreSection"><columns><column/><column/></columns> + <rows> + <row align="center"> + <label control="TextAreaWrap" value="&TextAreaWrap.label;" accesskey="&TextAreaWrap.accessKey;"/> + <menulist id="TextAreaWrap"> + <menupopup> + <menuitem label="&WrapDefault.value;"/> + <menuitem label="&WrapOff.value;" value="off"/> + <menuseparator/> + <menuitem label="&WrapSoft.value;" value="soft"/> + <menuitem label="&WrapHard.value;" value="hard"/> + <menuseparator/> + <menuitem label="&WrapPhysical.value;" value="physical"/> + <menuitem label="&WrapVirtual.value;" value="virtual"/> + <menuseparator/> + <menuitem label="normal" value="normal"/> + <menuitem label="nowrap" value="nowrap"/> + <menuitem label="pre" value="pre"/> + </menupopup> + </menulist> + </row> + <row> + <spacer/> + <checkbox id="TextAreaReadOnly" label="&TextAreaReadOnly.label;" accesskey="&TextAreaReadOnly.accessKey;"/> + </row> + <row> + <spacer/> + <checkbox id="TextAreaDisabled" label="&TextAreaDisabled.label;" accesskey="&TextAreaDisabled.accessKey;"/> + </row> + <row align="center"> + <label control="TextAreaTabIndex" value="&TextAreaTabIndex.label;" accesskey="&TextAreaTabIndex.accessKey;"/> + <hbox> + <textbox id="TextAreaTabIndex" class="narrow" oninput="forceInteger(this.id);"/> + </hbox> + </row> + <row align="center"> + <label control="TextAreaAccessKey" value="&TextAreaAccessKey.label;" accesskey="&TextAreaAccessKey.accessKey;"/> + <hbox> + <textbox id="TextAreaAccessKey" class="narrow" maxlength="1"/> + </hbox> + </row> + <row> + <label control="TextAreaValue" value="&InitialText.label;" accesskey="&InitialText.accessKey;"/> + </row> + <html:textarea id="TextAreaValue" flex="1" rows="5"/> + </rows> + </grid> + </groupbox> + + <vbox id="AdvancedEdit"> + <hbox flex="1" style="margin-top: 0.2em" align="center"> + <!-- This will right-align the button --> + <spacer flex="1"/> + <button id="AdvancedEditButton1" oncommand="onAdvancedEdit()" label="&AdvancedEditButton.label;" + accesskey="&AdvancedEditButton.accessKey;" tooltiptext="&AdvancedEditButton.tooltip;"/> + </hbox> + <separator id="advancedSeparator" class="groove"/> + </vbox> + +</dialog> diff --git a/comm/suite/editor/components/dialogs/content/EditConflict.js b/comm/suite/editor/components/dialogs/content/EditConflict.js new file mode 100644 index 0000000000..28611796cd --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EditConflict.js @@ -0,0 +1,42 @@ +/* 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/. */ + +// dialog initialization code + +document.addEventListener("dialogcancel", onClose); + +function Startup() +{ + if (!GetCurrentEditor()) + { + window.close(); + return; + } + + SetWindowLocation(); +} + +function KeepCurrentPage() +{ + // Simply close dialog and don't change current page + //TODO: Should we force saving of the current page? + SaveWindowLocation(); + return true; +} + +function UseOtherPage() +{ + // Reload the URL -- that will get other editor's contents + window.opener.setTimeout(window.opener.EditorLoadUrl, 0, GetDocumentUrl()); + SaveWindowLocation(); + return true; +} + +function PreventCancel() +{ + SaveWindowLocation(); + + // Don't let Esc key close the dialog! + return false; +} diff --git a/comm/suite/editor/components/dialogs/content/EditConflict.xhtml b/comm/suite/editor/components/dialogs/content/EditConflict.xhtml new file mode 100644 index 0000000000..fce32792d2 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EditConflict.xhtml @@ -0,0 +1,40 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditConflict.dtd"> + +<dialog buttons="cancel" title="&windowTitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload="Startup()"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EditConflict.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <label value ="&conflictWarning.label;"/> + <spacer class="bigspacer"/> + <label value ="&conflictResolve.label;"/> + <spacer class="bigspacer"/> + <hbox flex="1"> + <spacer class="bigspacer"/> + <button label="&keepCurrentPageButton.label;" + oncommand="KeepCurrentPage()"/> + <spacer class="bigspacer"/> + </hbox> + <hbox flex="1"> + <spacer class="bigspacer"/> + <button dlgtype="cancel" + label="&useOtherPageButton.label;" + oncommand="UseOtherPage()"/> + <spacer class="bigspacer"/> + </hbox> +</dialog> diff --git a/comm/suite/editor/components/dialogs/content/EditorPublish.js b/comm/suite/editor/components/dialogs/content/EditorPublish.js new file mode 100644 index 0000000000..6947a439ee --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EditorPublish.js @@ -0,0 +1,558 @@ +/* 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/. */ + +var gPublishSiteData; +var gReturnData; +var gDefaultSiteIndex = -1; +var gDefaultSiteName; +var gPreviousDefaultDir; +var gPreviousTitle; +var gSettingsChanged = false; +var gInitialSiteName; +var gInitialSiteIndex = -1; +var gPasswordManagerOn = true; + +// Dialog initialization code + +document.addEventListener("dialogaccept", onAccept); +document.addEventListener("dialogcancel", onCancel); + +function Startup() +{ + window.opener.ok = false; + + // Element to edit is passed in + gInitialSiteName = window.arguments[1]; + gReturnData = window.arguments[2]; + if (!gReturnData || !GetCurrentEditor()) + { + dump("Publish: No editor or return data object not supplied\n"); + window.close(); + return; + } + + gDialog.TabBox = document.getElementById("TabBox"); + gDialog.PublishTab = document.getElementById("PublishTab"); + gDialog.SettingsTab = document.getElementById("SettingsTab"); + + // Publish panel + gDialog.PageTitleInput = document.getElementById("PageTitleInput"); + gDialog.FilenameInput = document.getElementById("FilenameInput"); + gDialog.SiteList = document.getElementById("SiteList"); + gDialog.DocDirList = document.getElementById("DocDirList"); + gDialog.OtherDirCheckbox = document.getElementById("OtherDirCheckbox"); + gDialog.OtherDirRadiogroup = document.getElementById("OtherDirRadiogroup"); + gDialog.SameLocationRadio = document.getElementById("SameLocationRadio"); + gDialog.UseSubdirRadio = document.getElementById("UseSubdirRadio"); + gDialog.OtherDirList = document.getElementById("OtherDirList"); + + // Settings Panel + gDialog.SiteNameInput = document.getElementById("SiteNameInput"); + gDialog.PublishUrlInput = document.getElementById("PublishUrlInput"); + gDialog.BrowseUrlInput = document.getElementById("BrowseUrlInput"); + gDialog.UsernameInput = document.getElementById("UsernameInput"); + gDialog.PasswordInput = document.getElementById("PasswordInput"); + gDialog.SavePassword = document.getElementById("SavePassword"); + + gPasswordManagerOn = Services.prefs.getBoolPref("signon.rememberSignons"); + gDialog.SavePassword.disabled = !gPasswordManagerOn; + + gPublishSiteData = GetPublishSiteData(); + gDefaultSiteName = GetDefaultPublishSiteName(); + + var addNewSite = false; + if (gPublishSiteData) + { + FillSiteList(); + } + else + { + // No current site data, start a new item in the Settings panel + AddNewSite(); + addNewSite = true; + } + + var docUrl = GetDocumentUrl(); + var scheme = GetScheme(docUrl); + var filename = ""; + + if (scheme) + { + filename = GetFilename(docUrl); + + if (scheme != "file") + { + var siteFound = false; + + // Editing a remote URL. + // Attempt to find doc URL in Site Data + if (gPublishSiteData) + { + var dirObj = {}; + var siteIndex = FindSiteIndexAndDocDir(gPublishSiteData, docUrl, dirObj); + + // Select this site only if the same as user's intended site, or there wasn't one + if (siteIndex != -1 && (gInitialSiteIndex == -1 || siteIndex == gInitialSiteIndex)) + { + siteFound = true; + + // Select the site we found + gDialog.SiteList.selectedIndex = siteIndex; + var docDir = dirObj.value; + + // Use the directory within site in the editable menulist + gPublishSiteData[siteIndex].docDir = docDir; + + //XXX HOW DO WE DECIDE WHAT "OTHER" DIR TO USE? + //gPublishSiteData[siteIndex].otherDir = docDir; + } + } + if (!siteFound) + { + // Not found in site database + // Setup for a new site and use data from a remote URL + if (!addNewSite) + AddNewSite(); + + addNewSite = true; + + var publishData = CreatePublishDataFromUrl(docUrl); + if (publishData) + { + filename = publishData.filename; + gDialog.SiteNameInput.value = publishData.siteName; + gDialog.PublishUrlInput.value = publishData.publishUrl; + gDialog.BrowseUrlInput.value = publishData.browseUrl; + gDialog.UsernameInput.value = publishData.username; + gDialog.PasswordInput.value = publishData.password; + gDialog.SavePassword.checked = false; + } + } + } + } + try { + gPreviousTitle = GetDocumentTitle(); + } catch (e) {} + + gDialog.PageTitleInput.value = gPreviousTitle; + gDialog.FilenameInput.value = decodeURIComponent(filename); + + if (!addNewSite) + { + // If not adding a site and we haven't selected a site -- use initial or default site + if (gDialog.SiteList.selectedIndex == -1) + gDialog.SiteList.selectedIndex = (gInitialSiteIndex != -1) ? gInitialSiteIndex : gDefaultSiteIndex; + + // Fill in all the site data for currently-selected site + SelectSiteList(); + SetTextboxFocus(gDialog.PageTitleInput); + } + + if (gDialog.SiteList.selectedIndex == -1) + { + // No selected site -- assume same directory + gDialog.OtherDirRadiogroup.selectedItem = gDialog.SameLocationRadio; + } + else if (gPublishSiteData[gDialog.SiteList.selectedIndex].docDir == + gPublishSiteData[gDialog.SiteList.selectedIndex].otherDir) + { + // For now, check "same location" if dirs are already set to same directory + gDialog.OtherDirRadiogroup.selectedItem = gDialog.SameLocationRadio; + } + else + { + gDialog.OtherDirRadiogroup.selectedItem = gDialog.UseSubdirRadio; + } + + doEnabling(); + + SetWindowLocation(); +} + +function FillSiteList() +{ + gDialog.SiteList.removeAllItems(); + gDefaultSiteIndex = -1; + + // Fill the site lists + var count = gPublishSiteData.length; + var i; + + for (i = 0; i < count; i++) + { + var name = gPublishSiteData[i].siteName; + var menuitem = gDialog.SiteList.appendItem(name); + // Highlight the default site + if (name == gDefaultSiteName) + { + gDefaultSiteIndex = i; + if (menuitem) + { + menuitem.setAttribute("class", "menuitem-highlight-1"); + menuitem.setAttribute("default", "true"); + } + } + // Find initial site location + if (name == gInitialSiteName) + gInitialSiteIndex = i; + } +} + +function doEnabling() +{ + var disableOther = !gDialog.OtherDirCheckbox.checked; + gDialog.SameLocationRadio.disabled = disableOther; + gDialog.UseSubdirRadio.disabled = disableOther; + gDialog.OtherDirList.disabled = (disableOther || gDialog.SameLocationRadio.selected); +} + +function SelectSiteList() +{ + var selectedSiteIndex = gDialog.SiteList.selectedIndex; + + var siteName = ""; + var publishUrl = ""; + var browseUrl = ""; + var username = ""; + var password = ""; + var savePassword = false; + var publishOtherFiles = true; + + gDialog.DocDirList.removeAllItems(); + gDialog.OtherDirList.removeAllItems(); + + if (gPublishSiteData && selectedSiteIndex != -1) + { + siteName = gPublishSiteData[selectedSiteIndex].siteName; + publishUrl = gPublishSiteData[selectedSiteIndex].publishUrl; + browseUrl = gPublishSiteData[selectedSiteIndex].browseUrl; + username = gPublishSiteData[selectedSiteIndex].username; + savePassword = gPasswordManagerOn ? gPublishSiteData[selectedSiteIndex].savePassword : false; + if (savePassword) + password = gPublishSiteData[selectedSiteIndex].password; + + // Fill the directory menulists + if (gPublishSiteData[selectedSiteIndex].dirList.length) + { + for (var i = 0; i < gPublishSiteData[selectedSiteIndex].dirList.length; i++) + { + gDialog.DocDirList.appendItem(gPublishSiteData[selectedSiteIndex].dirList[i]); + gDialog.OtherDirList.appendItem(gPublishSiteData[selectedSiteIndex].dirList[i]); + } + } + gDialog.DocDirList.value = FormatDirForPublishing(gPublishSiteData[selectedSiteIndex].docDir); + gDialog.OtherDirList.value = FormatDirForPublishing(gPublishSiteData[selectedSiteIndex].otherDir); + publishOtherFiles = gPublishSiteData[selectedSiteIndex].publishOtherFiles; + + } + else + { + gDialog.DocDirList.value = ""; + gDialog.OtherDirList.value = ""; + } + + gDialog.SiteNameInput.value = siteName; + gDialog.PublishUrlInput.value = publishUrl; + gDialog.BrowseUrlInput.value = browseUrl; + gDialog.UsernameInput.value = username; + gDialog.PasswordInput.value = password; + gDialog.SavePassword.checked = savePassword; + gDialog.OtherDirCheckbox.checked = publishOtherFiles; + + doEnabling(); +} + +function AddNewSite() +{ + // Button in Publish panel allows user + // to automatically switch to "Settings" panel + // to enter data for new site + SwitchPanel(gDialog.SettingsTab); + + gDialog.SiteList.selectedIndex = -1; + + SelectSiteList(); + + gSettingsChanged = true; + + SetTextboxFocus(gDialog.SiteNameInput); +} + +function SelectPublishTab() +{ + if (gSettingsChanged && !ValidateSettings()) + return; + + SwitchPanel(gDialog.PublishTab); + SetTextboxFocus(gDialog.PageTitleInput); +} + +function SelectSettingsTab() +{ + SwitchPanel(gDialog.SettingsTab); + SetTextboxFocus(gDialog.SiteNameInput); +} + +function SwitchPanel(tab) +{ + if (gDialog.TabBox.selectedTab != tab) + gDialog.TabBox.selectedTab = tab; +} + +function onInputSettings() +{ + // TODO: Save current data during SelectSite and compare here + // to detect if real change has occurred? + gSettingsChanged = true; +} + +function GetPublishUrlInput() +{ + gDialog.PublishUrlInput.value = FormatUrlForPublishing(gDialog.PublishUrlInput.value); + return gDialog.PublishUrlInput.value; +} + +function GetBrowseUrlInput() +{ + gDialog.BrowseUrlInput.value = FormatUrlForPublishing(gDialog.BrowseUrlInput.value); + return gDialog.BrowseUrlInput.value; +} + +function GetDocDirInput() +{ + gDialog.DocDirList.value = FormatDirForPublishing(gDialog.DocDirList.value); + return gDialog.DocDirList.value; +} + +function GetOtherDirInput() +{ + gDialog.OtherDirList.value = FormatDirForPublishing(gDialog.OtherDirList.value); + return gDialog.OtherDirList.value; +} + +function ChooseDir(menulist) +{ + //TODO: For FTP publish destinations, get file listing of just dirs + // and build a tree to let user select dir +} + +function ValidateSettings() +{ + var siteName = TrimString(gDialog.SiteNameInput.value); + if (!siteName) + { + ShowErrorInPanel(gDialog.SettingsTab, "MissingSiteNameError", gDialog.SiteNameInput); + return false; + } + if (PublishSiteNameExists(siteName, gPublishSiteData, gDialog.SiteList.selectedIndex)) + { + SwitchPanel(gDialog.SettingsTab); + ShowInputErrorMessage(GetString("DuplicateSiteNameError").replace(/%name%/, siteName)); + SetTextboxFocus(gDialog.SiteNameInput); + return false; + } + + // Extract username and password while removing them from publishingUrl + var urlUserObj = {}; + var urlPassObj = {}; + var publishUrl = StripUsernamePassword(gDialog.PublishUrlInput.value, urlUserObj, urlPassObj); + if (publishUrl) + { + publishUrl = FormatUrlForPublishing(publishUrl); + + // Assume scheme = "ftp://" if missing + // This compensates when user enters hostname w/o scheme (as most ISPs provide) + if (!GetScheme(publishUrl)) + publishUrl = "ftp://" + publishUrl; + + gDialog.PublishUrlInput.value = publishUrl; + } + else + { + ShowErrorInPanel(gDialog.SettingsTab, "MissingPublishUrlError", gDialog.PublishUrlInput); + return false; + } + var browseUrl = GetBrowseUrlInput(); + + var username = TrimString(gDialog.UsernameInput.value); + var savePassword = gDialog.SavePassword.checked; + var password = gDialog.PasswordInput.value; + var publishOtherFiles = gDialog.OtherDirCheckbox.checked; + + //XXX If there was a username and/or password in the publishUrl + // AND in the input field, which do we use? + // Let's use those in url only if input is empty + if (!username) + { + username = urlUserObj.value; + gDialog.UsernameInput.value = username; + gSettingsChanged = true; + } + if (!password) + { + password = urlPassObj.value; + gDialog.PasswordInput.value = password; + gSettingsChanged = true; + } + + // Update or add data for a site + var siteIndex = gDialog.SiteList.selectedIndex; + var newSite = false; + + if (siteIndex == -1) + { + // No site is selected, add a new site at the end + if (gPublishSiteData) + { + siteIndex = gPublishSiteData.length; + } + else + { + // First time: start entire site array + gPublishSiteData = new Array(1); + siteIndex = 0; + gDefaultSiteIndex = 0; + gDefaultSiteName = siteName; + } + gPublishSiteData[siteIndex] = {}; + gPublishSiteData[siteIndex].docDir = ""; + gPublishSiteData[siteIndex].otherDir = ""; + gPublishSiteData[siteIndex].dirList = [""]; + gPublishSiteData[siteIndex].publishOtherFiles = true; + gPublishSiteData[siteIndex].previousSiteName = siteName; + newSite = true; + } + gPublishSiteData[siteIndex].siteName = siteName; + gPublishSiteData[siteIndex].publishUrl = publishUrl; + gPublishSiteData[siteIndex].browseUrl = browseUrl; + gPublishSiteData[siteIndex].username = username; + // Don't save password in data that will be saved in prefs + gPublishSiteData[siteIndex].password = savePassword ? password : ""; + gPublishSiteData[siteIndex].savePassword = savePassword; + + if (publishOtherFiles != gPublishSiteData[siteIndex].publishOtherFiles) + gSettingsChanged = true; + + gPublishSiteData[siteIndex].publishOtherFiles = publishOtherFiles; + + gDialog.SiteList.selectedIndex = siteIndex; + if (siteIndex == gDefaultSiteIndex) + gDefaultSiteName = siteName; + + // Should never be empty, but be sure we have a default site + if (!gDefaultSiteName) + { + gDefaultSiteName = gPublishSiteData[0].siteName; + gDefaultSiteIndex = 0; + } + + // Rebuild the site menulist if we added a new site + if (newSite) + { + FillSiteList(); + gDialog.SiteList.selectedIndex = siteIndex; + } + else + { + // Update selected item if sitename changed + var selectedItem = gDialog.SiteList.selectedItem; + if (selectedItem) + { + var oldName = selectedItem.getAttribute("label"); + if (oldName != siteName) + { + selectedItem.setAttribute("label", siteName); + gDialog.SiteList.setAttribute("label", siteName); + gSettingsChanged = true; + if (oldName == gDefaultSiteName) + gDefaultSiteName = siteName; + } + } + } + + // Get the directory name in site to publish to + var docDir = GetDocDirInput(); + + gPublishSiteData[siteIndex].docDir = docDir; + + // And directory for images and other files + var otherDir = GetOtherDirInput(); + if (gDialog.SameLocationRadio.selected) + otherDir = docDir; + else + otherDir = GetOtherDirInput(); + + gPublishSiteData[siteIndex].otherDir = otherDir; + + // Fill return data object + gReturnData.siteName = siteName; + gReturnData.previousSiteName = gPublishSiteData[siteIndex].previousSiteName; + gReturnData.publishUrl = publishUrl; + gReturnData.browseUrl = browseUrl; + gReturnData.username = username; + // Note that we use the password for the next publish action + // even if savePassword is false; but we won't save it in PasswordManager database + gReturnData.password = password; + gReturnData.savePassword = savePassword; + gReturnData.docDir = gPublishSiteData[siteIndex].docDir; + gReturnData.otherDir = gPublishSiteData[siteIndex].otherDir; + gReturnData.publishOtherFiles = publishOtherFiles; + gReturnData.dirList = gPublishSiteData[siteIndex].dirList; + return true; +} + +function ValidateData() +{ + if (!ValidateSettings()) + return false; + + var siteIndex = gDialog.SiteList.selectedIndex; + if (siteIndex == -1) + return false; + + var filename = TrimString(gDialog.FilenameInput.value); + if (!filename) + { + ShowErrorInPanel(gDialog.PublishTab, "MissingPublishFilename", gDialog.FilenameInput); + return false; + } + gReturnData.filename = filename; + + return true; +} + +function ShowErrorInPanel(tab, errorMsgId, widgetWithError) +{ + SwitchPanel(tab); + ShowInputErrorMessage(GetString(errorMsgId)); + if (widgetWithError) + SetTextboxFocus(widgetWithError); +} + +function onAccept(event) +{ + if (ValidateData()) + { + // DON'T save the docDir and otherDir before trying to publish + gReturnData.saveDirs = false; + + // We save new site data to prefs only if we are attempting to publish + if (gSettingsChanged) + SavePublishDataToPrefs(gReturnData); + + // Set flag to resave data after publishing + // so we save docDir and otherDir if we published successfully + gReturnData.savePublishData = true; + + var title = TrimString(gDialog.PageTitleInput.value); + if (title != gPreviousTitle) + SetDocumentTitle(title); + + SaveWindowLocation(); + window.opener.ok = true; + return; + } + + event.preventDefault(); +} diff --git a/comm/suite/editor/components/dialogs/content/EditorPublish.xhtml b/comm/suite/editor/components/dialogs/content/EditorPublish.xhtml new file mode 100644 index 0000000000..ed15c9f7d2 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EditorPublish.xhtml @@ -0,0 +1,132 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> +<?xml-stylesheet href="chrome://messenger/skin/menulist.css" type="text/css"?> + +<?xul-overlay href="chrome://editor/content/EditorPublishOverlay.xhtml"?> + +<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorPublish.dtd"> + +<dialog title="&windowTitle.label;" + id="publishDlg" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload="Startup()" + buttons="accept,cancel" + buttonlabelaccept="&publishButton.label;"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EditorPublish.js"/> + <script src="chrome://editor/content/publishprefs.js"/> + <script src="chrome://messenger/content/customElements.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <tabbox id="TabBox"> + <tabs flex="1"> + <tab id="PublishTab" oncommand="SelectPublishTab()" label="&publishTab.label;"/> + <tab id="SettingsTab" oncommand="SelectSettingsTab()" label="&settingsTab.label;"/> + </tabs> + <tabpanels> + <!-- PUBLISH PANEL --> + <vbox> + <spacer class="spacer"/> + <grid pack="start"> + <columns><column/><column/><column/></columns> + <rows> + <row align="center"> + <label value="&siteList.label;" + accesskey="&siteList.accesskey;" + control="SiteList"/> + <!-- Contents filled in at runtime --> + <menulist id="SiteList" + style="min-width:18em; max-width:18em;" crop="right" + tooltiptext="&siteList.tooltip;" + oncommand="SelectSiteList();"/> + <hbox> + <button label="&newSiteButton.label;" + accesskey="&newSiteButton.accesskey;" + oncommand="AddNewSite();"/> + <spacer flex="1"/> + </hbox> + </row> + <spacer class="spacer"/> + <row align="center"> + <label value="&pageTitle.label;" accesskey="&pageTitle.accesskey;" + control="PageTitleInput"/> + <textbox id="PageTitleInput" + tooltiptext="&pageTitle.tooltip;" class="minWidth15"/> + <label value="&pageTitleExample.label;"/> + </row> + <row align="center"> + <label value="&filename.label;" accesskey="&filename.accesskey;" + control="FilenameInput"/> + <textbox id="FilenameInput" + tooltiptext="&filename.tooltip;" class="minWidth15 uri-element"/> + <label value="&filenameExample.label;"/> + </row> + </rows> + </grid> + <spacer class="spacer"/> + <label value="&docDirList.label;" + accesskey="&docDirList.accesskey;" + control="DocDirList"/> + <hbox align="center"> + <!-- Contents filled in at runtime --> + <menulist is="menulist-editable" id="DocDirList" + class="minWidth20 uri-element" editable="true" flex="1" + tooltiptext="&docDirList.tooltip;" oninput="onInputSettings();"/> + </hbox> + <spacer class="spacer"/> + <groupbox> + <caption> + <checkbox id="OtherDirCheckbox" label="&publishImgCheckbox.label;" + accesskey="&publishImgCheckbox.accesskey;" + tooltiptext="&publishImgCheckbox.tooltip;" + oncommand="doEnabling();"/> + </caption> + <vbox> + <radiogroup id="OtherDirRadiogroup"> + <hbox> + <spacer class="checkbox-spacer"/> + <radio id="SameLocationRadio" label="&sameLocationRadio.label;" + accesskey="&sameLocationRadio.accesskey;" + tooltiptext="&sameLocationRadio.tooltip;" + oncommand="doEnabling();"/> + </hbox> + <hbox> + <spacer class="checkbox-spacer"/> + <radio id="UseSubdirRadio" label="&useSubdirRadio.label;" + accesskey="&useSubdirRadio.accesskey;" + tooltiptext="&useSubdirRadio.tooltip;" + oncommand="doEnabling();"/> + </hbox> + </radiogroup> + </vbox> + <hbox> + <spacer class="checkbox-spacer"/> + <spacer class="radio-spacer"/> + <!-- Contents filled in at runtime --> + <menulist is="menulist-editable" id="OtherDirList" + class="minWidth20 uri-element" + editable="true" flex="1" tooltiptext="&otherDirList.tooltip;" + oninput="onInputSettings();"/> + </hbox> + </groupbox> + <spacer flex="1"/> + </vbox><!-- Publish Panel --> + + <!-- SETTINGS PANEL --> + <hbox id="SettingsPanel"> + <!-- from EditorPublishOverlay.xhtml --> + <vbox id="PublishSettingsInputs" flex="1"/> + </hbox><!-- Settings Panel --> + </tabpanels> + </tabbox> +</dialog> diff --git a/comm/suite/editor/components/dialogs/content/EditorPublishOverlay.xhtml b/comm/suite/editor/components/dialogs/content/EditorPublishOverlay.xhtml new file mode 100644 index 0000000000..136a75623c --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EditorPublishOverlay.xhtml @@ -0,0 +1,66 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE overlay SYSTEM "chrome://editor/locale/EditorPublish.dtd"> + +<overlay id="EditorPublishOverlay" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"> + +<vbox id="PublishSettingsInputs"> + <groupbox id="ServerSettingsBox"> + <label class="header">&serverInfo.label;</label> + <hbox align="center"> + <label value="&siteName.label;" accesskey="&siteName.accesskey;" + control="SiteNameInput"/> + <textbox id="SiteNameInput" class="MinWidth20em" flex="1" + tooltiptext="&siteName.tooltip;" oninput="onInputSettings();"/> + </hbox> + <spacer class="spacer"/> + <label value="&siteUrl.label;" accesskey="&siteUrl.accesskey;" + control="PublishUrlInput"/> + <textbox id="PublishUrlInput" class="MinWidth20em uri-element" + tooltiptext="&siteUrl.tooltip;" oninput="onInputSettings();"/> + <spacer class="spacer"/> + <label value="&browseUrl.label;" accesskey="&browseUrl.accesskey;" + control="BrowseUrlInput"/> + <textbox id="BrowseUrlInput" class="MinWidth20em uri-element" + tooltiptext="&browseUrl.tooltip;" oninput="onInputSettings();"/> + <spacer class="spacer"/> + </groupbox> + <groupbox id="LoginSettingsBox"> + <label class="header">&loginInfo.label;</label> + <grid> + <columns><column flex="1"/><column flex="3"/></columns> + <rows> + <row align="center"> + <label value="&username.label;" accesskey="&username.accesskey;" + control="UsernameInput"/> + <textbox id="UsernameInput" class="MinWidth10em" flex="1" + tooltiptext="&username.tooltip;" oninput="onInputSettings();"/> + </row> + <row align="center"> + <label value="&password.label;" accesskey="&password.accesskey;" + control="PasswordInput"/> + <hbox> + <textbox id="PasswordInput" type="password" class="MinWidth5em" + oninput="onInputSettings();" + tooltiptext="&password.tooltip;"/> + <checkbox id="SavePassword" label="&savePassword.label;" + accesskey="&savePassword.accesskey;" + tooltiptext="&savePassword.tooltip;" + oncommand="onInputSettings();"/> + </hbox> + </row> + </rows> + </grid> + <spacer class="spacer"/> + </groupbox> +</vbox> + +</overlay> diff --git a/comm/suite/editor/components/dialogs/content/EditorPublishProgress.js b/comm/suite/editor/components/dialogs/content/EditorPublishProgress.js new file mode 100644 index 0000000000..dafa053661 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EditorPublishProgress.js @@ -0,0 +1,391 @@ +/* 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/. */ + +var gInProgress = true; +var gPublishData; +var gPersistObj; +var gTotalFileCount = 0; +var gSucceededCount = 0; +var gFinished = false; +var gPublishingFailed = false; +var gFileNotFound = false; +var gStatusMessage=""; + +var gTimerID; +var gAllowEnterKey = false; + +// Publishing error codes +// These are translated from C++ error code strings like this: +// kFileNotFound = "FILE_NOT_FOUND", +const kNetReset = 2152398868; // nsISocketTransportService.idl +const kFileNotFound = 2152857618; +const kNotConnected = 2152398860; // in netCore.h +const kConnectionRefused = 2152398861; // nsISocketTransportService.idl +const kNetTimeout = 2152398862; // nsISocketTransportService.idl +const kNoConnectionOrTimeout = 2152398878; +const kPortAccessNotAllowed = 2152398867; // netCore.h +const kOffline = 2152398865; // netCore.h +const kDiskFull = 2152857610; +const kNoDeviceSpace = 2152857616; +const kNameTooLong = 2152857617; +const kAccessDenied = 2152857621; + +// These are more errors that I don't think we encounter during publishing, +// so we don't have error strings yet. Let's keep them here for future reference +//const kUnrecognizedPath = 2152857601; +//const kUnresolvableSymlink = 2152857602; +//const kUnknownType = 2152857604; +//const kDestinationNotDir = 2152857605; +//const kTargetDoesNotExist = 2152857606; +//const kAlreadyExists = 2152857608; +//const kInvalidPath = 2152857609; +//const kNotDirectory = 2152857612; +//const kIsDirectory = 2152857613; +//const kIsLocked = 2152857614; +//const kTooBig = 2152857615; +//const kReadOnly = 2152857619; +//const kDirectoryNotEmpty = 2152857620; +//const kErrorBindingRedirected = 2152398851; +//const kAlreadyConnected = 2152398859; // in netCore.h +//const kInProgress = 2152398863; // netCore.h +//const kNoContent = 2152398865; // netCore.h +//const kUnknownProtocol = 2152398866 // netCore.h +//const kFtpLogin = 2152398869; // ftpCore.h +//const kFtpCWD = 2152398870; // ftpCore.h +//const kFtpPasv = 2152398871; // ftpCore.h +//const kFtpPwd = 2152398872; // ftpCore.h + +document.addEventListener("dialogaccept", onEnterKey); +document.addEventListener("dialogcancel", onClose); + +function Startup() +{ + gPublishData = window.arguments[0]; + if (!gPublishData) + { + dump("No publish data!\n"); + window.close(); + return; + } + + gDialog.FileList = document.getElementById("FileList"); + gDialog.FinalStatusMessage = document.getElementById("FinalStatusMessage"); + gDialog.StatusMessage = document.getElementById("StatusMessage"); + gDialog.KeepOpen = document.getElementById("KeepOpen"); + gDialog.Close = document.documentElement.getButton("cancel"); + + SetWindowLocation(); + var title = GetDocumentTitle(); + if (!title) + title = "(" + opener.gUntitledString + ")"; + document.title = GetString("PublishProgressCaption").replace(/%title%/, title); + + document.getElementById("PublishToSite").value = + GetString("PublishToSite").replace(/%title%/, TruncateStringAtWordEnd(gPublishData.siteName, 25)); + + // Show publishing destination URL + document.getElementById("PublishUrl").value = gPublishData.publishUrl; + + // Show subdirectories only if not empty + if (gPublishData.docDir || gPublishData.otherDir) + { + if (gPublishData.docDir) + document.getElementById("docDir").value = gPublishData.docDir; + else + document.getElementById("DocSubdir").hidden = true; + + if (gPublishData.publishOtherFiles && gPublishData.otherDir) + document.getElementById("otherDir").value = gPublishData.otherDir; + else + document.getElementById("OtherSubdir").hidden = true; + } + else + document.getElementById("Subdirectories").hidden = true; + + // Add the document to the "publish to" list as quick as possible! + SetProgressStatus(gPublishData.filename, "busy"); + + if (gPublishData.publishOtherFiles) + { + // When publishing images as well, expand list to show more items + gDialog.FileList.setAttribute("rows", 5); + window.sizeToContent(); + } + + // Now that dialog is initialized, we can start publishing + gPersistObj = window.opener.StartPublishing(); +} + +// this function is to be used when we cancel persist's saving +// since not all messages will be returned to us if we cancel +// this function changes status for all non-done/non-failure to failure +function SetProgressStatusCancel() +{ + let listitems = document.querySelectorAll('listitem:not([progress="done"]):not([progress="failed"])'); + if (!listitems) + return; + + for (var i=0; i < listitems.length; i++) + { + listitems[i].setAttribute("progress", "failed"); + } +} + +// Add filename to list of files to publish +// or set status for file already in the list +// Returns true if file was in the list +function SetProgressStatus(filename, status) +{ + if (!filename) + return false; + + if (!status) + status = "busy"; + + // Just set attribute for status icon if we already have this filename. + let listitem = document.querySelector('listitem[label="' + filename + '"]'); + if (listitem) + { + listitem.setAttribute("progress", status); + return true; + } + // We're adding a new file item to list + gTotalFileCount++; + + listitem = document.createXULElement("listitem"); + if (listitem) + { + listitem.setAttribute("class", "listitem-iconic progressitem"); + // This triggers CSS to show icon for each status state + listitem.setAttribute("progress", status); + listitem.setAttribute("label", filename); + gDialog.FileList.appendChild(listitem); + } + return false; +} + +function SetProgressFinished(filename, networkStatus) +{ + var abortPublishing = false; + if (filename) + { + var status = networkStatus ? "failed" : "done"; + if (networkStatus == 0) + gSucceededCount++; + + SetProgressStatus(filename, status); + } + + if (networkStatus != 0) // Error condition + { + // We abort on all errors except if image file was not found + abortPublishing = networkStatus != kFileNotFound; + + // Mark all remaining files as "failed" + if (abortPublishing) + { + gPublishingFailed = true; + SetProgressStatusCancel(); + gDialog.FinalStatusMessage.value = GetString("PublishFailed"); + } + + switch (networkStatus) + { + case kFileNotFound: + gFileNotFound = true; + if (filename) + gStatusMessage = GetString("FileNotFound").replace(/%file%/, filename); + break; + case kNetReset: + // We get this when subdir doesn't exist AND + // if filename used is same as an existing subdir + var dir = (gPublishData.filename == filename) ? + gPublishData.docDir : gPublishData.otherDir; + + if (dir) + { + // This is the ambiguous case when we can't tell if subdir or filename is bad + // Remove terminal "/" from dir string and insert into message + gStatusMessage = GetString("SubdirDoesNotExist").replace(/%dir%/, dir.slice(0, dir.length-1)); + gStatusMessage = gStatusMessage.replace(/%file%/, filename); + + // Remove directory from saved prefs + // XXX Note that if subdir is good, + // but filename = next level subdirectory name, + // we really shouldn't remove subdirectory, + // but it's impossible to differentiate this case! + RemovePublishSubdirectoryFromPrefs(gPublishData, dir); + } + else if (filename) + gStatusMessage = GetString("FilenameIsSubdir").replace(/%file%/, filename); + + break; + case kNotConnected: + case kConnectionRefused: + case kNetTimeout: + case kNoConnectionOrTimeout: + case kPortAccessNotAllowed: + gStatusMessage = GetString("ServerNotAvailable"); + break; + case kOffline: + gStatusMessage = GetString("Offline"); + break; + case kDiskFull: + case kNoDeviceSpace: + if (filename) + gStatusMessage = GetString("DiskFull").replace(/%file%/, filename); + break; + case kNameTooLong: + if (filename) + gStatusMessage = GetString("NameTooLong").replace(/%file%/, filename); + break; + case kAccessDenied: + if (filename) + gStatusMessage = GetString("AccessDenied").replace(/%file%/, filename); + break; + case kUnknownType: + default: + gStatusMessage = GetString("UnknownPublishError") + break; + } + } + else if (!filename) + { + gFinished = true; + + document.documentElement.setAttribute("buttonlabelcancel", + document.documentElement.getAttribute("buttonlabelclose")); + + if (!gStatusMessage) + gStatusMessage = GetString(gPublishingFailed ? "UnknownPublishError" : "AllFilesPublished"); + + // Now allow "Enter/Return" key to close the dialog + AllowDefaultButton(); + + if (gPublishingFailed || gFileNotFound) + { + // Show "Troubleshooting" button to help solving problems + // and key for successful / failed files + document.getElementById("failureBox").hidden = false; + } + } + + if (gStatusMessage) + SetStatusMessage(gStatusMessage); +} + +function CheckKeepOpen() +{ + if (gTimerID) + { + clearTimeout(gTimerID); + gTimerID = null; + } +} + +function onClose() +{ + if (!gFinished) + { + const buttonFlags = (Services.prompt.BUTTON_TITLE_IS_STRING * + Services.prompt.BUTTON_POS_0) + + (Services.prompt.BUTTON_TITLE_CANCEL * + Services.prompt.BUTTON_POS_1); + let button = Services.prompt.confirmEx(window, + GetString("CancelPublishTitle"), + GetString("CancelPublishMessage"), + buttonFlags, + GetString("CancelPublishContinue"), + null, null, null, {}); + if (button == 0) + return false; + } + + if (gTimerID) + { + clearTimeout(gTimerID); + gTimerID = null; + } + + if (!gFinished && gPersistObj) + { + try { + gPersistObj.cancelSave(); + } catch (e) {} + } + SaveWindowLocation(); + + // Tell caller so they can cleanup and restore editability + window.opener.FinishPublishing(); + return true; +} + +function AllowDefaultButton() +{ + gDialog.Close.setAttribute("default","true"); + gAllowEnterKey = true; +} + +function onEnterKey(event) +{ + if (gAllowEnterKey) + return CloseDialog(); + + event.preventDefault(); +} + +function RequestCloseDialog() +{ + // Finish progress messages, settings buttons etc. + SetProgressFinished(null, 0); + + if (!gDialog.KeepOpen.checked) + { + // Leave window open a minimum amount of time + gTimerID = setTimeout(CloseDialog, 3000); + } + + // Set "completed" message if we succeeded + // (Some image files may have failed, + // but we don't abort publishing for that) + if (!gPublishingFailed) + { + gDialog.FinalStatusMessage.value = GetString("PublishCompleted"); + if (gFileNotFound && gTotalFileCount-gSucceededCount) + { + // Show number of files that failed to upload + gStatusMessage = + (GetString("FailedFileMsg").replace(/%x%/,(gTotalFileCount-gSucceededCount))) + .replace(/%total%/,gTotalFileCount); + + SetStatusMessage(gStatusMessage); + } + } +} + +function SetStatusMessage(message) +{ + // Status message is a child of <description> element + // so text can wrap to multiple lines if necessary + if (gDialog.StatusMessage.firstChild) + { + gDialog.StatusMessage.firstChild.data = message; + } + else + { + var textNode = document.createTextNode(message); + if (textNode) + gDialog.StatusMessage.appendChild(textNode); + } + window.sizeToContent(); +} + +function CloseDialog() +{ + SaveWindowLocation(); + window.opener.FinishPublishing(); + try { + window.close(); + } catch (e) {} +} diff --git a/comm/suite/editor/components/dialogs/content/EditorPublishProgress.xhtml b/comm/suite/editor/components/dialogs/content/EditorPublishProgress.xhtml new file mode 100644 index 0000000000..2cf422c8b5 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EditorPublishProgress.xhtml @@ -0,0 +1,66 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorPublishProgress.dtd"> + +<dialog title="" + id="publishProgressDlg" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + buttons="cancel" + buttonlabelclose="&closeButton.label;" + onload="Startup()"> + + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/publishprefs.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EditorPublishProgress.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <groupbox> + <caption><label id="PublishToSite"/></caption> + <label value="&siteUrl.label;"/> + <hbox> + <label class="indent bold" id="PublishUrl"/> + </hbox> + <spacer class="spacer"/> + <grid id="Subdirectories"> + <columns><column/><column/></columns> + <rows> + <row id="DocSubdir"> + <label value="&docSubdir.label;"/> + <label id="docDir"/> + </row> + <row id="OtherSubdir"> + <label value="&otherSubdir.label;"/> + <label id="otherDir"/> + </row> + </rows> + </grid> + <label id="OtherUrl" class="bold" style="margin-left:3em"/> + </groupbox> + <groupbox> + <caption><label value="&fileList.label;"/></caption> + <vbox align="center" style="max-width:30em"> + <label id="FinalStatusMessage" class="bold" value="&status.label;"/> + </vbox> + <description id="StatusMessage" class="wrap" style="max-width:30em; min-height: 1em"/> + <vbox flex="1"> + <listbox id="FileList" rows="1"/> + </vbox> + <hbox align="center" id="failureBox" hidden="true"> + <image class="progressitem" progress="done"/> + <label value="&succeeded.label;"/> + <spacer class="bigspacer"/> + <image class="progressitem" progress="failed"/> + <label value="&failed.label;"/> + </hbox> + </groupbox> + <checkbox id="KeepOpen" label="&keepOpen;" oncommand="CheckKeepOpen();" persist="checked"/> + <separator class="groove"/> +</dialog> diff --git a/comm/suite/editor/components/dialogs/content/EditorPublishSettings.js b/comm/suite/editor/components/dialogs/content/EditorPublishSettings.js new file mode 100644 index 0000000000..01677ae8c0 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EditorPublishSettings.js @@ -0,0 +1,343 @@ +/* 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/. */ + +var gPublishSiteData; +var gPublishDataChanged = false; +var gDefaultSiteIndex = -1; +var gDefaultSiteName; +var gPreviousDefaultSite; +var gPreviousTitle; +var gSettingsChanged = false; +var gSiteDataChanged = false; +var gAddNewSite = false; +var gCurrentSiteIndex = -1; +var gPasswordManagerOn = true; + +// Dialog initialization code + +document.addEventListener("dialogaccept", onAccept); +document.addEventListener("dialogcancel", onCancel); + +function Startup() +{ + if (!GetCurrentEditor()) + { + window.close(); + return; + } + + gDialog.SiteList = document.getElementById("SiteList"); + gDialog.SiteNameInput = document.getElementById("SiteNameInput"); + gDialog.PublishUrlInput = document.getElementById("PublishUrlInput"); + gDialog.BrowseUrlInput = document.getElementById("BrowseUrlInput"); + gDialog.UsernameInput = document.getElementById("UsernameInput"); + gDialog.PasswordInput = document.getElementById("PasswordInput"); + gDialog.SavePassword = document.getElementById("SavePassword"); + gDialog.SetDefaultButton = document.getElementById("SetDefaultButton"); + gDialog.RemoveSiteButton = document.getElementById("RemoveSiteButton"); + gDialog.OkButton = document.documentElement.getButton("accept"); + + gPublishSiteData = GetPublishSiteData(); + gDefaultSiteName = GetDefaultPublishSiteName(); + gPreviousDefaultSite = gDefaultSiteName; + + gPasswordManagerOn = Services.prefs.getBoolPref("signon.rememberSignons"); + gDialog.SavePassword.disabled = !gPasswordManagerOn; + + InitDialog(); + + SetWindowLocation(); +} + +function InitDialog() +{ + // If there's no current site data, start a new item in the Settings panel + if (!gPublishSiteData) + { + AddNewSite(); + } + else + { + FillSiteList(); + + // uncomment next code line if you want preselection of the default + // publishing site + //InitSiteSettings(gDefaultSiteIndex); + + SetTextboxFocus(gDialog.SiteNameInput); + } +} + +function FillSiteList() +{ + // Prevent triggering SelectSiteList() actions + gIsSelecting = true; + ClearListbox(gDialog.SiteList); + gIsSelecting = false; + gDefaultSiteIndex = -1; + + // Fill the site list + var count = gPublishSiteData.length; + for (var i = 0; i < count; i++) + { + var name = gPublishSiteData[i].siteName; + var item = gDialog.SiteList.appendItem(name); + SetPublishItemStyle(item); + if (name == gDefaultSiteName) + gDefaultSiteIndex = i; + } +} + +function SetPublishItemStyle(item) +{ + // Display default site with bold style + if (item) + { + if (item.getAttribute("label") == gDefaultSiteName) + item.setAttribute("class", "bold"); + else + item.removeAttribute("class"); + } +} + +function AddNewSite() +{ + // Save any pending changes locally first + if (!ApplyChanges()) + return; + + // Initialize Setting widgets to none of the selected sites + InitSiteSettings(-1); + gAddNewSite = true; + + SetTextboxFocus(gDialog.SiteNameInput); +} + +function RemoveSite() +{ + if (!gPublishSiteData) + return; + + var index = gDialog.SiteList.selectedIndex; + if (index != -1) + { + let item = gDialog.SiteList.selectedItem; + var nameToRemove = item.getAttribute("label"); + + // Remove one item from site data array + gPublishSiteData.splice(index, 1); + // Remove item from site list + gDialog.SiteList.clearSelection(); + item.remove(); + + // Adjust if we removed last item and reselect a site + if (index >= gPublishSiteData.length) + index--; + InitSiteSettings(index); + + if (nameToRemove == gDefaultSiteName) + { + // Deleting current default -- set to new selected item + // Arbitrary, but what else to do? + SetDefault(); + } + gSiteDataChanged = true; + } +} + +function SetDefault() +{ + if (!gPublishSiteData) + return; + + var index = gDialog.SiteList.selectedIndex; + if (index != -1) + { + gDefaultSiteIndex = index; + gDefaultSiteName = gPublishSiteData[index].siteName; + + // Set bold style on new default + var item = gDialog.SiteList.firstChild; + while (item) + { + SetPublishItemStyle(item); + item = item.nextSibling; + } + } +} + +// Recursion prevention: +// Use when you don't want to trigger ApplyChanges and InitSiteSettings +var gIsSelecting = false; + +function SelectSiteList() +{ + if (gIsSelecting) + return; + + gIsSelecting = true; + var newIndex = gDialog.SiteList.selectedIndex; + + // Save any pending changes locally first + if (!ApplyChanges()) + return; + + InitSiteSettings(newIndex); + + gIsSelecting = false; +} + +// Use this to prevent recursion in SelectSiteList +function SetSelectedSiteIndex(index) +{ + gIsSelecting = true; + gDialog.SiteList.selectedIndex = index; + gIsSelecting = false; +} + +function InitSiteSettings(selectedSiteIndex) +{ + // Index to the site we will need to update if settings changed + gCurrentSiteIndex = selectedSiteIndex; + + SetSelectedSiteIndex(selectedSiteIndex); + var haveData = (gPublishSiteData && selectedSiteIndex != -1); + + gDialog.SiteNameInput.value = haveData ? gPublishSiteData[selectedSiteIndex].siteName : ""; + gDialog.PublishUrlInput.value = haveData ? gPublishSiteData[selectedSiteIndex].publishUrl : ""; + gDialog.BrowseUrlInput.value = haveData ? gPublishSiteData[selectedSiteIndex].browseUrl : ""; + gDialog.UsernameInput.value = haveData ? gPublishSiteData[selectedSiteIndex].username : ""; + + var savePassord = haveData && gPasswordManagerOn; + gDialog.PasswordInput.value = savePassord ? gPublishSiteData[selectedSiteIndex].password : ""; + gDialog.SavePassword.checked = savePassord ? gPublishSiteData[selectedSiteIndex].savePassword : false; + + gDialog.SetDefaultButton.disabled = !haveData; + gDialog.RemoveSiteButton.disabled = !haveData; + gSettingsChanged = false; +} + +function onInputSettings() +{ + // TODO: Save current data during SelectSite1 and compare here + // to detect if real change has occurred? + gSettingsChanged = true; +} + +function ApplyChanges() +{ + if (gSettingsChanged && !UpdateSettings()) + { + // Restore selection to previously current site + SetSelectedSiteIndex(gCurrentSiteIndex); + return false; + } + return true; +} + +function UpdateSettings() +{ + // Validate and add new site + var newName = TrimString(gDialog.SiteNameInput.value); + if (!newName) + { + ShowInputErrorMessage(GetString("MissingSiteNameError"), gDialog.SiteNameInput); + return false; + } + if (PublishSiteNameExists(newName, gPublishSiteData, gCurrentSiteIndex)) + { + ShowInputErrorMessage(GetString("DuplicateSiteNameError").replace(/%name%/, newName)); + SetTextboxFocus(gDialog.SiteNameInput); + return false; + } + + var newUrl = FormatUrlForPublishing(gDialog.PublishUrlInput.value); + if (!newUrl) + { + ShowInputErrorMessage(GetString("MissingPublishUrlError"), gDialog.PublishUrlInput); + return false; + } + + // Start assuming we're updating existing site at gCurrentSiteIndex + var newSiteData = false; + + if (!gPublishSiteData) + { + // First time used - Create the first site profile + gPublishSiteData = new Array(1); + gCurrentSiteIndex = 0; + newSiteData = true; + } + else if (gCurrentSiteIndex == -1) + { + // No currently-selected site, + // must be adding a new site + // Add new data at the end of list + gCurrentSiteIndex = gPublishSiteData.length; + newSiteData = true; + } + + if (newSiteData) + { + // Init new site profile + gPublishSiteData[gCurrentSiteIndex] = {}; + gPublishSiteData[gCurrentSiteIndex].docDir = ""; + gPublishSiteData[gCurrentSiteIndex].otherDir = ""; + gPublishSiteData[gCurrentSiteIndex].dirList = [""]; + gPublishSiteData[gCurrentSiteIndex].previousSiteName = newName; + } + + gPublishSiteData[gCurrentSiteIndex].siteName = newName; + gPublishSiteData[gCurrentSiteIndex].publishUrl = newUrl; + gPublishSiteData[gCurrentSiteIndex].browseUrl = FormatUrlForPublishing(gDialog.BrowseUrlInput.value); + gPublishSiteData[gCurrentSiteIndex].username = TrimString(gDialog.UsernameInput.value); + gPublishSiteData[gCurrentSiteIndex].password= gDialog.PasswordInput.value; + gPublishSiteData[gCurrentSiteIndex].savePassword = gDialog.SavePassword.checked; + + if (gCurrentSiteIndex == gDefaultSiteIndex) + gDefaultSiteName = newName; + + // When adding the very first site, assume that's the default + if (gPublishSiteData.length == 1 && !gDefaultSiteName) + { + gDefaultSiteName = gPublishSiteData[0].siteName; + gDefaultSiteIndex = 0; + } + + FillSiteList(); + + // Select current site in list + SetSelectedSiteIndex(gCurrentSiteIndex); + + // Signal saving data to prefs + gSiteDataChanged = true; + + // Clear current site flags + gSettingsChanged = false; + gAddNewSite = false; + + return true; +} + +function onAccept(event) +{ + // Save any pending changes locally first + if (!ApplyChanges()) { + event.preventDefault(); + return; + } + + if (gSiteDataChanged) + { + // Save all local data to prefs + SavePublishSiteDataToPrefs(gPublishSiteData, gDefaultSiteName); + } + else if (gPreviousDefaultSite != gDefaultSiteName) + { + // only the default site was changed + SetDefaultSiteName(gDefaultSiteName); + } + + SaveWindowLocation(); +} diff --git a/comm/suite/editor/components/dialogs/content/EditorPublishSettings.xhtml b/comm/suite/editor/components/dialogs/content/EditorPublishSettings.xhtml new file mode 100644 index 0000000000..ace6c2fc5d --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EditorPublishSettings.xhtml @@ -0,0 +1,50 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<?xul-overlay href="chrome://editor/content/EditorPublishOverlay.xhtml"?> + +<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorPublish.dtd"> + +<dialog title="&windowTitleSettings.label;" + id="publishSettingsDlg" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload="Startup()" + buttons="accept,cancel"> + + <!-- Methods common to all editor dialogs --> + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EditorPublishSettings.js"/> + <script src="chrome://editor/content/publishprefs.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <hbox id="SettingsPanel"> + <groupbox align="center"> + <label class="header">&publishSites.label;</label> + <!-- XXX: If tree isn't wrapped in vbox, it appears BELOW next vbox --> + <vbox flex="1"> + <listbox rows="4" id="SiteList" flex="1" onselect="SelectSiteList();"/> + </vbox> + <hbox pack="center"> + <vbox> + <button id="NewSiteButton" label="&newSiteButton.label;" + accesskey="&newSiteButton.accesskey;" oncommand="AddNewSite();"/> + <button id="SetDefaultButton" label="&setDefaultButton.label;" + accesskey="&setDefaultButton.accesskey;" oncommand="SetDefault();"/> + <button id="RemoveSiteButton" label="&removeButton.label;" + accesskey="&removeButton.accesskey;" oncommand="RemoveSite();"/> + </vbox> + </hbox> + </groupbox> + <!-- from EditorPublishOverlay.xhtml --> + <vbox id="PublishSettingsInputs"/> + </hbox> + <spacer class="spacer"/> +</dialog> diff --git a/comm/suite/editor/components/dialogs/content/EditorSaveAsCharset.js b/comm/suite/editor/components/dialogs/content/EditorSaveAsCharset.js new file mode 100644 index 0000000000..745a8bfd30 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EditorSaveAsCharset.js @@ -0,0 +1,155 @@ +/* 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/. */ + +document.addEventListener("dialogaccept", onAccept); +document.addEventListener("dialogcancel", onCancel); + +var {CharsetMenu} = ChromeUtils.import("resource://gre/modules/CharsetMenu.jsm"); + +var gCharset=""; +var gTitleWasEdited = false; +var gCharsetWasChanged = false; +var gInsertNewContentType = false; +var gContenttypeElement; +var gInitDone = false; +var gCharsetInfo; + +//Cancel() is in EdDialogCommon.js + +var gCharsetView = { + get rowCount() { return gCharsetInfo.length; }, + selection: null, + getRowProperties: function(index) { return ""; }, + getCellProperties: function(index, column) { return ""; }, + getColumnProperties: function(columm) { return ""; }, + isContainer: function() { return false; }, + isContainerOpen: function() { return false; }, + isContainerEmpty: function() { return true; }, + isSeparator: function() { return false; }, + isSorted: function() { return false; }, + canDrop: function(index, orientation) { return false; }, + drop: function(index, orientation) {}, + getParentIndex: function(index) { return -1; }, + hasNextSibling: function(index, after) { return false; }, + getLevel: function(index) { return 1; }, + getImageSrc: function(index) { return null; }, + getProgressMode: function(index) { return 0; }, + getCellValue: function(index) { return ""; }, + getCellText: function(index) { return gCharsetInfo[index].label; }, + toggleOpenState: function(index) {}, + cycleHeader: function(column) {}, + selectionChanged: function() {}, + cycleCell: function(index, column) {}, + isEditable: function isEditable(index, column) { return false; }, +}; + +function Startup() +{ + var editor = GetCurrentEditor(); + if (!editor) + { + window.close(); + return; + } + + gDialog.TitleInput = document.getElementById("TitleInput"); + gDialog.charsetTree = document.getElementById('CharsetTree'); + gDialog.exportToText = document.getElementById('ExportToText'); + + gContenttypeElement = GetMetaElementByAttribute("http-equiv", "content-type"); + if (!gContenttypeElement && (editor.contentsMIMEType != 'text/plain')) + { + gContenttypeElement = CreateMetaElementWithAttribute("http-equiv", "content-type"); + if (!gContenttypeElement ) + { + window.close(); + return; + } + gInsertNewContentType = true; + } + + try { + gCharset = editor.documentCharacterSet; + } catch (e) {} + + var data = CharsetMenu.getData(); + var charsets = data.pinnedCharsets.concat(data.otherCharsets); + gCharsetInfo = CharsetMenu.getCharsetInfo(charsets.map(info => info.value)); + gDialog.charsetTree.view = gCharsetView; + + InitDialog(); + + // Use the same text as the messagebox for getting title by regular "Save" + document.getElementById("EnterTitleLabel").setAttribute("value",GetString("NeedDocTitle")); + // This is an <HTML> element so it wraps -- append a child textnode + var helpTextParent = document.getElementById("TitleHelp"); + var helpText = document.createTextNode(GetString("DocTitleHelp")); + if (helpTextParent) + helpTextParent.appendChild(helpText); + + // SET FOCUS TO FIRST CONTROL + SetTextboxFocus(gDialog.TitleInput); + + gInitDone = true; + + SetWindowLocation(); +} + + +function InitDialog() +{ + gDialog.TitleInput.value = GetDocumentTitle(); + + var tree = gDialog.charsetTree; + var index = gCharsetInfo.map(info => info.value).indexOf(gCharset); + if (index >= 0) { + tree.view.selection.select(index); + tree.ensureRowIsVisible(index); + } +} + + +function onAccept() +{ + var editor = GetCurrentEditor(); + editor.beginTransaction(); + + if(gCharsetWasChanged) + { + try { + SetMetaElementContent(gContenttypeElement, "text/html; charset=" + gCharset, gInsertNewContentType, true); + editor.documentCharacterSet = gCharset; + } catch (e) {} + } + + editor.endTransaction(); + + if(gTitleWasEdited) + SetDocumentTitle(TrimString(gDialog.TitleInput.value)); + + window.opener.ok = true; + window.opener.exportToText = gDialog.exportToText.checked; + SaveWindowLocation(); +} + + +function SelectCharset() +{ + if(gInitDone) + { + try + { + gCharset = gCharsetInfo[gDialog.charsetTree.currentIndex].value; + if (gCharset) + gCharsetWasChanged = true; + } + catch(e) {} + } +} + + +function TitleChanged() +{ + gTitleWasEdited = true; +} diff --git a/comm/suite/editor/components/dialogs/content/EditorSaveAsCharset.xhtml b/comm/suite/editor/components/dialogs/content/EditorSaveAsCharset.xhtml new file mode 100644 index 0000000000..815a56dfe3 --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/EditorSaveAsCharset.xhtml @@ -0,0 +1,46 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?> +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorSaveAsCharset.dtd"> + +<dialog title="&windowTitle2.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" + onload = "Startup()" + style="width: 32em;"> + + <script src="chrome://editor/content/editorUtilities.js"/> + <script src="chrome://editor/content/EdDialogCommon.js"/> + <script src="chrome://editor/content/EditorSaveAsCharset.js"/> + + <spacer id="location" offsetY="50" persist="offsetX offsetY"/> + + <groupbox> + <hbox class="groupbox-title"> + <label class="header">&documentTitleTitle.label;</label> + </hbox> + <label id="EnterTitleLabel"/> + <textbox id="TitleInput" oninput="TitleChanged();"/> + <description id="TitleHelp" class="wrap" style="width:1em" /> + </groupbox> + + <groupbox flex="1"> + <hbox class="groupbox-title"> + <label class="header">&documentCharsetTitle2.label;</label> + </hbox> + <label value="&documentCharsetDesc2.label;"/> + <tree id="CharsetTree" rows="8" hidecolumnpicker="true" onselect="SelectCharset();"> + <treecols> + <treecol id="CharsetCol" flex="1" hideheader="true"/> + </treecols> + <treechildren/> + </tree> + </groupbox> + + <checkbox id="ExportToText" label="&documentExportToText.label;" /> + <separator class="groove"/> +</dialog> diff --git a/comm/suite/editor/components/dialogs/content/edImage.inc.xhtml b/comm/suite/editor/components/dialogs/content/edImage.inc.xhtml new file mode 100644 index 0000000000..e80fb0457c --- /dev/null +++ b/comm/suite/editor/components/dialogs/content/edImage.inc.xhtml @@ -0,0 +1,248 @@ +# 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/. + + <vbox id="imageLocation"> + <spacer class="spacer"/> + <label control = "srcInput" + value = "&locationEditField.label;" + accesskey="&locationEditField.accessKey;" + tooltiptext="&locationEditField.tooltip;" + /> + <tooltip id="shortenedDataURI"> + <label value="&locationEditField.shortenedDataURI;"/> + </tooltip> + <textbox id="srcInput" oninput="ChangeImageSrc();" tabindex="1" class="uri-element" + tooltiptext="&locationEditField.tooltip;"/> + <hbox id="MakeRelativeHbox"> + <checkbox id="MakeRelativeCheckbox" + for="srcInput" + tabindex="2" + label="&makeUrlRelative.label;" + accesskey="&makeUrlRelative.accessKey;" + oncommand="MakeInputValueRelativeOrAbsolute(this);" + tooltiptext="&makeUrlRelative.tooltip;"/> + <checkbox id="AttachSourceToMail" + hidden="true" + label="&attachImageSource.label;" + accesskey="&attachImageSource.accesskey;" + oncommand="DoAttachSourceCheckbox()"/> + <spacer flex="1"/> + <button id="ChooseFile" + tabindex="3" + oncommand="chooseFile()" + label="&chooseFileButton.label;" + accesskey="&chooseFileButton.accessKey;"/> + </hbox> + <spacer class="spacer"/> + <radiogroup id="altTextRadioGroup" flex="1"> + <grid> + <columns><column/><column flex="1"/></columns> + <rows> + <row align="center"> + <label + style = "margin-left: 26px" + control = "titleInput" + accesskey = "&title.accessKey;" + value ="&title.label;" + tooltiptext="&title.tooltip;" + for = "titleInput"/> + <textbox flex="1" + id = "titleInput" + class = "MinWidth20em" + tooltiptext="&title.tooltip;" + tabindex="4"/> + </row> + <row align="center"> + <radio id="altTextRadio" value="usealt-yes" + label="&altText.label;" + accesskey="&altText.accessKey;" + tooltiptext="&altTextEditField.tooltip;" +#ifndef MOZ_SUITE + persist="selected" +#endif + oncommand = "SetAltTextDisabled(false);" + tabindex="5"/> + <textbox flex="1" + id = "altTextInput" + class = "MinWidth20em" + tooltiptext="&altTextEditField.tooltip;" + oninput = "SetAltTextDisabled(false);" + tabindex="6"/> + </row> + </rows> + </grid> + + <radio id="noAltTextRadio" value="usealt-no" + label="&noAltText.label;" + accesskey = "&noAltText.accessKey;" +#ifndef MOZ_SUITE + persist="selected" +#endif + oncommand = "SetAltTextDisabled(true);"/> + </radiogroup> + </vbox> + + <vbox id="imageDimensions" align="start"> + <spacer class="spacer"/> + <hbox> + <radiogroup id="imgSizeGroup"> + <radio + id = "actualSizeRadio" + label = "&actualSizeRadio.label;" + accesskey = "&actualSizeRadio.accessKey;" + tooltiptext="&actualSizeRadio.tooltip;" + oncommand = "SetActualSize()" + value="actual"/> + <radio + id = "customSizeRadio" + label = "&customSizeRadio.label;" + selected = "true" + accesskey = "&customSizeRadio.accessKey;" + tooltiptext="&customSizeRadio.tooltip;" + oncommand = "doDimensionEnabling();" + value="custom"/> + </radiogroup> + <spacer flex="1"/> + <vbox> + <spacer flex="1"/> + <checkbox id="constrainCheckbox" label="&constrainCheckbox.label;" + accesskey="&constrainCheckbox.accessKey;" + oncommand="ToggleConstrain()" + tooltiptext="&constrainCheckbox.tooltip;"/> + </vbox> + <spacer flex="1"/> + </hbox> + <spacer class="spacer"/> + <grid class="indent"> + <columns><column/><column/><column flex="1"/></columns> + <rows> + <row align="center"> + <label id = "widthLabel" + control = "widthInput" + accesskey = "&widthEditField.accessKey;" + value = "&widthEditField.label;" /> + <textbox + id = "widthInput" + class = "narrow" + oninput = "constrainProportions(this.id, 'heightInput')"/> + <menulist id = "widthUnitsMenulist" + oncommand = "doDimensionEnabling();" /> + <!-- contents are appended by JS --> + </row> + <row align="center"> + <label id = "heightLabel" + control = "heightInput" + accesskey = "&heightEditField.accessKey;" + value = "&heightEditField.label;" /> + <textbox + id = "heightInput" + class = "narrow" + oninput = "constrainProportions(this.id, 'widthInput')"/> + <menulist id = "heightUnitsMenulist" + oncommand = "doDimensionEnabling();" /> + <!-- contents are appended by JS --> + </row> + </rows> + </grid> + <spacer flex="1"/> + </vbox> + + <hbox id="imageAppearance"> + <groupbox> + <hbox class="groupbox-title"> + <label id="spacingLabel" class="header">&spacingBox.label;</label> + </hbox> + <grid> + <columns><column/><column/><column/></columns> + <rows> + <row align="center"> + <label + class = "align-right" + id = "leftrightLabel" + control = "imageleftrightInput" + accesskey = "&leftRightEditField.accessKey;" + value = "&leftRightEditField.label;"/> + <textbox + class = "narrow" + id = "imageleftrightInput" + oninput = "forceInteger(this.id)"/> + <label + id = "leftrighttypeLabel" + value = "&pixelsPopup.value;" /> + </row> + <spacer class="spacer"/> + <row align="center"> + <label + class = "align-right" + id = "topbottomLabel" + control = "imagetopbottomInput" + accesskey = "&topBottomEditField.accessKey;" + value = "&topBottomEditField.label;"/> + <textbox + class = "narrow" + id = "imagetopbottomInput" + oninput = "forceInteger(this.id)"/> + <label id="topbottomtypeLabel" + value="&pixelsPopup.value;" /> + </row> + <spacer class="spacer"/> + <row align="center"> + <label class="align-right" + id="borderLabel" + control="border" + accesskey="&borderEditField.accessKey;" + value="&borderEditField.label;"/> + <textbox + class = "narrow" + id = "border" + oninput = "forceInteger(this.id)"/> + <label id="bordertypeLabel" + value="&pixelsPopup.value;" /> + </row> + </rows> + </grid> + </groupbox> + + <vbox> + <groupbox align="start"> + <hbox class="groupbox-title"> + <label id="alignLabel" class="header">&alignment.label;</label> + </hbox> + <menulist id="alignTypeSelect" class="align-menu"> + <menupopup> + <menuitem class="align-menu menuitem-iconic" + value="top" + label="&topPopup.value;"/> + <menuitem class="align-menu menuitem-iconic" + value="middle" + label="¢erPopup.value;"/> + <menuitem class="align-menu menuitem-iconic" + value="bottom" + label="&bottomPopup.value;"/> + <!-- HTML attribute value is opposite of the button label on purpose --> + <menuitem class="align-menu menuitem-iconic" + value="right" + label="&wrapLeftPopup.value;"/> + <menuitem class="align-menu menuitem-iconic" + value="left" + label="&wrapRightPopup.value;"/> + </menupopup> + </menulist> + </groupbox> + + <groupbox> + <hbox class="groupbox-title"> + <label id="imagemapLabel" class="header">&imagemapBox.label;</label> + </hbox> + <hbox equalsize="always"> + <button id="removeImageMap" + oncommand="removeImageMap()" + accesskey="&removeImageMapButton.accessKey;" + label="&removeImageMapButton.label;" + flex="1"/> + <spacer flex="1"/><!-- remove when we restore Image Map Editor --> + </hbox> + </groupbox> + </vbox> + </hbox> diff --git a/comm/suite/editor/components/dialogs/jar.mn b/comm/suite/editor/components/dialogs/jar.mn new file mode 100644 index 0000000000..705fe0068c --- /dev/null +++ b/comm/suite/editor/components/dialogs/jar.mn @@ -0,0 +1,82 @@ +# 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/. + +comm.jar: + content/editor/EdAdvancedEdit.js (content/EdAdvancedEdit.js) + content/editor/EdAdvancedEdit.xhtml (content/EdAdvancedEdit.xhtml) + content/editor/EdAEAttributes.js (content/EdAEAttributes.js) + content/editor/EdAECSSAttributes.js (content/EdAECSSAttributes.js) + content/editor/EdAEHTMLAttributes.js (content/EdAEHTMLAttributes.js) + content/editor/EdAEJSEAttributes.js (content/EdAEJSEAttributes.js) + content/editor/EdButtonProps.js (content/EdButtonProps.js) + content/editor/EdButtonProps.xhtml (content/EdButtonProps.xhtml) + content/editor/EdColorPicker.js (content/EdColorPicker.js) + content/editor/EdColorPicker.xhtml (content/EdColorPicker.xhtml) + content/editor/EdColorProps.js (content/EdColorProps.js) + content/editor/EdColorProps.xhtml (content/EdColorProps.xhtml) + content/editor/EdConvertToTable.js (content/EdConvertToTable.js) + content/editor/EdConvertToTable.xhtml (content/EdConvertToTable.xhtml) + content/editor/EdDialogCommon.js (content/EdDialogCommon.js) + content/editor/EdDialogTemplate.js (content/EdDialogTemplate.js) + content/editor/EdDialogTemplate.xhtml (content/EdDialogTemplate.xhtml) + content/editor/EdDictionary.js (content/EdDictionary.js) + content/editor/EdDictionary.xhtml (content/EdDictionary.xhtml) + content/editor/EdFieldSetProps.js (content/EdFieldSetProps.js) + content/editor/EdFieldSetProps.xhtml (content/EdFieldSetProps.xhtml) + content/editor/EdFormProps.js (content/EdFormProps.js) + content/editor/EdFormProps.xhtml (content/EdFormProps.xhtml) + content/editor/EdHLineProps.js (content/EdHLineProps.js) + content/editor/EdHLineProps.xhtml (content/EdHLineProps.xhtml) + content/editor/edImage.inc.xhtml (content/edImage.inc.xhtml) + content/editor/EdImageDialog.js (content/EdImageDialog.js) + content/editor/EdImageLinkLoader.js (content/EdImageLinkLoader.js) + content/editor/EdImageProps.js (content/EdImageProps.js) +* content/editor/EdImageProps.xhtml (content/EdImageProps.xhtml) + content/editor/EdInputImage.js (content/EdInputImage.js) +* content/editor/EdInputImage.xhtml (content/EdInputImage.xhtml) + content/editor/EdInputProps.js (content/EdInputProps.js) + content/editor/EdInputProps.xhtml (content/EdInputProps.xhtml) + content/editor/EdInsertChars.js (content/EdInsertChars.js) + content/editor/EdInsertChars.xhtml (content/EdInsertChars.xhtml) + content/editor/EdInsertMath.js (content/EdInsertMath.js) + content/editor/EdInsertMath.xhtml (content/EdInsertMath.xhtml) + content/editor/EdInsertTable.js (content/EdInsertTable.js) + content/editor/EdInsertTable.xhtml (content/EdInsertTable.xhtml) + content/editor/EdInsertTOC.js (content/EdInsertTOC.js) + content/editor/EdInsertTOC.xhtml (content/EdInsertTOC.xhtml) + content/editor/EdInsSrc.js (content/EdInsSrc.js) + content/editor/EdInsSrc.xhtml (content/EdInsSrc.xhtml) + content/editor/EditConflict.js (content/EditConflict.js) + content/editor/EditConflict.xhtml (content/EditConflict.xhtml) + content/editor/EditorPublish.js (content/EditorPublish.js) + content/editor/EditorPublish.xhtml (content/EditorPublish.xhtml) + content/editor/EditorPublishOverlay.xhtml (content/EditorPublishOverlay.xhtml) + content/editor/EditorPublishProgress.js (content/EditorPublishProgress.js) + content/editor/EditorPublishProgress.xhtml (content/EditorPublishProgress.xhtml) + content/editor/EditorPublishSettings.js (content/EditorPublishSettings.js) + content/editor/EditorPublishSettings.xhtml (content/EditorPublishSettings.xhtml) + content/editor/EditorSaveAsCharset.js (content/EditorSaveAsCharset.js) + content/editor/EditorSaveAsCharset.xhtml (content/EditorSaveAsCharset.xhtml) + content/editor/EdLabelProps.js (content/EdLabelProps.js) + content/editor/EdLabelProps.xhtml (content/EdLabelProps.xhtml) + content/editor/EdLinkProps.js (content/EdLinkProps.js) + content/editor/EdLinkProps.xhtml (content/EdLinkProps.xhtml) + content/editor/EdListProps.js (content/EdListProps.js) + content/editor/EdListProps.xhtml (content/EdListProps.xhtml) + content/editor/EdNamedAnchorProps.js (content/EdNamedAnchorProps.js) + content/editor/EdNamedAnchorProps.xhtml (content/EdNamedAnchorProps.xhtml) + content/editor/EdPageProps.js (content/EdPageProps.js) + content/editor/EdPageProps.xhtml (content/EdPageProps.xhtml) + content/editor/EdReplace.js (content/EdReplace.js) + content/editor/EdReplace.xhtml (content/EdReplace.xhtml) + content/editor/EdSelectProps.js (content/EdSelectProps.js) + content/editor/EdSelectProps.xhtml (content/EdSelectProps.xhtml) + content/editor/EdSnapToGrid.js (content/EdSnapToGrid.js) + content/editor/EdSnapToGrid.xhtml (content/EdSnapToGrid.xhtml) + content/editor/EdSpellCheck.js (content/EdSpellCheck.js) + content/editor/EdSpellCheck.xhtml (content/EdSpellCheck.xhtml) + content/editor/EdTableProps.js (content/EdTableProps.js) + content/editor/EdTableProps.xhtml (content/EdTableProps.xhtml) + content/editor/EdTextAreaProps.js (content/EdTextAreaProps.js) + content/editor/EdTextAreaProps.xhtml (content/EdTextAreaProps.xhtml) diff --git a/comm/suite/editor/components/dialogs/moz.build b/comm/suite/editor/components/dialogs/moz.build new file mode 100644 index 0000000000..de5cd1bf81 --- /dev/null +++ b/comm/suite/editor/components/dialogs/moz.build @@ -0,0 +1,6 @@ +# vim: set filetype=python: +# 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/. + +JAR_MANIFESTS += ["jar.mn"] diff --git a/comm/suite/editor/components/moz.build b/comm/suite/editor/components/moz.build new file mode 100644 index 0000000000..cc5a5d1960 --- /dev/null +++ b/comm/suite/editor/components/moz.build @@ -0,0 +1,10 @@ +# vim: set filetype=python: +# 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/. + +DIRS += [ + "dialogs", + "prefs", + "texzilla", +] diff --git a/comm/suite/editor/components/prefs/content/editorPrefsOverlay.xhtml b/comm/suite/editor/components/prefs/content/editorPrefsOverlay.xhtml new file mode 100644 index 0000000000..41112036ca --- /dev/null +++ b/comm/suite/editor/components/prefs/content/editorPrefsOverlay.xhtml @@ -0,0 +1,50 @@ +<?xml version="1.0"?> +<!-- 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/. --> + + +<!DOCTYPE overlay [ +<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" > +%brandDTD; +<!ENTITY % editorPrefsOverlayDTD SYSTEM "chrome://editor/locale/editorPrefsOverlay.dtd" > +%editorPrefsOverlayDTD; +]> + +<overlay id="editorPrefsOverlay" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"> + + <preferences id="appearance_preferences"> + <preference id="general.startup.editor" + name="general.startup.editor" + type="bool"/> + </preferences> + + <!-- editor startup toggle --> + <groupbox id="generalStartupPreferences"> + <checkbox id="generalStartupEditor" + insertafter="generalStartupMail,generalStartupBrowser" + label="&editorCheck.label;" + accesskey="&editorCheck.accesskey;" + preference="general.startup.editor"/> + </groupbox> + <!-- category tree entries for editor --> + <treechildren id="prefsPanelChildren"> + <treeitem container="true" + id="composerItem" + insertbefore="securityItem" + label="&compose.label;" + prefpane="composer_pane" + url="chrome://editor/content/pref-composer.xhtml" + helpTopic="composer_prefs_general"> + <treechildren id="composerChildren"> + <treeitem id="editingItem" + label="&editing.label;" + prefpane="editing_pane" + url="chrome://editor/content/pref-editing.xhtml" + helpTopic="composer_prefs_newpage"/> + </treechildren> + </treeitem> + </treechildren> + +</overlay> diff --git a/comm/suite/editor/components/prefs/content/pref-composer.xhtml b/comm/suite/editor/components/prefs/content/pref-composer.xhtml new file mode 100644 index 0000000000..79b4a1514c --- /dev/null +++ b/comm/suite/editor/components/prefs/content/pref-composer.xhtml @@ -0,0 +1,84 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +<!DOCTYPE overlay SYSTEM "chrome://editor/locale/pref-composer.dtd"> + +<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"> + <prefpane id="composer_pane" label="&pref.composer.title;"> + + <preferences id="composer_preferences"> + <preference id="editor.history.url_maximum" + name="editor.history.url_maximum" + type="int"/> + <preference id="editor.prettyprint" + name="editor.prettyprint" + type="bool" + inverted="true"/> + <preference id="editor.save_associated_files" + name="editor.save_associated_files" + type="bool"/> + <preference id="editor.always_show_publish_dialog" + name="editor.always_show_publish_dialog" + type="bool"/> + <preference id="editor.table.maintain_structure" + name="editor.table.maintain_structure" + type="bool"/> + <preference id="editor.use_css" + name="editor.use_css" + type="bool"/> + <preference id="editor.CR_creates_new_p" + name="editor.CR_creates_new_p" + type="bool"/> + </preferences> + + <!-- Recent files menu --> + <groupbox> + <label class="header">&recentFiles.title;</label> + <hbox align="center"> + <label value="&documentsInMenu.label;" + accesskey="&documentsInMenu.accesskey;" + control="recentFiles"/> + <html:input id="recentFiles" type="number" class="size3" + min="0" max="99" value="10" + preference="editor.history.url_maximum"/> + </hbox> + </groupbox> + + <!-- HTML formatting on output --> + <groupbox> + <label class="header">&savingFiles.title;</label> + <checkbox id="preserveFormatting" + label="&preserveExisting.label;" + accesskey="&preserveExisting.accesskey;" + tooltiptext="&preserveExisting.tooltip;" + preference="editor.prettyprint"/> + <checkbox id="saveAssociatedFiles" + label="&saveAssociatedFiles.label;" + accesskey="&saveAssociatedFiles.accesskey;" + preference="editor.save_associated_files"/> + <checkbox id="showPublishDialog" + label="&showPublishDialog.label;" + accesskey="&showPublishDialog.accesskey;" + preference="editor.always_show_publish_dialog"/> + </groupbox> + + <groupbox align="start"> + <label class="header">&composerEditing.label;</label> + <checkbox id="maintainTableStructure" + label="&maintainStructure.label;" + accesskey="&maintainStructure.accesskey;" + tooltiptext="&maintainStructure.tooltip;" + preference="editor.table.maintain_structure"/> + <checkbox id="useCSS" + label="&useCSS.label;" + accesskey="&useCSS.accesskey;" + preference="editor.use_css"/> + <checkbox id="crInPCreatesNewP" + label="&crInPCreatesNewP.label;" + accesskey="&crInPCreatesNewP.accesskey;" + preference="editor.CR_creates_new_p"/> + </groupbox> + </prefpane> +</overlay> diff --git a/comm/suite/editor/components/prefs/content/pref-editing.js b/comm/suite/editor/components/prefs/content/pref-editing.js new file mode 100644 index 0000000000..61f4c1edef --- /dev/null +++ b/comm/suite/editor/components/prefs/content/pref-editing.js @@ -0,0 +1,187 @@ +/* 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/. */ + +const browserPrefsObserver = +{ + observe: function(aSubject, aTopic, aData) + { + if (aTopic != "nsPref:changed" || document.getElementById("editor.use_custom_colors").value) + return; + + switch (aData) + { + case "browser.anchor_color": + SetColorPreview("linkText", Services.prefs.getCharPref(aData)); + break; + case "browser.active_color": + SetColorPreview("activeLinkText", Services.prefs.getCharPref(aData)); + break; + case "browser.visited_color": + SetColorPreview("visitedLinkText", Services.prefs.getCharPref(aData)); + break; + default: + SetBgAndFgColors(Services.prefs.getBoolPref("browser.display.use_system_colors")) + } + } +}; + +function Startup() +{ + // Add browser prefs observers + Services.prefs.addObserver("browser.display.use_system_colors", browserPrefsObserver); + Services.prefs.addObserver("browser.display.foreground_color", browserPrefsObserver); + Services.prefs.addObserver("browser.display.background_color", browserPrefsObserver); + Services.prefs.addObserver("browser.anchor_color", browserPrefsObserver); + Services.prefs.addObserver("browser.active_color", browserPrefsObserver); + Services.prefs.addObserver("browser.visited_color", browserPrefsObserver); + + // Add event listener so we can remove our observers + window.addEventListener("unload", WindowOnUnload, {capture: false, once: true}); + UpdateDependent(document.getElementById("editor.use_custom_colors").value); +} + +function GetColorAndUpdatePref(aType, aButtonID) +{ + // Don't allow a blank color, i.e., using the "default" + var colorObj = { NoDefault:true, Type:"", TextColor:0, PageColor:0, Cancel:false }; + var preference = document.getElementById("editor." + aButtonID + "_color"); + + if (aButtonID == "background") + colorObj.PageColor = preference.value; + else + colorObj.TextColor = preference.value; + + colorObj.Type = aType; + + window.openDialog("chrome://editor/content/EdColorPicker.xhtml", "_blank", "chrome,close,titlebar,modal", "", colorObj); + + // User canceled the dialog + if (colorObj.Cancel) + return; + + // Update preference with picked color + if (aType == "Page") + preference.value = colorObj.BackgroundColor; + else + preference.value = colorObj.TextColor; +} + +function UpdateDependent(aCustomEnabled) +{ + ToggleElements(aCustomEnabled); + + if (aCustomEnabled) + { // Set current editor colors on preview and buttons + SetColors("textCW", "normalText", document.getElementById("editor.text_color").value); + SetColors("linkCW", "linkText", document.getElementById("editor.link_color").value); + SetColors("activeCW", "activeLinkText", document.getElementById("editor.active_link_color").value); + SetColors("visitedCW", "visitedLinkText", document.getElementById("editor.followed_link_color").value); + SetColors("backgroundCW", "ColorPreview", document.getElementById("editor.background_color").value); + } + else + { // Set current browser colors on preview + SetBgAndFgColors(Services.prefs.getBoolPref("browser.display.use_system_colors")); + SetColorPreview("linkText", Services.prefs.getCharPref("browser.anchor_color")); + SetColorPreview("activeLinkText", Services.prefs.getCharPref("browser.active_color")); + SetColorPreview("visitedLinkText", Services.prefs.getCharPref("browser.visited_color")); + } +} + +function ToggleElements(aCustomEnabled) +{ + var buttons = document.getElementById("color-rows").getElementsByTagName("button"); + + for (var i = 0; i < buttons.length; i++) + { + let isLocked = CheckLocked(buttons[i].id); + buttons[i].disabled = !aCustomEnabled || isLocked; + buttons[i].previousSibling.disabled = !aCustomEnabled || isLocked; + buttons[i].firstChild.setAttribute("default", !aCustomEnabled || isLocked); + } +} + +function CheckLocked(aButtonID) +{ + return document.getElementById("editor." + aButtonID + "_color").locked; +} + +// Updates preview and button color when a editor color pref change +function UpdateColors(aColorWellID, aPreviewID, aColor) +{ + // Only show editor colors from prefs if we're in custom mode + if (!document.getElementById("editor.use_custom_colors").value) + return; + + SetColors(aColorWellID, aPreviewID, aColor) +} + +function SetColors(aColorWellID, aPreviewID, aColor) +{ + SetColorWell(aColorWellID, aColor); + SetColorPreview(aPreviewID, aColor); +} + +function SetColorWell(aColorWellID, aColor) +{ + document.getElementById(aColorWellID).style.backgroundColor = aColor; +} + +function SetColorPreview(aPreviewID, aColor) +{ + if (aPreviewID == "ColorPreview") + document.getElementById(aPreviewID).style.backgroundColor = aColor; + else + document.getElementById(aPreviewID).style.color = aColor; +} + +function UpdateBgImagePreview(aImage) +{ + var colorPreview = document.getElementById("ColorPreview"); + colorPreview.style.backgroundImage = aImage && "url(" + aImage + ")"; +} + +// Sets browser background/foreground colors +function SetBgAndFgColors(aSysPrefEnabled) +{ + if (aSysPrefEnabled) + { // Use system colors + SetColorPreview("normalText", "windowtext"); + SetColorPreview("ColorPreview", "window"); + } + else + { + SetColorPreview("normalText", Services.prefs.getCharPref("browser.display.foreground_color")); + SetColorPreview("ColorPreview", Services.prefs.getCharPref("browser.display.background_color")); + } +} + +function ChooseImageFile() +{ + const nsIFilePicker = Ci.nsIFilePicker; + var fp = Cc["@mozilla.org/filepicker;1"] + .createInstance(nsIFilePicker); + var editorBundle = document.getElementById("bundle_editor"); + var title = editorBundle.getString("SelectImageFile"); + fp.init(window, title, nsIFilePicker.modeOpen); + fp.appendFilters(nsIFilePicker.filterImages); + fp.open(rv => { + if (rv != nsIFilePicker.returnOK || !fp.file) { + return; + } + document.getElementById("editor.default_background_image").value = fp.fileURL.spec; + let textbox = document.getElementById("backgroundImageInput"); + textbox.focus(); + textbox.select(); + }); +} + +function WindowOnUnload() +{ + Services.prefs.removeObserver("browser.display.use_system_colors", browserPrefsObserver, false); + Services.prefs.removeObserver("browser.display.foreground_color", browserPrefsObserver, false); + Services.prefs.removeObserver("browser.display.background_color", browserPrefsObserver, false); + Services.prefs.removeObserver("browser.anchor_color", browserPrefsObserver, false); + Services.prefs.removeObserver("browser.active_color", browserPrefsObserver, false); + Services.prefs.removeObserver("browser.visited_color", browserPrefsObserver, false); +} diff --git a/comm/suite/editor/components/prefs/content/pref-editing.xhtml b/comm/suite/editor/components/prefs/content/pref-editing.xhtml new file mode 100644 index 0000000000..140ff320ce --- /dev/null +++ b/comm/suite/editor/components/prefs/content/pref-editing.xhtml @@ -0,0 +1,181 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?> + +<!DOCTYPE overlay SYSTEM "chrome://editor/locale/pref-editing.dtd"> + +<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"> + <prefpane id="editing_pane" + label="&pref.editing.title;" + script="chrome://editor/content/pref-editing.js"> + + <preferences id="editing_preferences"> + <preference id="editor.author" + name="editor.author" + type="string"/> + <preference id="editor.use_custom_colors" + name="editor.use_custom_colors" + type="bool" + onchange="UpdateDependent(this.value);"/> + <preference id="editor.text_color" + name="editor.text_color" + type="string" + onchange="UpdateColors('textCW', 'normalText', this.value);"/> + <preference id="editor.link_color" + name="editor.link_color" + type="string" + onchange="UpdateColors('linkCW', 'linkText', this.value);"/> + <preference id="editor.active_link_color" + name="editor.active_link_color" + type="string" + onchange="UpdateColors('activeCW', 'activeLinkText', this.value);"/> + <preference id="editor.followed_link_color" + name="editor.followed_link_color" + type="string" + onchange="UpdateColors('visitedCW', 'visitedLinkText', this.value);"/> + <preference id="editor.background_color" + name="editor.background_color" + type="string" + onchange="UpdateColors('backgroundCW', 'ColorPreview', this.value);"/> + <preference id="editor.default_background_image" + name="editor.default_background_image" + type="string" + onchange="UpdateBgImagePreview(this.value);"/> + </preferences> + + <stringbundle id="bundle_editor" + src="chrome://editor/locale/editor.properties"/> + + <vbox> + <label value="&authorName.label;" + accesskey="&authorName.accesskey;" + control="editorAuthor"> + </label> + <hbox> + <textbox id="editorAuthor" + flex="1" + preference="editor.author"/> + <spacer flex="1"/> + </hbox> + </vbox> + <spacer class="smallspacer"/> + <groupbox align="start"> + <label class="header">&pageColorHeader;</label> + <radiogroup id="useCustomColors" + preference="editor.use_custom_colors"> + <radio id="defaultColorsRadio" + value="false" + label="&defaultColors.label;" + accesskey="&defaultColors.accesskey;"/> + <radio id="customColorsRadio" + value="true" + label="&customColors.label;" + accesskey="&customColors.accesskey;"/> + </radiogroup> + <hbox class="indent"> + <grid> + <columns><column/><column/></columns> + <rows id="color-rows"> + <row align="center"> + <label id="textLabel" + value="&normalText.label;&colon.character;" + accesskey="&normalText.accesskey;" + control="text"/> + <button id="text" + class="color-button" + oncommand="GetColorAndUpdatePref('Text', 'text');"> + <spacer id="textCW" class="color-well"/> + </button> + </row> + <row align="center"> + <label id="linkLabel" + value="&linkText.label;&colon.character;" + accesskey="&linkText.accesskey;" + control="link"/> + <button id="link" + class="color-button" + oncommand="GetColorAndUpdatePref('Link', 'link');"> + <spacer id="linkCW" class="color-well"/> + </button> + </row> + <row align="center"> + <label id="activeLinkLabel" + value="&activeLinkText.label;&colon.character;" + accesskey="&activeLinkText.accesskey;" + control="active_link"/> + <button id="active_link" + class="color-button" + oncommand="GetColorAndUpdatePref('ActiveLink', 'active_link');"> + <spacer id="activeCW" class="color-well"/> + </button> + </row> + <row align="center"> + <label id="visitedLinkLabel" + value ="&visitedLinkText.label;&colon.character;" + accesskey="&visitedLinkText.accesskey;" + control="followed_link"/> + <button id="followed_link" + class="color-button" + oncommand="GetColorAndUpdatePref('VisitedLink', 'followed_link');"> + <spacer id="visitedCW" class="color-well"/> + </button> + </row> + <row align="center"> + <label id="backgroundLabel" + value="&background.label;" + accesskey="&background.accesskey;" + control="background"/> + <button id="background" + class="color-button" + oncommand="GetColorAndUpdatePref('Page', 'background');"> + <spacer id="backgroundCW" class="color-well"/> + </button> + </row> + </rows> + </grid> + <vbox id="ColorPreview" + flex="1"> + <spacer flex="1"/> + <label id="normalText" + class="larger" + value="&normalText.label;"/> + <spacer flex="1"/> + <label id="linkText" + class="larger" + value="&linkText.label;"/> + <spacer flex="1"/> + <label id="activeLinkText" + class="larger" + value="&activeLinkText.label;"/> + <spacer flex="1"/> + <label id="visitedLinkText" + class="larger" + value="&visitedLinkText.label;"/> + <spacer flex="1"/> + </vbox> + <spacer flex="1"/> + </hbox> + <spacer class="spacer"/> + <label id="backgroundImageLabel" + value="&backgroundImage.label;" + accesskey="&backgroundImage.accesskey;" + control="backgroundImageInput"> + </label> + <hbox align="center"> + <textbox id="backgroundImageInput" + class="uri-element" + preference="editor.default_background_image" + style="min-width: 23em;" + flex="1"/> + <button label="&chooseFile.label;" + accesskey="&chooseFile.accesskey;" + oncommand="ChooseImageFile();"> + <observes element="backgroundImageInput" attribute="disabled"/> + </button> + </hbox> + </groupbox> + </prefpane> +</overlay> diff --git a/comm/suite/editor/components/prefs/jar.mn b/comm/suite/editor/components/prefs/jar.mn new file mode 100644 index 0000000000..308da7340c --- /dev/null +++ b/comm/suite/editor/components/prefs/jar.mn @@ -0,0 +1,11 @@ +# 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/. + +comm.jar: +% overlay chrome://communicator/content/pref/preferences.xhtml chrome://editor/content/editorPrefsOverlay.xhtml +% overlay chrome://communicator/content/pref/pref-appearance.xhtml chrome://editor/content/editorPrefsOverlay.xhtml + content/editor/editorPrefsOverlay.xhtml (content/editorPrefsOverlay.xhtml) + content/editor/pref-composer.xhtml (content/pref-composer.xhtml) + content/editor/pref-editing.js (content/pref-editing.js) + content/editor/pref-editing.xhtml (content/pref-editing.xhtml) diff --git a/comm/suite/editor/components/prefs/moz.build b/comm/suite/editor/components/prefs/moz.build new file mode 100644 index 0000000000..de5cd1bf81 --- /dev/null +++ b/comm/suite/editor/components/prefs/moz.build @@ -0,0 +1,6 @@ +# vim: set filetype=python: +# 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/. + +JAR_MANIFESTS += ["jar.mn"] diff --git a/comm/suite/editor/components/texzilla/content/TeXZilla.js b/comm/suite/editor/components/texzilla/content/TeXZilla.js new file mode 100644 index 0000000000..0f8e3e5b29 --- /dev/null +++ b/comm/suite/editor/components/texzilla/content/TeXZilla.js @@ -0,0 +1,339 @@ +/* THIS IS A GENERATED FILE. DO NOT EDIT THIS DIRECTLY. */ +/* 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/. */ + +(function() { +"using strict"; +var nb=void 0,tb=!0,xb=null,yb=!1,zb=function(){function c(b,a,c){var $a;c=c||{};for($a=b.length;$a--;c[b[$a]]=a);return c}function Fb(b){return b.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function sb(b){b="negativeveryverythinmathspace negativeverythinmathspace negativemediummathspace negativethickmathspace negativeverythickmathspace negativeveryverythickmathspace veryverythinmathspace verythinmathspace thinmathspace mediummathspace thickmathspace verythickmathspace veryverythickmathspace".split(" ").indexOf(b); +return(-1===b?0:b-6)/18}function tc(b){b=b.trim();var a=/(-?[0-9]*(?:[0-9]\.?|\.[0-9])[0-9]*)(e[mx]|in|cm|mm|p[xtc]|%)?/.exec(b);return a?(a[1]=parseFloat(a[1]),a[2]||(a[1]*=100,a[2]="%"),{i:a[1],k:a[2]}):{i:sb(b),k:"em"}}function Pb(b){var a="<"+b.tag,c;for(c in b.attributes)b.attributes[c]!==nb&&(a+=" "+c+'="'+b.attributes[c]+'"');b.content?(a+=">",Array.isArray(b.content)?b.content.forEach(function(b){a+=Pb(b)}):a+=b.content,a+="</"+b.tag+">"):a+="/>";return a}function e(b,a,c){return{tag:b,content:a, +attributes:c}}function ab(b,a,c){return e("mo",Fb(b),{lspace:a!==nb?a+"em":nb,rspace:c!==nb?c+"em":nb})}function Ub(b,a){return e("mi",Fb(b),a?{mathvariant:"normal"}:nb)}function Gb(b){return e("mspace",xb,{width:b+"em"})}function uc(b,a){var c="bold italic bold-italic script bold-script fraktur double-struck bold-fraktur sans-serif bold-sans-serif sans-serif-italic sans-serif-bold-italic monospace initial tailed looped stretched".split(" ").indexOf(a);if(930==b)return b;if(988==b)return 0==c?120778: +b;if(989==b)return 0==c?120779:b;if(305==b)return 1==c?120484:b;if(567==b)return 1==c?120485:b;var $a;if(65<=b&&90>=b||97<=b&&122>=b){if(12<c)return b;c=(90>=b?b-65:26+b-97)+119808+52*c;$a={119893:8462,119965:8492,119968:8496,119969:8497,119971:8459,119972:8464,119975:8466,119976:8499,119981:8475,119994:8495,119996:8458,120004:8500,120070:8493,120075:8460,120076:8465,120085:8476,120093:8488,120122:8450,120127:8461,120133:8469,120135:8473,120136:8474,120137:8477,120145:8484};return $a[c]?$a[c]:c}if(48<= +b&&57>=b){switch(c){case 0:c=0;break;case 6:c=1;break;case 8:c=2;break;case 9:c=3;break;case 12:c=4;break;default:return b}return b-48+10*c+120782}if(1536<=b&&1791>=b){switch(c){case 13:$a={1576:126497,1578:126517,1579:126518,1580:126498,1581:126503,1582:126519,1587:126510,1588:126516,1589:126513,1590:126521,1593:126511,1594:126523,1601:126512,1602:126514,1603:126506,1604:126507,1605:126508,1606:126509,1607:126500,1610:126505};break;case 14:$a={1580:126530,1581:126535,1582:126551,1587:126542,1588:126548, +1589:126545,1590:126553,1593:126543,1594:126555,1602:126546,1604:126539,1606:126541,1610:126537,1647:126559,1722:126557};break;case 16:$a={1576:126561,1578:126581,1579:126582,1580:126562,1581:126567,1582:126583,1587:126574,1588:126580,1589:126577,1590:126585,1591:126568,1592:126586,1593:126575,1594:126587,1601:126576,1602:126578,1603:126570,1605:126572,1606:126573,1607:126564,1610:126569,1646:126588,1697:126590};break;case 15:$a={1575:126592,1576:126593,1578:126613,1579:126614,1580:126594,1581:126599, +1582:126615,1583:126595,1584:126616,1585:126611,1586:126598,1587:126606,1588:126612,1589:126609,1590:126617,1591:126600,1592:126618,1593:126607,1594:126619,1601:126608,1602:126610,1604:126603,1605:126604,1606:126605,1607:126596,1608:126597,1610:126601};break;case 6:$a={1576:126625,1578:126645,1579:126646,1580:126626,1581:126631,1582:126647,1583:126627,1584:126648,1585:126643,1586:126630,1587:126638,1588:126644,1589:126641,1590:126649,1591:126632,1592:126650,1593:126639,1594:126651,1601:126640,1602:126642, +1604:126635,1605:126636,1606:126637,1608:126629,1610:126633};break;default:return b}return $a[b]?$a[b]:b}if(913<=b&&937>=b)$a=b-913;else if(945<=b&&969>=b)$a=26+b-945;else switch(b){case 1012:$a=17;break;case 8711:$a=25;break;case 8706:$a=51;break;case 1013:$a=52;break;case 977:$a=53;break;case 1008:$a=54;break;case 981:$a=55;break;case 1009:$a=56;break;case 982:$a=57;break;default:return b}switch(c){case 0:c=0;break;case 1:c=1;break;case 2:c=2;break;case 9:c=3;break;case 11:c=4;break;default:return b}return $a+ +120488+58*c}function vc(b,a){var c=tb,$a;for($a in a)-1!==["mathcolor","mathbackground","mathvariant"].indexOf($a)?"mathvariant"!==$a&&1!=b.length?c=yb:b.forEach(function(b){if(-1!==["mi","mn","mo","mtext","ms"].indexOf(b.tag)){if(b.attributes||(b.attributes={}),!b.attributes[$a])if("mathvariant"===$a){var d;if(!(d="normal"!==a[$a])){if("mi"!==b.tag)d=yb;else{d=b.content;var e=d.codePointAt(0);d=1===d.length&&65535>=e||2===d.length&&65535<e}d=!d}if(d){if(d=a[$a],"normal"!==d){for(var e=b.content, +g="",m=0;m<e.length;m++){var s=e.codePointAt(m);65535<s?(g+=e[m],m++,g+=e[m]):g+=String.fromCodePoint(uc(s,d))}b.content=g}}else b.attributes[$a]=a[$a]}else b.attributes[$a]=a[$a]}else c=yb}):c=yb;return c}function db(b,a,c){a=a||"mrow";if("mstyle"===a){if(1==b.length&&"mrow"===b[0].tag&&!b[0].attributes)return db(b[0].content,a,c);if(vc(b,c))return db(b)}return 1==b.length&&"mrow"===a&&!c?b[0]:e(a,b,c)}function Ib(b,a,c,$a){return e("math",[e("semantics",[db(b),e("annotation",Fb($a),{encoding:"TeX"})])], +{xmlns:Qb,display:a?"block":nb,dir:c?"rtl":nb})}function Vb(b){if(!b||b.namespaceURI!==Qb)return xb;if("semantics"===b.tagName)for(b=b.firstElementChild;b;b=b.nextElementSibling){if(b.namespaceURI===Qb&&"annotation"===b.localName&&-1!==wc.indexOf(b.getAttribute("encoding")))return b.textContent}else if(1===b.childElementCount)return Vb(b.firstElementChild);return xb}function xc(b){for(var a="",c,$a,e=0;e<b.length;e++)c=b.charCodeAt(e),128>c?a+=b.charAt(e):55296<=c&&56319>=c?(e++,$a=b.charCodeAt(e), +a+="&#x"+(1024*(c-55296)+$a-56320+65536).toString(16)+";"):a+="&#x"+c.toString(16)+";";return a}function Rb(){this.e={}}var Wb=[1,4],Xb=[1,6],Yb=[1,7],Zb=[1,8],$b=[1,9],Ab=[68,195,198,200,202,204],m=[1,27],s=[1,124],v=[1,52],x=[1,48],h=[1,28],q=[1,29],p=[1,30],y=[1,31],f=[1,32],u=[1,33],n=[1,34],k=[1,35],r=[1,37],t=[1,38],l=[1,39],w=[1,40],z=[1,41],A=[1,42],B=[1,43],C=[1,44],D=[1,45],E=[1,46],F=[1,47],G=[1,49],H=[1,50],I=[1,51],J=[1,53],K=[1,54],L=[1,55],M=[1,56],N=[1,57],O=[1,58],P=[1,59],Q=[1,60], +R=[1,61],S=[1,62],T=[1,63],U=[1,64],V=[1,65],W=[1,66],X=[1,67],Y=[1,68],Z=[1,69],$=[1,70],aa=[1,71],ba=[1,72],ca=[1,73],da=[1,74],ea=[1,75],fa=[1,76],ga=[1,77],ha=[1,78],ia=[1,79],ja=[1,80],ka=[1,81],la=[1,82],ma=[1,83],na=[1,84],oa=[1,85],pa=[1,86],qa=[1,87],ra=[1,88],sa=[1,89],ta=[1,90],ua=[1,91],va=[1,92],wa=[1,93],xa=[1,94],ya=[1,95],za=[1,96],Aa=[1,97],Ba=[1,98],Ca=[1,99],Da=[1,100],Ea=[1,101],Fa=[1,102],Ga=[1,103],Ha=[1,104],Ia=[1,105],Ja=[1,106],Ka=[1,107],eb=[1,24],La=[1,108],Ma=[1,109],Na= +[1,110],Oa=[1,111],Pa=[1,112],Qa=[1,113],Ra=[1,114],Sa=[1,115],Ta=[1,116],Ua=[1,117],Va=[1,118],Wa=[1,119],Xa=[1,120],Ya=[1,121],bb=[1,122],cb=[1,123],fb=[1,16],gb=[1,17],hb=[1,18],ib=[1,19],jb=[1,20],kb=[1,21],lb=[1,22],ac=[6,10,53,64,65,66,144,146,148,150,152,154,156,158,160,162,164,189,192,199,201,203,205],Db=[8,49,50,51,56,57,58,59,60,61,62,63,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113, +114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,139,141,142,145,147,149,151,153,155,157,159,161,163,165,166,173,174,179,180,181,182,183,184,185],mb=[1,134],ub=[6,8,10,49,50,51,53,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,139, +141,142,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,173,174,189,192,199,201,203,205],Za=[1,137],g=[6,8,10,49,50,51,53,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,138,139,141,142,144,145,146,147,148,149,150,151,152,153,154, +155,156,157,158,159,160,161,162,163,164,165,166,169,170,171,173,174,189,192,199,201,203,205],Sb=[1,161],pb=[2,197],qb=[1,217],vb=[1,214],Eb=[6,8,10,49,50,51,53,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,139,141,142,144,145,146,147,148,149,150,151,152,153,154,155,156, +157,158,159,160,161,162,163,164,165,166,169,170,173,174,189,192,199,201,203,205],bc=[1,241],Bb=[1,243],Cb=[1,244],Mb=[1,259],cc=[4,8],dc=[1,275],ec=[8,49,50,51,56,57,58,59,60,61,62,63,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,138,139,141,142,145,147,149,151,153,155,157,159,161,163,165,166],wb=[1,286], +Nb=[10,144,146,148,150,152,154,156,158,160,162,164,192],fc=[1,288],Jb=[10,144,146,148,150,152,154,156,158,160,162,164,189,192],gc=[164,189,192],Tb=[10,189,192],Kb=[1,343],Lb=[1,344],hc=[1,352],ic=[1,353],jc=[4,8,49,50,51,56,57,58,59,60,61,62,63,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,139,141,142, +145,147,149,151,153,155,157,159,161,163,165,166],Ob=[10,21,23],Hb=[10,21,23,25,27],kc=[1,399],lc=[1,400],mc=[1,401],nc=[1,402],oc=[1,403],pc=[1,404],qc=[1,405],rc=[1,406],sc=[10,19,21,23,25,27,29,31,33,35,37,39,41],ob=[10,19,21,23,29,31,33,35,37,39,41],rb={trace:function(){},e:{},la:{error:2,textOptArg:3,"[":4,TEXTOPTARG:5,"]":6,textArg:7,"{":8,TEXTARG:9,"}":10,lengthOptArg:11,lengthArg:12,attrOptArg:13,attrArg:14,tokenContent:15,arrayAlign:16,columnAlign:17,collayout:18,COLLAYOUT:19,colalign:20, +COLALIGN:21,rowalign:22,ROWALIGN:23,rowspan:24,ROWSPAN:25,colspan:26,COLSPAN:27,align:28,ALIGN:29,eqrows:30,EQROWS:31,eqcols:32,EQCOLS:33,rowlines:34,ROWLINES:35,collines:36,COLLINES:37,frame:38,FRAME:39,padding:40,PADDING:41,cellopt:42,celloptList:43,rowopt:44,arrayopt:45,arrayoptList:46,rowoptList:47,left:48,LEFT:49,OPFS:50,".":51,right:52,RIGHT:53,closedTerm:54,styledExpression:55,BIG:56,BBIG:57,BIGG:58,BBIGG:59,BIGL:60,BBIGL:61,BIGGL:62,BBIGGL:63,TEXATOP:64,TEXOVER:65,TEXCHOOSE:66,NUM:67,TEXT:68, +A:69,AILL:70,AIUL:71,AILG:72,AIUG:73,F:74,MI:75,MN:76,MO:77,OP:78,OPS:79,OPAS:80,MS:81,MTEXT:82,HIGH_SURROGATE:83,LOW_SURROGATE:84,BMP_CHARACTER:85,OPERATORNAME:86,MATHOP:87,MATHBIN:88,MATHREL:89,FRAC:90,ROOT:91,SQRT:92,UNDERSET:93,OVERSET:94,UNDEROVERSET:95,XARROW:96,MATHRLAP:97,MATHLLAP:98,MATHCLAP:99,PHANTOM:100,TFRAC:101,BINOM:102,TBINOM:103,PMOD:104,UNDERBRACE:105,UNDERLINE:106,OVERBRACE:107,ACCENT:108,ACCENTNS:109,BOXED:110,SLASH:111,QUAD:112,QQUAD:113,NEGSPACE:114,NEGMEDSPACE:115,NEGTHICKSPACE:116, +THINSPACE:117,MEDSPACE:118,THICKSPACE:119,SPACE:120,MATHRAISEBOX:121,MATHBB:122,MATHBF:123,MATHBIT:124,MATHSCR:125,MATHBSCR:126,MATHSF:127,MATHFRAK:128,MATHIT:129,MATHTT:130,MATHRM:131,HREF:132,STATUSLINE:133,TOOLTIP:134,TOGGLE:135,BTOGGLE:136,closedTermList:137,ETOGGLE:138,TENSOR:139,subsupList:140,MULTI:141,BMATRIX:142,tableRowList:143,EMATRIX:144,BGATHERED:145,EGATHERED:146,BPMATRIX:147,EPMATRIX:148,BBMATRIX:149,EBMATRIX:150,BVMATRIX:151,EVMATRIX:152,BBBMATRIX:153,EBBMATRIX:154,BVVMATRIX:155,EVVMATRIX:156, +BSMALLMATRIX:157,ESMALLMATRIX:158,BCASES:159,ECASES:160,BALIGNED:161,EALIGNED:162,BARRAY:163,EARRAY:164,SUBSTACK:165,ARRAY:166,ARRAYOPTS:167,compoundTerm:168,_:169,"^":170,OPP:171,opm:172,OPM:173,FM:174,compoundTermList:175,subsupTermScript:176,subsupTerm:177,textstyle:178,DISPLAYSTYLE:179,TEXTSTYLE:180,TEXTSIZE:181,SCRIPTSIZE:182,SCRIPTSCRIPTSIZE:183,COLOR:184,BGCOLOR:185,tableCell:186,CELLOPTS:187,tableCellList:188,COLSEP:189,tableRow:190,ROWOPTS:191,ROWSEP:192,document:193,documentItemList:194, +EOF:195,documentItem:196,mathItem:197,STARTMATH0:198,ENDMATH0:199,STARTMATH1:200,ENDMATH1:201,STARTMATH2:202,ENDMATH2:203,STARTMATH3:204,ENDMATH3:205,$accept:0,$end:1},z:{2:"error",4:"[",5:"TEXTOPTARG",6:"]",8:"{",9:"TEXTARG",10:"}",19:"COLLAYOUT",21:"COLALIGN",23:"ROWALIGN",25:"ROWSPAN",27:"COLSPAN",29:"ALIGN",31:"EQROWS",33:"EQCOLS",35:"ROWLINES",37:"COLLINES",39:"FRAME",41:"PADDING",49:"LEFT",50:"OPFS",51:".",53:"RIGHT",56:"BIG",57:"BBIG",58:"BIGG",59:"BBIGG",60:"BIGL",61:"BBIGL",62:"BIGGL",63:"BBIGGL", +64:"TEXATOP",65:"TEXOVER",66:"TEXCHOOSE",67:"NUM",68:"TEXT",69:"A",70:"AILL",71:"AIUL",72:"AILG",73:"AIUG",74:"F",75:"MI",76:"MN",77:"MO",78:"OP",79:"OPS",80:"OPAS",81:"MS",82:"MTEXT",83:"HIGH_SURROGATE",84:"LOW_SURROGATE",85:"BMP_CHARACTER",86:"OPERATORNAME",87:"MATHOP",88:"MATHBIN",89:"MATHREL",90:"FRAC",91:"ROOT",92:"SQRT",93:"UNDERSET",94:"OVERSET",95:"UNDEROVERSET",96:"XARROW",97:"MATHRLAP",98:"MATHLLAP",99:"MATHCLAP",100:"PHANTOM",101:"TFRAC",102:"BINOM",103:"TBINOM",104:"PMOD",105:"UNDERBRACE", +106:"UNDERLINE",107:"OVERBRACE",108:"ACCENT",109:"ACCENTNS",110:"BOXED",111:"SLASH",112:"QUAD",113:"QQUAD",114:"NEGSPACE",115:"NEGMEDSPACE",116:"NEGTHICKSPACE",117:"THINSPACE",118:"MEDSPACE",119:"THICKSPACE",120:"SPACE",121:"MATHRAISEBOX",122:"MATHBB",123:"MATHBF",124:"MATHBIT",125:"MATHSCR",126:"MATHBSCR",127:"MATHSF",128:"MATHFRAK",129:"MATHIT",130:"MATHTT",131:"MATHRM",132:"HREF",133:"STATUSLINE",134:"TOOLTIP",135:"TOGGLE",136:"BTOGGLE",138:"ETOGGLE",139:"TENSOR",141:"MULTI",142:"BMATRIX",144:"EMATRIX", +145:"BGATHERED",146:"EGATHERED",147:"BPMATRIX",148:"EPMATRIX",149:"BBMATRIX",150:"EBMATRIX",151:"BVMATRIX",152:"EVMATRIX",153:"BBBMATRIX",154:"EBBMATRIX",155:"BVVMATRIX",156:"EVVMATRIX",157:"BSMALLMATRIX",158:"ESMALLMATRIX",159:"BCASES",160:"ECASES",161:"BALIGNED",162:"EALIGNED",163:"BARRAY",164:"EARRAY",165:"SUBSTACK",166:"ARRAY",167:"ARRAYOPTS",169:"_",170:"^",171:"OPP",173:"OPM",174:"FM",179:"DISPLAYSTYLE",180:"TEXTSTYLE",181:"TEXTSIZE",182:"SCRIPTSIZE",183:"SCRIPTSCRIPTSIZE",184:"COLOR",185:"BGCOLOR", +187:"CELLOPTS",189:"COLSEP",191:"ROWOPTS",192:"ROWSEP",195:"EOF",198:"STARTMATH0",199:"ENDMATH0",200:"STARTMATH1",201:"ENDMATH1",202:"STARTMATH2",203:"ENDMATH2",204:"STARTMATH3",205:"ENDMATH3"},W:[0,[3,3],[7,3],[11,3],[12,3],[13,1],[14,1],[15,1],[16,1],[17,1],[18,2],[20,2],[22,2],[24,2],[26,2],[28,2],[30,2],[32,2],[34,2],[36,2],[38,2],[40,2],[42,1],[42,1],[42,1],[42,1],[43,1],[43,2],[44,1],[44,1],[45,1],[45,1],[45,1],[45,1],[45,1],[45,1],[45,1],[45,1],[45,1],[45,1],[46,1],[46,2],[47,1],[47,2],[48, +2],[48,2],[52,2],[52,2],[54,2],[54,3],[54,2],[54,2],[54,2],[54,2],[54,2],[54,2],[54,2],[54,2],[54,3],[54,5],[54,5],[54,5],[54,5],[54,5],[54,5],[54,1],[54,1],[54,1],[54,1],[54,1],[54,1],[54,1],[54,1],[54,2],[54,2],[54,2],[54,1],[54,1],[54,1],[54,1],[54,1],[54,2],[54,4],[54,2],[54,2],[54,1],[54,2],[54,2],[54,2],[54,2],[54,3],[54,3],[54,2],[54,5],[54,3],[54,3],[54,4],[54,5],[54,2],[54,2],[54,2],[54,2],[54,2],[54,3],[54,3],[54,3],[54,2],[54,2],[54,2],[54,2],[54,2],[54,2],[54,2],[54,2],[54,1],[54,1],[54, +1],[54,1],[54,1],[54,1],[54,1],[54,1],[54,4],[54,5],[54,4],[54,3],[54,2],[54,2],[54,2],[54,2],[54,2],[54,2],[54,2],[54,2],[54,2],[54,2],[54,3],[54,3],[54,3],[54,3],[54,3],[54,5],[54,8],[54,7],[54,7],[54,3],[54,3],[54,3],[54,3],[54,3],[54,3],[54,3],[54,3],[54,3],[54,3],[54,5],[54,4],[54,4],[54,4],[54,8],[137,1],[137,2],[168,3],[168,5],[168,4],[168,5],[168,4],[168,3],[168,3],[168,2],[168,1],[168,5],[168,5],[168,3],[168,3],[168,1],[172,1],[172,1],[175,1],[175,2],[176,1],[176,1],[177,4],[177,2],[177, +2],[177,3],[140,1],[140,2],[178,1],[178,1],[178,1],[178,1],[178,1],[178,2],[178,2],[55,2],[55,1],[186,0],[186,5],[186,1],[188,1],[188,3],[190,5],[190,1],[143,1],[143,3],[193,2],[194,1],[194,2],[196,1],[196,1],[197,2],[197,3],[197,2],[197,3],[197,3],[197,3]],H:function(b,a,c,$a,g,d){b=d.length-1;switch(g){case 1:this.b=d[b-1].replace(/\\[\\\]]/g,function(a){return a.slice(1)});this.b=Fb(this.b);break;case 2:this.b=d[b-1].replace(/\\[\\\}]/g,function(a){return a.slice(1)});this.b=Fb(this.b);break;case 3:case 4:this.b= +tc(d[b-1]);break;case 5:case 6:this.b=d[b].replace(/"/g,""");break;case 7:this.b=d[b].replace(/\s+/g," ").replace(/^ | $/g," ");break;case 8:d[b]=d[b].trim();if("t"===d[b])this.b="axis 1";else if("c"===d[b])this.b="center";else if("b"===d[b])this.b="axis -1";else throw"Unknown array alignment";break;case 9:this.b="";d[b]=d[b].replace(/\s+/g,"");for($a=0;$a<d[b].length;$a++)"c"===d[b][$a]?this.b+=" center":"l"===d[b][$a]?this.b+=" left":"r"===d[b][$a]&&(this.b+=" right");if(this.b.length)this.b= +this.b.slice(1);else throw"Invalid column alignments";break;case 10:case 11:this.b={columnalign:d[b]};break;case 12:this.b={rowalign:d[b]};break;case 13:this.b={rowspan:d[b]};break;case 14:this.b={colspan:d[b]};break;case 15:this.b={align:d[b]};break;case 16:this.b={equalrows:d[b]};break;case 17:this.b={equalcolumns:d[b]};break;case 18:this.b={rowlines:d[b]};break;case 19:this.b={columnlines:d[b]};break;case 20:this.b={frame:d[b]};break;case 21:this.b={rowspacing:d[b],columnspacing:d[b]};break;case 22:case 23:case 24:case 25:case 26:case 28:case 29:case 30:case 31:case 32:case 33:case 34:case 35:case 36:case 37:case 38:case 39:case 40:case 42:case 170:case 175:case 180:case 181:case 186:case 196:case 207:case 209:this.b= +d[b];break;case 27:case 41:case 43:this.b=Object.assign(d[b-1],d[b]);break;case 44:case 46:this.b=ab(d[b]);break;case 45:case 47:this.b="";break;case 48:this.b=e("mrow");break;case 49:this.b=db(d[b-1]);break;case 50:case 54:this.b=e("mo",d[b],{maxsize:"1.2em",minsize:"1.2em"});break;case 51:case 55:this.b=e("mo",d[b],{maxsize:"1.8em",minsize:"1.8em"});break;case 52:case 56:this.b=e("mo",d[b],{maxsize:"2.4em",minsize:"2.4em"});break;case 53:case 57:this.b=e("mo",d[b],{maxsize:"3em",minsize:"3em"}); +break;case 58:this.b=e("mrow",[d[b-2],db(d[b-1]),d[b]]);break;case 59:this.b=e("mfrac",[db(d[b-3]),db(d[b-1])],{linethickness:"0px"});break;case 60:this.b=e("mfrac",[db(d[b-3]),db(d[b-1])],{linethickness:"0px"});this.b=e("mrow",[d[b-4],this.b,d[b]]);break;case 61:this.b=e("mfrac",[db(d[b-3]),db(d[b-1])]);break;case 62:this.b=e("mfrac",[db(d[b-3]),db(d[b-1])]);this.b=e("mrow",[d[b-4],this.b,d[b]]);break;case 63:this.b=e("mfrac",[db(d[b-3]),db(d[b-1])],{linethickness:"0px"});this.b=e("mrow",[ab("("), +this.b,ab(")")]);break;case 64:this.b=e("mfrac",[db(d[b-3]),db(d[b-1])],{linethickness:"0px"});this.b=e("mrow",[d[b-4],this.b,d[b]]);this.b=e("mrow",[ab("("),this.b,ab(")")]);break;case 65:case 74:this.b=e("mn",d[b]);break;case 66:case 83:case 85:this.b=e("mtext",d[b]);break;case 67:case 68:case 69:case 70:this.b=Ub(d[b]);break;case 71:this.b=Ub(d[b],tb);break;case 72:case 177:this.b=ab(d[b],0,0);break;case 73:this.b=e("mi",d[b]);break;case 75:case 76:case 77:case 176:this.b=ab(d[b]);break;case 78:case 79:case 80:this.b= +e("mo",d[b],{stretchy:"false"});break;case 81:this.b=e("ms",d[b]);break;case 82:this.b=e("ms",d[b],{lquote:d[b-2],rquote:d[b-1]});break;case 84:this.b=e("mtext",d[b-1]+d[b]);break;case 86:this.b=ab(d[b],0,sb("thinmathspace"));break;case 87:this.b=ab(d[b],sb("thinmathspace"),sb("thinmathspace"));break;case 88:this.b=ab(d[b],sb("mediummathspace"),sb("mediummathspace"));break;case 89:this.b=ab(d[b],sb("thickmathspace"),sb("thickmathspace"));break;case 90:this.b=e("mfrac",[d[b-1],d[b]]);break;case 91:this.b= +e("mroot",[d[b],d[b-1]]);break;case 92:this.b=e("msqrt",[d[b]]);break;case 93:this.b=e("mroot",[d[b],db(d[b-2])]);break;case 94:this.b=e("munder",[d[b],d[b-1]]);break;case 95:this.b=e("mover",[d[b],d[b-1]]);break;case 96:this.b=e("munderover",[d[b],d[b-2],d[b-1]]);break;case 97:this.b="mrow"===d[b].tag&&!d[b].content&&!d[b].attributes?e("munder",[ab(d[b-4]),db(d[b-2])]):e("munderover",[ab(d[b-4]),db(d[b-2]),d[b]]);break;case 98:this.b=e("mover",[ab(d[b-1]),d[b]]);break;case 99:this.b=e("mpadded", +[d[b]],{width:"0em"});break;case 100:this.b=e("mpadded",[d[b]],{width:"0em",lspace:"-100%width"});break;case 101:this.b=e("mpadded",[d[b]],{width:"0em",lspace:"-50%width"});break;case 102:this.b=e("mphantom",[d[b]]);break;case 103:this.b=e("mfrac",[d[b-1],d[b]]);this.b=db([this.b],"mstyle",{displaystyle:"false"});break;case 104:this.b=e("mfrac",[d[b-1],d[b]],{linethickness:"0px"});this.b=e("mrow",[ab("("),this.b,ab(")")]);break;case 105:this.b=e("mfrac",[d[b-1],d[b]],{linethickness:"0px"});this.b= +db([this.b],"mstyle",{displaystyle:"false"});this.b=e("mrow",[ab("("),this.b,ab(")")]);break;case 106:this.b=e("mrow",[ab("(",sb("mediummathspace")),ab("mod",nb,sb("thinmathspace")),d[b],ab(")",nb,sb("mediummathspace"))]);break;case 107:this.b=e("munder",[d[b],ab("⏟")]);break;case 108:this.b=e("munder",[d[b],ab("_")]);break;case 109:this.b=e("mover",[d[b],ab("⏞")]);break;case 110:this.b=e("mover",[d[b],ab(d[b-1])]);break;case 111:this.b=e("mover",[d[b],e("mo",d[b-1],{stretchy:"false"})]);break;case 112:this.b= +e("menclose",[d[b]],{notation:"box"});break;case 113:this.b=e("menclose",[d[b]],{notation:"updiagonalstrike"});break;case 114:this.b=Gb(1);break;case 115:this.b=Gb(2);break;case 116:this.b=Gb(sb("negativethinmathspace"));break;case 117:this.b=Gb(sb("negativemediummathspace"));break;case 118:this.b=Gb(sb("negativethickmathspace"));break;case 119:this.b=Gb(sb("thinmathspace"));break;case 120:this.b=Gb(sb("mediummathspace"));break;case 121:this.b=Gb(sb("thickmathspace"));break;case 122:this.b=e("mspace", +xb,{height:"."+d[b-2]+"ex",depth:"."+d[b-1]+"ex",width:"."+d[b]+"em"});break;case 123:this.b=e("mpadded",[d[b]],{voffset:d[b-3].i+d[b-3].k,height:d[b-2].i+d[b-2].k,depth:d[b-1].i+d[b-1].k});break;case 124:this.b=e("mpadded",[d[b]],{voffset:d[b-2].i+d[b-2].k,height:d[b-1].i+d[b-1].k,depth:0>d[b-2].i?"+"+-d[b-2].i+d[b-2].k:"depth"});break;case 125:$a={voffset:d[b-1].i+d[b-1].k};0<=d[b-1].i?$a.height="+"+d[b-1].i+d[b-1].k:($a.height="0pt",$a.depth="+"+-d[b-1].i+d[b-1].k);this.b=e("mpadded",[d[b]],$a); +break;case 126:this.b=db([d[b]],"mstyle",{mathvariant:"double-struck"});break;case 127:this.b=db([d[b]],"mstyle",{mathvariant:"bold"});break;case 128:this.b=db([d[b]],"mstyle",{mathvariant:"bold-italic"});break;case 129:this.b=db([d[b]],"mstyle",{mathvariant:"script"});break;case 130:this.b=db([d[b]],"mstyle",{mathvariant:"bold-script"});break;case 131:this.b=db([d[b]],"mstyle",{mathvariant:"sans-serif"});break;case 132:this.b=db([d[b]],"mstyle",{mathvariant:"fraktur"});break;case 133:this.b=db([d[b]], +"mstyle",{mathvariant:"italic"});break;case 134:this.b=db([d[b]],"mstyle",{mathvariant:"monospace"});break;case 135:this.b=db([d[b]],"mstyle",{mathvariant:"normal"});break;case 136:this.b=e("mrow",[d[b]],$a.v?xb:{href:d[b-1]});break;case 137:this.b=$a.v?d[b]:e("maction",[d[b],e("mtext",d[b-1])],{actiontype:"statusline"});break;case 138:this.b=$a.v?d[b]:e("maction",[d[b],e("mtext",d[b-1])],{actiontype:"tooltip"});break;case 139:this.b=$a.v?d[b]:e("maction",[d[b-1],d[b]],{actiontype:"toggle",selection:"2"}); +break;case 140:this.b=$a.v?e("mrow",d[b-1]):e("maction",d[b-1],{actiontype:"toggle"});break;case 141:case 144:this.b=e("mmultiscripts",[d[b-3]].concat(d[b-1]));break;case 142:this.b=e("mmultiscripts",[d[b-3]].concat(d[b-1]).concat(e("mprescripts")).concat(d[b-5]));break;case 143:this.b=e("mmultiscripts",[d[b-2],e("mprescripts")].concat(d[b-4]));break;case 145:this.b=e("mtable",d[b-1],{displaystyle:"false",rowspacing:"0.5ex"});break;case 146:this.b=e("mtable",d[b-1],{displaystyle:"true",rowspacing:"1.0ex"}); +break;case 147:this.b=e("mtable",d[b-1],{displaystyle:"false",rowspacing:"0.5ex"});this.b=e("mrow",[ab("("),this.b,ab(")")]);break;case 148:this.b=e("mtable",d[b-1],{displaystyle:"false",rowspacing:"0.5ex"});this.b=e("mrow",[ab("["),this.b,ab("]")]);break;case 149:this.b=e("mtable",d[b-1],{displaystyle:"false",rowspacing:"0.5ex"});this.b=e("mrow",[ab("|"),this.b,ab("|")]);break;case 150:this.b=e("mtable",d[b-1],{displaystyle:"false",rowspacing:"0.5ex"});this.b=e("mrow",[ab("{"),this.b,ab("}")]);break; +case 151:this.b=e("mtable",d[b-1],{displaystyle:"false",rowspacing:"0.5ex"});this.b=e("mrow",[ab("‖"),this.b,ab("‖")]);break;case 152:this.b=e("mtable",d[b-1],{displaystyle:"false",rowspacing:"0.5ex"});this.b=db([this.b],"mstyle",{scriptlevel:"2"});break;case 153:this.b=e("mtable",d[b-1],{displaystyle:"false",columnalign:"left left"});this.b=e("mrow",[ab("{"),this.b]);break;case 154:this.b=e("mtable",d[b-1],{displaystyle:"true",columnalign:"right left right left right left right left right left", +columnspacing:"0em"});break;case 155:this.b=e("mtable",d[b-1],{displaystyle:"false",rowspacing:"0.5ex",align:d[b-3],columnalign:d[b-2]});break;case 156:this.b=e("mtable",d[b-1],{displaystyle:"false",rowspacing:"0.5ex",columnalign:d[b-2]});break;case 157:this.b=e("mtable",d[b-1],{displaystyle:"false",columnalign:"center",rowspacing:"0.5ex"});break;case 158:this.b=e("mtable",d[b-1],{displaystyle:"false"});break;case 159:this.b=e("mtable",d[b-1],Object.assign(d[b-3],{displaystyle:"false"}));break;case 160:this.b= +[d[b]];break;case 161:this.b=d[b-1].concat([d[b]]);break;case 162:this.b=e("mmultiscripts",[d[b-1]].concat(d[b]));break;case 163:this.b=e("msubsup",[d[b-4],d[b-2],d[b]]);break;case 164:this.b=e("msubsup",[d[b-3],d[b-1],ab(d[b])]);break;case 165:this.b=e("msubsup",[d[b-4],d[b],d[b-2]]);break;case 166:this.b=e("msubsup",[d[b-3],d[b],ab(d[b-2])]);break;case 167:this.b=e("msub",[d[b-2],d[b]]);break;case 168:this.b=e("msup",[d[b-2],d[b]]);break;case 169:this.b=e("msup",[d[b-1],ab(d[b])]);break;case 171:this.b= +e("munderover",[d[b-4],d[b-2],d[b]]);break;case 172:this.b=e("munderover",[d[b-4],d[b],d[b-2]]);break;case 173:this.b=e("munder",[d[b-2],d[b]]);break;case 174:this.b=e("mover",[d[b-2],d[b]]);break;case 178:case 200:case 204:this.b=[d[b]];break;case 179:this.b=d[b-1].concat([d[b]]);break;case 182:this.b=[d[b-2],d[b]];break;case 183:this.b=[d[b],e("none")];break;case 184:case 185:this.b=[e("none"),d[b]];break;case 187:this.b=d[b-1].concat(d[b]);break;case 188:this.b={displaystyle:"true"};break;case 189:this.b= +{displaystyle:"false"};break;case 190:this.b={scriptlevel:"0"};break;case 191:this.b={scriptlevel:"1"};break;case 192:this.b={scriptlevel:"2"};break;case 193:this.b={mathcolor:d[b]};break;case 194:this.b={mathbackground:d[b]};break;case 195:this.b=[db(d[b],"mstyle",d[b-1])];break;case 197:this.b=e("mtd",[]);break;case 198:this.b=db(d[b],"mtd",d[b-2]);break;case 199:this.b=db(d[b],"mtd");break;case 201:case 205:this.b=d[b-2].concat([d[b]]);break;case 202:this.b=this.b=e("mtr",d[b],d[b-2]);break;case 203:this.b= +e("mtr",d[b]);break;case 206:return this.b=d[b-1];case 208:this.b=d[b-1]+d[b];break;case 210:this.b=Pb(d[b]);break;case 211:this.b=Ib([e("mrow")],yb,yb,$a.t);break;case 212:this.b=Ib(d[b-1],yb,yb,$a.t);break;case 213:this.b=Ib([e("mrow")],tb,yb,$a.t);break;case 214:this.b=Ib(d[b-1],tb,yb,$a.t);break;case 215:this.b=Ib(d[b-1],yb,yb,$a.t);break;case 216:this.b=Ib(d[b-1],tb,yb,$a.t)}},ma:[{68:Wb,193:1,194:2,196:3,197:5,198:Xb,200:Yb,202:Zb,204:$b},{1:[3]},{68:Wb,195:[1,10],196:11,197:5,198:Xb,200:Yb, +202:Zb,204:$b},c(Ab,[2,207]),c(Ab,[2,209]),c(Ab,[2,210]),{8:m,48:36,49:s,50:v,51:x,54:25,55:13,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da, +130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,199:[1,12]},{8:m,48:36,49:s,50:v,51:x,54:25,55:126,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa, +101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,201:[1,125]},{8:m,48:36,49:s,50:v,51:x,54:25,55:127,56:h, +57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa, +153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},{8:m,48:36,49:s,50:v,51:x,54:25,55:128,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa, +117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},{1:[2,206]},c(Ab,[2,208]),c(Ab,[2,211]),{199:[1,129]},{8:m,48:36,49:s,50:v,51:x,54:25,55:130,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B, +74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26, +173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},c(ac,[2,196],{54:25,172:26,48:36,168:131,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa, +124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb}),c(Db,[2,188]),c(Db,[2,189]),c(Db,[2,190]),c(Db,[2,191]),c(Db,[2,192]),{7:133,8:mb,14:132},{7:133,8:mb,14:135},c(ub,[2,178]),{8:m,48:36,49:s,50:v,51:x,54:136,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N, +87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(ub,[2,170],{169:[1,138],170:[1,139],171:[1,140]}),c(ub,[2,175],{169:[1, +141],170:[1,142]}),{8:m,10:[1,143],48:36,49:s,50:v,51:x,54:25,55:144,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha, +134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},{50:[1,145]},{50:[1,146]},{50:[1,147]},{50:[1,148]},{50:[1,149]},{50:[1,150]},{50:[1,151]},{50:[1,152]},{8:m,48:36,49:s,50:v,51:x,54:25,55:153,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O, +88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb, +184:kb,185:lb},c(g,[2,65]),c(g,[2,66]),c(g,[2,67]),c(g,[2,68]),c(g,[2,69]),c(g,[2,70]),c(g,[2,71]),c(g,[2,72]),{7:155,8:mb,15:154},{7:155,8:mb,15:156},{7:155,8:mb,15:157},c(g,[2,76]),c(g,[2,77]),c(g,[2,78]),c(g,[2,79]),c(g,[2,80]),{3:160,4:Sb,7:155,8:mb,13:159,15:158},{7:155,8:mb,15:162},{84:[1,163]},c(g,[2,85]),{7:164,8:mb},{7:165,8:mb},{7:166,8:mb},{7:167,8:mb},{8:m,48:36,49:s,50:v,51:x,54:168,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H, +80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:169,56:h,57:q, +58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra, +155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{4:[1,171],8:m,48:36,49:s,50:v,51:x,54:170,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da, +130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:172,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa, +117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:173,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da, +104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:174,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N, +87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{4:[1,175],8:m,48:36,49:s,50:v,51:x,54:176,56:h,57:q,58:p,59:y,60:f,61:u, +62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua, +161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:177,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia, +135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:178,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va, +122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:179,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia, +109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:180,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U, +94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:181,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B, +74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36, +49:s,50:v,51:x,54:182,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma, +145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:183,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa, +127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:184,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na, +114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:185,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa, +101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:186,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I, +81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:187,56:h,57:q,58:p, +59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa, +157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:188,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga, +133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:189,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta, +120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:190,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga, +107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:191,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R, +91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(g,[2,114]),c(g,[2,115]),c(g,[2,116]),c(g,[2,117]),c(g,[2,118]),c(g,[2,119]),c(g,[2,120]), +c(g,[2,121]),{7:192,8:mb},{8:[1,194],12:193},{8:m,48:36,49:s,50:v,51:x,54:195,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa, +132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:196,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa, +119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:197,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa, +106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:198,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P, +89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:199,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t, +69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa, +166:Ya},{8:m,48:36,49:s,50:v,51:x,54:200,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za, +141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:201,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya, +125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:202,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la, +112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:203,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y, +98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:204,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F, +78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{7:133,8:mb,14:205},{7:206,8:mb}, +{7:207,8:mb},{8:m,48:36,49:s,50:v,51:x,54:208,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka, +139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:210,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa, +124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,137:209,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:[1,211]},c([144,189,192],pb,{178:14,175:15,168:23,54:25,172:26,48:36,143:212,190:213,188:215,186:216,55:218,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U, +94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),c([146,189,192],pb,{178:14,175:15, +168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:219,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea, +131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),c([148,189,192],pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:220,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S, +92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),c([150,189,192],pb, +{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:221,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca, +129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),c([152,189,192],pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:222,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P, +89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),c([154, +189,192],pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:223,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba, +128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),c([156,189,192],pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:224,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O, +88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}), +c([158,189,192],pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:225,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa, +127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),c([160,189,192],pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:226,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M, +86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb, +191:vb}),c([162,189,192],pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:227,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya, +125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),{3:230,4:Sb,7:231,8:mb,16:228,17:229},{8:[1,232]},{8:[1,233]},c(Eb,[2,176]),c(Eb,[2,177]),{50:[1,234],51:[1,235]},c(Ab,[2,213]),{201:[1,236]},{203:[1,237]},{205:[1,238]},c(Ab,[2,212]),c(ac,[2,195]),c(ub,[2,179]),c(Db,[2,193]),c([8,10, +19,21,23,25,27,29,31,33,35,37,39,41,49,50,51,56,57,58,59,60,61,62,63,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,139,141,142,145,147,149,151,153,155,157,159,161,163,165,166,173,174,179,180,181,182,183,184,185],[2,6]),{9:[1,239]},c(Db,[2,194]),{8:bc,140:240,169:Bb,170:Cb,177:242},{8:m,48:36,49:s,50:v, +51:x,54:245,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa, +149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:246,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca, +129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:247,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa, +116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(ub,[2,169],{169:[1,248]}),{8:m,48:36,49:s,50:v,51:x,54:249,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y, +98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:250,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F, +78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(g,[2,48]),{10:[1,251],64:[1,252], +65:[1,253],66:[1,254]},c(g,[2,50]),c(g,[2,51]),c(g,[2,52]),c(g,[2,53]),c(g,[2,54]),c(g,[2,55]),c(g,[2,56]),c(g,[2,57]),{52:255,53:Mb,64:[1,256],65:[1,257],66:[1,258]},c(g,[2,73]),c(g,[2,7]),c(g,[2,74]),c(g,[2,75]),c(g,[2,81]),{3:160,4:Sb,13:260},c(cc,[2,5]),{5:[1,261]},c(g,[2,83]),c(g,[2,84]),c(g,[2,86]),c(g,[2,87]),c(g,[2,88]),c(g,[2,89]),{8:m,48:36,49:s,50:v,51:x,54:262,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M, +86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:263,56:h,57:q,58:p,59:y,60:f,61:u,62:n, +63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua, +161:Va,163:Wa,165:Xa,166:Ya},c(g,[2,92]),{8:m,48:36,49:s,50:v,51:x,54:25,55:264,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa, +132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},{8:m,48:36,49:s,50:v,51:x,54:265,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa, +106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:266,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P, +89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:267,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t, +69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa, +166:Ya},{8:m,48:36,49:s,50:v,51:x,54:25,55:268,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka, +139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},c(g,[2,98]),c(g,[2,99]),c(g,[2,100]),c(g,[2,101]),c(g,[2,102]),{8:m,48:36,49:s,50:v,51:x,54:269,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba, +102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:270,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K, +83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:271,56:h,57:q,58:p,59:y,60:f, +61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta, +159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(g,[2,106]),c(g,[2,107]),c(g,[2,108]),c(g,[2,109]),c(g,[2,110]),c(g,[2,111]),c(g,[2,112]),c(g,[2,113]),{7:272,8:mb},{4:dc,8:m,11:273,48:36,49:s,50:v,51:x,54:274,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa, +115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{9:[1,276]},c(g,[2,126]),c(g,[2,127]),c(g,[2,128]),c(g,[2,129]),c(g,[2,130]),c(g,[2,131]),c(g,[2,132]),c(g,[2,133]),c(g,[2,134]),c(g,[2,135]),{8:m,48:36,49:s,50:v,51:x,54:277,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A, +73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36, +49:s,50:v,51:x,54:278,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma, +145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:279,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa, +127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:280,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na, +114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:282,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa, +101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,138:[1,281],139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(ec,[2,160]),{10:[1,284],140:283,169:Bb,170:Cb,177:242},{144:[1,285],192:wb},c(Nb,[2,204]),{8:[1,287]},c(Nb,[2,203],{189:fc}),c(Jb, +[2,200]),{8:[1,289]},c(Jb,[2,199]),{146:[1,290],192:wb},{148:[1,291],192:wb},{150:[1,292],192:wb},{152:[1,293],192:wb},{154:[1,294],192:wb},{156:[1,295],192:wb},{158:[1,296],192:wb},{160:[1,297],192:wb},{162:[1,298],192:wb},{7:231,8:mb,17:299},c(gc,pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:300,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q, +90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),{8:[2,8]}, +c([8,49,50,51,56,57,58,59,60,61,62,63,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,139,141,142,145,147,149,151,153,155,157,159,161,163,164,165,166,173,174,179,180,181,182,183,184,185,187,189,191,192],[2,9]),c(Tb,pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:301,8:m,49:s, +50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa, +149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),c(Tb,pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:302,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga, +107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,167:[1,303],173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),c(Db,[2,44]),c(Db,[2,45]),c(Ab,[2,214]),c(Ab,[2,215]),c(Ab,[2,216]),{10:[1,304]},c(ub,[2,162],{177:305, +169:Bb,170:Cb}),{140:306,169:Bb,170:Cb,177:242},c(Eb,[2,186]),{8:m,48:36,49:s,50:v,51:x,54:309,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da, +130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,170:[1,308],172:310,173:bb,174:cb,176:307},{8:m,48:36,49:s,50:v,51:x,54:309,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka, +111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,172:310,173:bb,174:cb,176:311},{8:bc},c(ub,[2,167],{170:[1,312],171:[1,313]}),c(ub,[2,168],{169:[1,314]}),{8:m,48:36,49:s,50:v,51:x,54:315,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B, +74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(ub,[2,173], +{170:[1,316]}),c(ub,[2,174],{169:[1,317]}),c(g,[2,49]),{8:m,48:36,49:s,50:v,51:x,54:25,55:318,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da, +130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},{8:m,48:36,49:s,50:v,51:x,54:25,55:319,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca, +103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},{8:m,48:36,49:s,50:v,51:x,54:25,55:320,56:h,57:q,58:p,59:y,60:f,61:u, +62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua, +161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},c(g,[2,58]),{8:m,48:36,49:s,50:v,51:x,54:25,55:321,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa, +119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},{8:m,48:36,49:s,50:v,51:x,54:25,55:322,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O, +88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb, +184:kb,185:lb},{8:m,48:36,49:s,50:v,51:x,54:25,55:323,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja, +136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},{50:[1,324],51:[1,325]},{7:155,8:mb,15:326},{6:[1,327]},c(g,[2,90]),c(g,[2,91]),{6:[1,328]},c(g,[2,94]),c(g,[2,95]),{8:m,48:36,49:s,50:v,51:x,54:329,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q, +90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{6:[1,330]},c(g,[2,103]),c(g,[2,104]),c(g,[2,105]),{7:331,8:mb},{4:dc,8:m,11:332,48:36, +49:s,50:v,51:x,54:333,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma, +145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(g,[2,125]),{5:[1,334]},{10:[1,335]},c(g,[2,136]),c(g,[2,137]),c(g,[2,138]),c(g,[2,139]),c(g,[2,140]),c(ec,[2,161]),{10:[1,336],169:Bb,170:Cb,177:305},{8:m,48:36,49:s,50:v,51:x,54:337,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea, +105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(g,[2,145]),c(Jb,pb,{178:14,175:15,168:23,54:25,172:26,48:36,188:215,186:216,55:218,190:338,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w, +71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya, +173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),{20:341,21:Kb,22:342,23:Lb,44:340,47:339},c(Jb,pb,{178:14,175:15,168:23,54:25,172:26,48:36,55:218,186:345,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na, +114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb}),{20:348,21:Kb,22:349,23:Lb,24:350,25:hc,26:351,27:ic,42:347,43:346},c(g,[2,146]),c(g,[2,147]),c(g,[2,148]),c(g,[2,149]),c(g,[2,150]),c(g,[2,151]),c(g,[2,152]),c(g, +[2,153]),c(g,[2,154]),c(gc,pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:354,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya, +125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),{164:[1,355],192:wb},{10:[1,356],192:wb},{10:[1,357],192:wb},{8:[1,358]},c([6,8,10,19,21,23,25,27,29,31,33,35,37,39,41,49,50,51,53,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,85,86,87,88,89,90, +91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,138,139,141,142,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,169,170,171,173,174,179,180,181,182,183,184,185,187,189,191,192,199,201,203,205],[2,2]),c(Eb,[2,187]),{10:[1,359],169:Bb,170:Cb,177:305},c([6,8,10,49,50,51,53,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75, +76,77,78,79,80,81,82,83,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,139,141,142,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,169,173,174,189,192,199,201,203,205],[2,183],{170:[1,360]}),{8:m,48:36,49:s,50:v,51:x,54:309,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E, +77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,172:310,173:bb,174:cb,176:361}, +c(Eb,[2,180]),c(Eb,[2,181]),c(Eb,[2,184]),{8:m,48:36,49:s,50:v,51:x,54:362,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga, +133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(ub,[2,164]),{8:m,48:36,49:s,50:v,51:x,54:363,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra, +118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(ub,[2,166]),{8:m,48:36,49:s,50:v,51:x,54:364,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca, +103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:365,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L, +85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{10:[1,366]},{10:[1,367]},{10:[1,368]},{52:369,53:Mb},{52:370, +53:Mb},{52:371,53:Mb},c(g,[2,46]),c(g,[2,47]),c(g,[2,82]),c(cc,[2,1]),{8:m,48:36,49:s,50:v,51:x,54:372,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca, +129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(g,[2,96]),{8:m,48:36,49:s,50:v,51:x,54:373,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na, +114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(g,[2,122]),{8:m,48:36,49:s,50:v,51:x,54:374,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y, +98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(g,[2,124]),{6:[1,375]},c(jc,[2,4]),{8:m,48:36,49:s,50:v,51:x,54:376,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w, +71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya}, +{8:[1,377]},c(Nb,[2,205]),{10:[1,378],20:341,21:Kb,22:342,23:Lb,44:379},c(Ob,[2,42]),c(Ob,[2,28]),c(Ob,[2,29]),{7:133,8:mb,14:380},{7:133,8:mb,14:381},c(Jb,[2,201]),{10:[1,382],20:348,21:Kb,22:349,23:Lb,24:350,25:hc,26:351,27:ic,42:383},c(Hb,[2,26]),c(Hb,[2,22]),c(Hb,[2,23]),c(Hb,[2,24]),c(Hb,[2,25]),{7:133,8:mb,14:384},{7:133,8:mb,14:385},{164:[1,386],192:wb},c(g,[2,156]),c(g,[2,157]),c(g,[2,158]),{18:389,19:kc,20:390,21:Kb,22:391,23:Lb,28:392,29:lc,30:393,31:mc,32:394,33:nc,34:395,35:oc,36:396, +37:pc,38:397,39:qc,40:398,41:rc,45:388,46:387},c(g,[2,141]),{8:m,48:36,49:s,50:v,51:x,54:309,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da, +130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,172:310,173:bb,174:cb,176:407},c(Eb,[2,185]),c(ub,[2,163]),c(ub,[2,165]),c(ub,[2,171]),c(ub,[2,172]),c(g,[2,59]),c(g,[2,61]),c(g,[2,63]),c(g,[2,60]),c(g,[2,62]),c(g,[2,64]),c(g,[2,93]),c(g,[2,97]),c(g,[2,123]),c(jc,[2,3]),{8:[1,408]},{140:409,169:Bb,170:Cb,177:242},c(Jb,pb,{178:14,175:15,168:23,54:25,172:26,48:36,186:216,55:218,188:410,8:m,49:s, +50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa, +149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb}),c(Ob,[2,43]),c(sc,[2,11]),c(sc,[2,12]),{8:m,48:36,49:s,50:v,51:x,54:25,55:411,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la, +112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},c(Hb,[2,27]),c(Hb,[2,13]),c(Hb,[2,14]),c(g,[2,155]),{10:[1,412],18:389,19:kc,20:390,21:Kb,22:391,23:Lb,28:392,29:lc,30:393,31:mc, +32:394,33:nc,34:395,35:oc,36:396,37:pc,38:397,39:qc,40:398,41:rc,45:413},c(ob,[2,40]),c(ob,[2,30]),c(ob,[2,31]),c(ob,[2,32]),c(ob,[2,33]),c(ob,[2,34]),c(ob,[2,35]),c(ob,[2,36]),c(ob,[2,37]),c(ob,[2,38]),c(ob,[2,39]),{7:133,8:mb,14:414},{7:133,8:mb,14:415},{7:133,8:mb,14:416},{7:133,8:mb,14:417},{7:133,8:mb,14:418},{7:133,8:mb,14:419},{7:133,8:mb,14:420},{7:133,8:mb,14:421},c(Eb,[2,182]),{10:[1,423],140:422,169:Bb,170:Cb,177:242},{10:[1,424],169:Bb,170:Cb,177:305},c(Nb,[2,202],{189:fc}),c(Jb,[2,198]), +c(Tb,pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:425,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba, +128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),c(ob,[2,41]),c(ob,[2,10]),c(ob,[2,15]),c(ob,[2,16]),c(ob,[2,17]),c(ob,[2,18]),c(ob,[2,19]),c(ob,[2,20]),c(ob,[2,21]),{10:[1,426],169:Bb,170:Cb,177:305},c(g,[2,143]),c(g,[2,144]),{10:[1,427],192:wb},c(g,[2,142]),c(g,[2,159])],N:{10:[2,206],230:[2,8]},parseError:function(b, +a){if(a.va)this.trace(b);else{var c=Error(b);c.hash=a;throw c;}},parse:function(b){var a=[0],c=[xb],e=[],g=this.ma,d="",m=0,s=0,v=0,x=e.slice.call(arguments,1),h=Object.create(this.S),q={},p;for(p in this.e)Object.prototype.hasOwnProperty.call(this.e,p)&&(q[p]=this.e[p]);h.ga(b,q);q.S=h;q.V=this;"undefined"==typeof h.c&&(h.c={});p=h.c;e.push(p);var y=h.options&&h.options.w;this.parseError="function"===typeof q.parseError?q.parseError:Object.getPrototypeOf(this).parseError;for(var f,u,n,k,r={},t,l;;){n= +a[a.length-1];if(this.N[n])k=this.N[n];else{if(f===xb||"undefined"==typeof f)f=nb,f=h.R()||1,"number"!==typeof f&&(f=this.la[f]||f);k=g[n]&&g[n][f]}if("undefined"===typeof k||!k.length||!k[0]){var w="";l=[];for(t in g[n])this.z[t]&&2<t&&l.push("'"+this.z[t]+"'");w=h.D?"Parse error on line "+(m+1)+":\n"+h.D()+"\nExpecting "+l.join(", ")+", got '"+(this.z[f]||f)+"'":"Parse error on line "+(m+1)+": Unexpected "+(1==f?"end of input":"'"+(this.z[f]||f)+"'");this.parseError(w,{text:h.match,$:this.z[f]|| +f,T:h.f,ta:p,qa:l})}if(k[0]instanceof Array&&1<k.length)throw Error("Parse Error: multiple actions possible at state: "+n+", token: "+f);switch(k[0]){case 1:a.push(f);c.push(h.a);e.push(h.c);a.push(k[1]);f=xb;u?(f=u,u=xb):(s=h.q,d=h.a,m=h.f,p=h.c,0<v&&v--);break;case 2:l=this.W[k[1]][1];r.b=c[c.length-l];r.K={r:e[e.length-(l||1)].r,o:e[e.length-1].o,l:e[e.length-(l||1)].l,m:e[e.length-1].m};y&&(r.K.n=[e[e.length-(l||1)].n[0],e[e.length-1].n[1]]);n=this.H.apply(r,[d,s,m,q,k[1],c,e].concat(x));if("undefined"!== +typeof n)return n;l&&(a=a.slice(0,-2*l),c=c.slice(0,-1*l),e=e.slice(0,-1*l));a.push(this.W[k[1]][0]);c.push(r.b);e.push(r.K);k=g[a[a.length-2]][a[a.length-1]];a.push(k);break;case 3:return tb}}return tb}},Qb="http://www.w3.org/1998/Math/MathML",wc="TeX LaTeX text/x-tex text/x-latex application/x-tex application/x-latex".split(" ");try{rb.C=new DOMParser}catch(yc){rb.C={parseFromString:function(){throw"DOMParser undefined. Did you call TeXZilla.setDOMParser?";}}}rb.fa=function(b){this.C=b};try{rb.G= +new XMLSerializer}catch(zc){rb.G={serializeToString:function(){throw"XMLSerializer undefined. Did you call TeXZilla.setXMLSerializer?";}}}rb.ja=function(b){this.G=b};rb.U=function(b){return this.C.parseFromString(b,"application/xml").documentElement};rb.ia=function(b){this.e.v=b};rb.ha=function(b){this.e.da=b};rb.ca=function(b){"string"===typeof b&&(b=this.U(b));return Vb(b)};rb.Z=function(b,a,c,g){var f;try{f=this.parse("\\("+b+"\\)"),c&&(f=f.replace(/^<math/,'<math dir="rtl"')),a&&(f=f.replace(/^<math/, +'<math display="block"'))}catch(d){if(g)throw d;f=Pb(Ib([e("merror",[e("mtext",Fb(d.message))])],a,c,b))}return f};rb.Y=function(b,a,c,e){return this.U(this.Z(b,a,c,e))};rb.na=function(b,a,c,e,f){var d,g;e===nb&&(e=64);f===nb&&(f=window.document);a=this.Y(b,tb,a);a.setAttribute("mathsize",e+"px");e=document.createElement("div");e.style.visibility="hidden";e.style.position="absolute";e.appendChild(a);f.body.appendChild(e);d=a.getBoundingClientRect();f.body.removeChild(e);e.removeChild(a);c?(c=Math.pow(2, +Math.ceil(Math.log(d.width)/Math.LN2)),f=Math.pow(2,Math.ceil(Math.log(d.height)/Math.LN2))):(c=Math.ceil(d.width),f=Math.ceil(d.height));g=document.createElementNS("http://www.w3.org/2000/svg","svg");g.setAttribute("width",c+"px");g.setAttribute("height",f+"px");e=document.createElementNS("http://www.w3.org/2000/svg","g");e.setAttribute("transform","translate("+(c-d.width)/2+","+(f-d.height)/2+")");g.appendChild(e);e=document.createElementNS("http://www.w3.org/2000/svg","foreignObject");e.setAttribute("width", +d.width);e.setAttribute("height",d.height);e.appendChild(a);g.firstChild.appendChild(e);a=new Image;a.src="data:image/svg+xml;base64,"+window.btoa(xc(this.G.serializeToString(g)));a.width=c;a.height=f;a.alt=Fb(b);return a};rb.Q=function(b,a){try{return this.parse(b)}catch(c){if(a)throw c;return b}};rb.P=function(b,a){var c,e,f;for(f=b.firstChild;f;f=f.nextSibling)switch(f.nodeType){case 1:this.P(f,a);break;case 3:this.e.O=tb;c=this.C.parseFromString("<root>"+zb.Q(f.data,a)+"</root>","application/xml").documentElement; +for(this.e.O=yb;e=c.firstChild;)b.insertBefore(c.removeChild(e),f);e=f.previousSibling;b.removeChild(f);f=e}};rb.S=function(){return{J:1,parseError:function(b,a){if(this.e.V)this.e.V.parseError(b,a);else throw Error(b);},ga:function(b,a){this.e=a||this.e||{};this.g=b;this.u=this.B=this.s=yb;this.f=this.q=0;this.a=this.h=this.match="";this.d=["INITIAL"];this.c={r:1,l:0,o:1,m:0};this.options.w&&(this.c.n=[0,0]);this.offset=0;return this},input:function(){var b=this.g[0];this.a+=b;this.q++;this.offset++; +this.match+=b;this.h+=b;b.match(/(?:\r\n?|\n).*/g)?(this.f++,this.c.o++):this.c.m++;this.options.w&&this.c.n[1]++;this.g=this.g.slice(1);return b},I:function(b){var a=b.length,c=b.split(/(?:\r\n?|\n)/g);this.g=b+this.g;this.a=this.a.substr(0,this.a.length-a);this.offset-=a;b=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1);this.h=this.h.substr(0,this.h.length-1);c.length-1&&(this.f-=c.length-1);var e=this.c.n;this.c={r:this.c.r,o:this.f+1,l:this.c.l,m:c?(c.length=== +b.length?this.c.l:0)+b[b.length-c.length].length-c[0].length:this.c.l-a};this.options.w&&(this.c.n=[e[0],e[0]+this.q-a]);this.q=this.a.length;return this},ua:function(){this.u=tb;return this},wa:function(){if(this.options.L)this.B=tb;else return this.parseError("Lexical error on line "+(this.f+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.D(),{text:"",$:xb,T:this.f});return this},sa:function(b){this.I(this.match.slice(b))}, +ea:function(){var b=this.h.substr(0,this.h.length-this.match.length);return(20<b.length?"...":"")+b.substr(-20).replace(/\n/g,"")},oa:function(){var b=this.match;20>b.length&&(b+=this.g.substr(0,20-b.length));return(b.substr(0,20)+(20<b.length?"...":"")).replace(/\n/g,"")},D:function(){var b=this.ea(),a=Array(b.length+1).join("-");return b+this.oa()+"\n"+a+"^"},X:function(b,a){var c,e;this.options.L&&(e={f:this.f,c:{r:this.c.r,o:this.o,l:this.c.l,m:this.c.m},a:this.a,match:this.match,matches:this.matches, +h:this.h,q:this.q,offset:this.offset,u:this.u,g:this.g,e:this.e,d:this.d.slice(0),s:this.s},this.options.w&&(e.c.n=this.c.n.slice(0)));if(c=b[0].match(/(?:\r\n?|\n).*/g))this.f+=c.length;this.c={r:this.c.o,o:this.f+1,l:this.c.m,m:c?c[c.length-1].length-c[c.length-1].match(/\r?\n?/)[0].length:this.c.m+b[0].length};this.a+=b[0];this.match+=b[0];this.matches=b;this.q=this.a.length;this.options.w&&(this.c.n=[this.offset,this.offset+=this.q]);this.B=this.u=yb;this.g=this.g.slice(b[0].length);this.h+=b[0]; +c=this.H.call(this,this.e,this,a,this.d[this.d.length-1]);this.s&&this.g&&(this.s=yb);if(c)return c;if(this.B)for(var f in e)this[f]=e[f];return yb},next:function(){if(this.s)return this.J;this.g||(this.s=tb);var b,a,c;this.u||(this.match=this.a="");for(var e=this.aa(),f=0;f<e.length;f++)if((a=this.g.match(this.rules[e[f]]))&&(!b||a[0].length>b[0].length))if(b=a,c=f,this.options.L){b=this.X(a,e[f]);if(b!==yb)return b;if(this.B)b=yb;else return yb}else if(!this.options.ra)break;return b?(b=this.X(b, +e[c]),b!==yb?b:yb):""===this.g?this.J:this.parseError("Lexical error on line "+(this.f+1)+". Unrecognized text.\n"+this.D(),{text:"",$:xb,T:this.f})},R:function(){var b=this.next();return b?b:this.R()},j:function(b){this.d.push(b)},p:function(){return 0<this.d.length-1?this.d.pop():this.d[0]},aa:function(){return this.d.length&&this.d[this.d.length-1]?this.M[this.d[this.d.length-1]].rules:this.M.INITIAL.rules},ya:function(b){b=this.d.length-1-Math.abs(b||0);return 0<=b?this.d[b]:"INITIAL"},pushState:function(b){this.j(b)}, +xa:function(){return this.d.length},options:{},H:function(b,a,c){switch(c){case 0:this.I(a.a);this.pushState("DOCUMENT");break;case 1:return this.pushState("MATH"+(0+!!b.da)),b.ka=this.h.length,"STARTMATH"+(2*("$"==a.a[0])+("$"==a.a[1]||"["==a.a[1]));case 2:return this.p(),"EOF";case 3:return a.a=a.a[1],"TEXT";case 4:return b.O&&(a.a=Fb(a.a)),"TEXT";case 5:return"TEXT";case 6:return this.p(),"[";case 7:this.I(a.a);this.p();this.p();break;case 8:return"TEXTOPTARG";case 9:return this.p(),"]";case 10:return"{"; +case 11:return"TEXTARG";case 12:return this.p(),"}";case 13:return this.p(),"]";case 15:return this.p(),b.ba=this.h.length-this.match.length,b.t=this.h.substring(b.ka,b.ba),"ENDMATH"+(2*("$"==a.a[0])+("$"==a.a[1]||"]"==a.a[1]));case 16:return"{";case 17:return"}";case 18:return"^";case 19:return"_";case 20:return".";case 21:return"COLSEP";case 22:return"ROWSEP";case 23:return"NUM";case 24:return"A";case 25:return a.a="Ζ","AIUG";case 26:return a.a="ζ","AILG";case 27:return this.pushState("OPTARG"), +this.pushState("TRYOPTARG"),a.a="⇌","XARROW";case 28:return this.pushState("OPTARG"),this.pushState("TRYOPTARG"),a.a="⇒","XARROW";case 29:return this.pushState("OPTARG"),this.pushState("TRYOPTARG"),a.a="→","XARROW";case 30:return this.pushState("OPTARG"),this.pushState("TRYOPTARG"),a.a="↦","XARROW";case 31:return this.pushState("OPTARG"),this.pushState("TRYOPTARG"),a.a="⇋","XARROW";case 32:return this.pushState("OPTARG"),this.pushState("TRYOPTARG"),a.a="⇔","XARROW";case 33:return this.pushState("OPTARG"), +this.pushState("TRYOPTARG"),a.a="↔","XARROW";case 34:return this.pushState("OPTARG"),this.pushState("TRYOPTARG"),a.a="⇐","XARROW";case 35:return this.pushState("OPTARG"),this.pushState("TRYOPTARG"),a.a="←","XARROW";case 36:return a.a="Ξ","AIUG";case 37:return a.a="ξ","AILG";case 38:return this.pushState("OPTARG"),this.pushState("TRYOPTARG"),a.a="↪","XARROW";case 39:return this.pushState("OPTARG"),this.pushState("TRYOPTARG"),a.a="↩","XARROW";case 40:return a.a="≀","OP";case 41:return a.a="℘","A";case 42:return a.a= +"⇀","ACCENT";case 43:return a.a="˜","ACCENT";case 44:return a.a="^","ACCENT";case 45:return a.a="ˇ","ACCENT";case 46:return a.a="¯","ACCENT";case 47:return a.a="≙","OP";case 48:return a.a="⋀","OPM";case 49:return a.a="∧","OP";case 50:return a.a="⦀","OPFS";case 51:return a.a="⊪","OP";case 52:return a.a="‖","OPFS";case 53:return a.a="|","OPFS";case 54:return a.a="⊻","OP";case 55:return a.a="⋁","OPM";case 56:return a.a="∨","OP";case 57:return a.a="⇀","ACCENTNS";case 58:return a.a="⋮","OP";case 59:return a.a= +"⊫","OP";case 60:return a.a="⊩","OP";case 61:return a.a="⊨","OP";case 62:return a.a="⊢","OP";case 63:return a.a="⫫","OP";case 64:return a.a="⊳","OP";case 65:return a.a="⊲","OP";case 66:return a.a="▵","OP";case 67:return a.a="ϑ","AILG";case 68:return a.a="⫌︀","OP";case 69:return a.a="⊋︀","OP";case 70:return a.a="⫋︀","OP";case 71:return a.a="⊊︀","OP";case 72:return a.a="⊊︀","OP";case 73:return a.a="ς","A";case 74:return a.a="ϱ","AILG";case 75:return a.a="∝","OP";case 76:return a.a="ϖ","AILG";case 77:return a.a= +"φ","AILG";case 78:return a.a="∅","A";case 79:return a.a="ϰ","AILG";case 80:return a.a="ε","AILG";case 81:return a.a="⤊","OPS";case 82:return a.a="⇈","OPS";case 83:return a.a="ϒ","A";case 84:return a.a="υ","AILG";case 85:return a.a="ϒ","A";case 86:return a.a="⊎","OP";case 87:return a.a="⨛","OP";case 88:return a.a="↿","OPS";case 89:return a.a="↾","OPS";case 90:return a.a="⇕","OPS";case 91:return a.a="↕","OPS";case 92:return a.a="↕","OPS";case 93:return a.a="⇑","OPS";case 94:return a.a="↑","OPS";case 95:return a.a= +"↑","OPS";case 96:return a.a="⊵","OP";case 97:return a.a="⊴","OP";case 98:return a.a="⋃","OPM";case 99:return a.a="∪","OP";case 100:return"UNDERSET";case 101:return"UNDEROVERSET";case 102:return"UNDERLINE";case 103:return"UNDERBRACE";case 104:return a.a="⋰","OP";case 105:return"OP";case 106:return"OP";case 107:return"OP";case 108:return"OP";case 109:return"OP";case 110:return"OP";case 111:return"OP";case 112:return"OP";case 113:return"OP";case 114:return"OP";case 115:return"OP";case 116:return"OP"; +case 117:return"OP";case 118:return"OP";case 119:return"OP";case 120:return"OP";case 121:return"OP";case 122:return"OP";case 123:return"OP";case 124:return"OP";case 125:return"OP";case 126:return"OP";case 127:return"OP";case 128:return"OP";case 129:return"OP";case 130:return"OP";case 131:return"OP";case 132:return"OP";case 133:return"OP";case 134:return"OP";case 135:return"OP";case 136:return"OP";case 137:return"OP";case 138:return"OPFS";case 139:return"OPFS";case 140:return"OP";case 141:return"OP"; +case 142:return"OP";case 143:return"OP";case 144:return"OP";case 145:return"OP";case 146:return"OP";case 147:return"OP";case 148:return"OP";case 149:return"OP";case 150:return"OP";case 151:return"OP";case 152:return"OP";case 153:return"OP";case 154:return"OP";case 155:return"OP";case 156:return"OP";case 157:return"OP";case 158:return"OP";case 159:return"OP";case 160:return"OP";case 161:return a.a="⤖","OP";case 162:return a.a="↠","OPS";case 163:return a.a="↞","OPS";case 164:return a.a="∭","OP";case 165:return a.a= +"⊵","OP";case 166:return a.a="▹","OP";case 167:return a.a="≜","OP";case 168:return a.a="⊴","OP";case 169:return a.a="◃","OP";case 170:return a.a="▿","OP";case 171:return a.a="▵","OP";case 172:return a.a="⤪","OP";case 173:return a.a="⤩","OP";case 174:return a.a="⊤","OP";case 175:return this.pushState("TEXTARG"),"TOOLTIP";case 176:return a.a="⤧","OP";case 177:return"TOGGLE";case 178:return a.a="⤨","OP";case 179:return a.a="→","OPS";case 180:return a.a="⊠","OP";case 181:return a.a="×","OP";case 182:return a.a= +"˜","ACCENTNS";case 183:return"THINSPACE";case 184:return"THICKSPACE";case 185:return a.a="∼","OP";case 186:return a.a="≈","OP";case 187:return a.a="Θ","AIUG";case 188:return a.a="θ","AILG";case 189:return a.a="∴","OP";case 190:return"TFRAC";case 191:return"TEXTSTYLE";case 192:return"TEXTSIZE";case 193:return a.a="”","OPF";case 194:return a.a="“","OPF";case 195:return a.a="~","OPS";case 196:return a.a="`","OP";case 197:return a.a="^","OPS";case 198:return a.a="´","OP";case 199:return this.j("TEXTARG"), +"MTEXT";case 200:return"TENSOR";case 201:return"TBINOM";case 202:return a.a="Τ","AIUG";case 203:return a.a="τ","AILG";case 204:return a.a="⇙","OPS";case 205:return a.a="↙","OPS";case 206:return a.a="⇙","OPS";case 207:return a.a="↙","OPS";case 208:return a.a="√","OPS";case 209:return a.a="⫌","OP";case 210:return a.a="⊋","OP";case 211:return a.a="⫆","OP";case 212:return a.a="⊇","OP";case 213:return a.a="⋑","OP";case 214:return a.a="⊃","OP";case 215:return a.a="∑","OPM";case 216:return a.a="≿","OP"; +case 217:return a.a="⋩","OP";case 218:return a.a="⪶","OP";case 219:return a.a="⪺","OP";case 220:return a.a="⪰","OP";case 221:return a.a="≽","OP";case 222:return a.a="⪸","OP";case 223:return a.a="≻","OP";case 224:return"SUBSTACK";case 225:return a.a="⫋","OP";case 226:return a.a="⊊","OP";case 227:return a.a="⫅","OP";case 228:return a.a="⊆","OP";case 229:return a.a="⋐","OP";case 230:return a.a="⊂","OP";case 231:return this.pushState("TEXTARG"),"STATUSLINE";case 232:return a.a="⋆","OP";case 233:return"OVERSET"; +case 234:return a.a="⫽","OP";case 235:return a.a="□","OP";case 236:return a.a="⊒","OP";case 237:return a.a="⊐","OP";case 238:return a.a="⊑","OP";case 239:return a.a="⊏","OP";case 240:return this.pushState("OPTARG"),this.pushState("TRYOPTARG"),"SQRT";case 241:return a.a="⊔","OP";case 242:return a.a="⊓","OP";case 243:return a.a="∢","OP";case 244:return a.a="♠","OP";case 245:return this.pushState("TEXTARG"),this.pushState("TEXTARG"),this.pushState("TEXTARG"),"SPACE";case 246:return a.a="⌣","OP";case 247:return a.a= +"⌣","OP";case 248:return a.a="∖","OP";case 249:return a.a="⌢","OP";case 250:return"SLASH";case 251:return a.a="≃","OP";case 252:return a.a="∼","OP";case 253:return a.a="Σ","AIUG";case 254:return a.a="σ","AILG";case 255:return a.a="⧢","OP";case 256:return a.a="∥","OP";case 257:return a.a="∣","OP";case 258:return a.a="♯","OP";case 259:return a.a="∖","OP";case 260:return a.a="⤭","OP";case 261:return a.a="⇘","OPS";case 262:return a.a="↘","OPS";case 263:return a.a="⇘","OPS";case 264:return a.a="↘","OPS"; +case 265:return"SCRIPTSIZE";case 266:return"SCRIPTSCRIPTSIZE";case 267:return a.a="⋊","OP";case 268:return a.a="↱","OPS";case 269:return a.a="⇛","OPS";case 270:return a.a="⟫","OPFS";case 271:return a.a="’","OPF";case 272:return this.j("TEXTARG"),"ROWSPAN";case 273:return"ROWOPTS";case 274:return this.pushState("TEXTARG"),"ROWLINES";case 275:return this.j("TEXTARG"),"ROWALIGN";case 276:return"ROOT";case 277:return a.a="⎱","OP";case 278:return a.a="≓","OP";case 279:return a.a="⟲","OP";case 280:return a.a= +"⋌","OP";case 281:return a.a="↝","OPS";case 282:return a.a="⇉","OPS";case 283:return a.a="⇌","OPS";case 284:return a.a="⇄","OPS";case 285:return a.a="⇀","OPS";case 286:return a.a="⇁","OPS";case 287:return a.a="⇾","OPS";case 288:return a.a="↣","OPS";case 289:return a.a="⇒","OPS";case 290:return a.a="→","OPS";case 291:return"RIGHT";case 292:return a.a="Ρ","AIUG";case 293:return a.a="ρ","AILG";case 294:return a.a="⊳","OP";case 295:return a.a="⌋","OPFS";case 296:return a.a="ℜ","A";case 297:return a.a= +"⤰","OP";case 298:return a.a="⤫","OP";case 299:return a.a="⌉","OPFS";case 300:return a.a="]","OPFS";case 301:return a.a="}","OPFS";case 302:return a.a="⟩","OPFS";case 303:return a.a="⟩","OPFS";case 304:return a.a="≟","OP";case 305:return a.a="⨌","OP";case 306:return"QUAD";case 307:return"QQUAD";case 308:return a.a="▪","OP";case 309:return a.a="Ψ","AIUG";case 310:return a.a="ψ","AILG";case 311:return a.a="∝","OP";case 312:return a.a="∏","OPM";case 313:return a.a="∏","OPM";case 314:return a.a="′","OPP"; +case 315:return a.a="≾","OP";case 316:return a.a="⋨","OP";case 317:return a.a="⪵","OP";case 318:return a.a="⪹","OP";case 319:return a.a="⪯","OP";case 320:return a.a="≼","OP";case 321:return a.a="⪷","OP";case 322:return a.a="≺","OP";case 323:return"PMOD";case 324:return a.a="±","OP";case 325:return a.a="⨥","OP";case 326:return a.a="⊞","OP";case 327:return a.a="⋔","OP";case 328:return a.a="Π","AIUG";case 329:return a.a="π","AILG";case 330:return a.a="Φ","AIUG";case 331:return a.a="ϕ","AILG";case 332:return"PHANTOM"; +case 333:return a.a="⫫","OP";case 334:return a.a="⊥","OP";case 335:return a.a="⪣","OP";case 336:return a.a="∂","OP";case 337:return a.a="⅋","OP";case 338:return a.a="∥","OP";case 339:return this.pushState("TEXTARG"),"PADDING";case 340:return"OVERSET";case 341:return a.a="¯","ACCENT";case 342:return"OVERBRACE";case 343:return"TEXOVER";case 344:return a.a="⨴","OP";case 345:return a.a="⊗","OP";case 346:return a.a="⊘","OP";case 347:return"OPS";case 348:return"OPP";case 349:return"OPM";case 350:return a.a= +"⨭","OP";case 351:return a.a="⊕","OP";case 352:return"OPFS";case 353:return"OPF";case 354:return this.j("TEXTARG"),"OPERATORNAME";case 355:return"OP";case 356:return a.a="⊖","OP";case 357:return a.a="ℴ","A";case 358:return a.a="Ω","AIUG";case 359:return a.a="ω","AILG";case 360:return a.a="∮","OP";case 361:return a.a="∯","OP";case 362:return a.a="∰","OP";case 363:return a.a="⊙","OP";case 364:return a.a="⊝","OP";case 365:return a.a="⦸","OP";case 366:return a.a="⤲","OP";case 367:return a.a="⇖","OPS"; +case 368:return a.a="↖","OPS";case 369:return a.a="⇖","OPS";case 370:return a.a="↖","OPS";case 371:return a.a="⊯","OP";case 372:return a.a="⊮","OP";case 373:return a.a="⊭","OP";case 374:return a.a="⊬","OP";case 375:return"NUM";case 376:return a.a="Ν","AIUG";case 377:return a.a="ν","AILG";case 378:return a.a="⋭","OP";case 379:return a.a="⋫","OP";case 380:return a.a="⋬","OP";case 381:return a.a="⋪","OP";case 382:return a.a="⊉","OP";case 383:return a.a="⊅","OP";case 384:return a.a="≿̸","OP";case 385:return a.a= +"⪰̸","OP";case 386:return a.a="⊁","OP";case 387:return a.a="⊈","OP";case 388:return a.a="⊈","OP";case 389:return a.a="⊄","OP";case 390:return a.a="≄","OP";case 391:return a.a="≁","OP";case 392:return a.a="∦","OP";case 393:return a.a="∤","OP";case 394:return a.a="⇏","OP";case 395:return a.a="↛","OP";case 396:return a.a="⪯̸","OP";case 397:return a.a="⊀","OP";case 398:return a.a="∦","OP";case 399:return a.a="∌","OP";case 400:return a.a="∉","OP";case 401:return a.a="¬","OP";case 402:return a.a="∤","OP"; +case 403:return a.a="≮","OP";case 404:return a.a="⩽̸","OP";case 405:return a.a="⩽̸","OP";case 406:return a.a="≰","OP";case 407:return a.a="⇎","OP";case 408:return a.a="↮","OP";case 409:return a.a="⇍","OP";case 410:return a.a="↚","OP";case 411:return a.a="∋","OP";case 412:return a.a="≯","OP";case 413:return a.a="⩾̸","OP";case 414:return a.a="⩾̸","OP";case 415:return a.a="≱","OP";case 416:return a.a="∄","OP";case 417:return a.a="≢","OP";case 418:return a.a="≂̸","OP";case 419:return a.a="≠","OP";case 420:return a.a= +"⤮","OP";case 421:return a.a="⤱","OP";case 422:return"NEGTHICKSPACE";case 423:return"NEGSPACE";case 424:return"NEGMEDSPACE";case 425:return a.a="¬","OP";case 426:return a.a="⇗","OPS";case 427:return a.a="↗","OPS";case 428:return a.a="⇗","OPS";case 429:return a.a="↗","OPS";case 430:return a.a="≠","OP";case 431:return a.a="≇","OP";case 432:return a.a="≎̸","OP";case 433:return a.a="≏̸","OP";case 434:return a.a="♮","OP";case 435:return a.a="≉","OP";case 436:return a.a="∇","OP";case 437:return"MULTI"; +case 438:return a.a="⊸","OP";case 439:return a.a="Μ","AIUG";case 440:return a.a="μ","AILG";case 441:return this.j("TEXTARG"),"MTEXT";case 442:return this.pushState("TEXTARG"),this.pushState("TEXTOPTARG"),this.pushState("TRYOPTARG"),this.pushState("TEXTOPTARG"),this.pushState("TRYOPTARG"),"MS";case 443:return a.a="∓","OP";case 444:return a.a="⊧","OP";case 445:return a.a="mod","MO";case 446:return this.pushState("TEXTARG"),"MO";case 447:return this.pushState("TEXTARG"),"MN";case 448:return a.a="⫛", +"OP";case 449:return a.a="⨪","OP";case 450:return a.a="⊟","OP";case 451:return a.a="−","OP";case 452:return a.a=a.a.slice(1),"FM";case 453:return a.a="∣","OP";case 454:return this.pushState("TEXTARG"),"MI";case 455:return a.a="℧","A";case 456:return a.a="℧","A";case 457:return"MEDSPACE";case 458:return a.a="∡","OP";case 459:return"MATHTT";case 460:return"MATHSF";case 461:return"MATHSCR";case 462:return"MATHRM";case 463:return"MATHRLAP";case 464:return this.j("TEXTARG"),"MATHREL";case 465:return this.pushState("TEXTOPTARG"), +this.pushState("TRYOPTARG"),this.pushState("TEXTOPTARG"),this.pushState("TRYOPTARG"),this.pushState("TEXTARG"),"MATHRAISEBOX";case 466:return this.j("TEXTARG"),"MATHOP";case 467:return"MATHIT";case 468:return"MATHLLAP";case 469:return"MATHIT";case 470:return"MATHFRAK";case 471:return"MATHFRAK";case 472:return"MATHCLAP";case 473:return"MATHSCR";case 474:return"MATHBSCR";case 475:return"MATHBIT";case 476:return this.j("TEXTARG"),"MATHBIN";case 477:return"MATHBF";case 478:return"MATHBSCR";case 479:return"MATHBB"; +case 480:return a.a="⤇","OP";case 481:return a.a="↦","OPS";case 482:return a.a="⤆","OP";case 483:return a.a="↦","OPS";case 484:return a.a="≨︀","OP";case 485:return a.a="≨︀","OP";case 486:return a.a="⋉","OP";case 487:return a.a="<","OP";case 488:return a.a="↰","OPS";case 489:return a.a="‘","OPF";case 490:return a.a="◊","OP";case 491:return a.a="⨜","OP";case 492:return a.a="↬","OPS";case 493:return a.a="↫","OPS";case 494:return a.a="⟹","OPS";case 495:return a.a="⟶","OPS";case 496:return a.a="⟼","OPS"; +case 497:return a.a="⟺","OPS";case 498:return a.a="⟷","OPS";case 499:return a.a="⟸","OPS";case 500:return a.a="⟵","OPS";case 501:return a.a="⋦","OP";case 502:return a.a="≨","OP";case 503:return a.a="⪇","OP";case 504:return a.a="⪉","OP";case 505:return a.a="⎰","OP";case 506:return a.a="⋘","OP";case 507:return a.a="⇚","OPS";case 508:return a.a="⟪","OPFS";case 509:return a.a="≪","OP";case 510:return a.a="⊲","OP";case 511:return a.a="⌊","OPFS";case 512:return a.a="≲","OP";case 513:return a.a="≶","OP"; +case 514:return a.a="⪋","OP";case 515:return a.a="⋚","OP";case 516:return a.a="⋖","OP";case 517:return a.a="⪅","OP";case 518:return a.a="<","OP";case 519:return a.a="⩽","OP";case 520:return a.a="≦","OP";case 521:return a.a="≤","OP";case 522:return a.a="⟳","OP";case 523:return a.a="⋋","OP";case 524:return a.a="↜","OPS";case 525:return a.a="↭","OPS";case 526:return a.a="⇋","OPS";case 527:return a.a="⇿","OPS";case 528:return a.a="⇆","OPS";case 529:return a.a="⇔","OPS";case 530:return a.a="↔","OPS";case 531:return a.a= +"⇇","OPS";case 532:return a.a="↼","OPS";case 533:return a.a="↽","OPS";case 534:return a.a="⇽","OPS";case 535:return a.a="↢","OPS";case 536:return a.a="⇐","OPS";case 537:return a.a="←","OPS";case 538:return"LEFT";case 539:return a.a="≤","OP";case 540:return a.a="…","OP";case 541:return a.a="⌈","OPFS";case 542:return a.a="[","OPFS";case 543:return a.a="{","OPFS";case 544:return a.a="⟨","OPFS";case 545:return a.a="⟨","OPFS";case 546:return a.a="Λ","AIUG";case 547:return a.a="λ","AILG";case 548:return a.a= +"∻","OP";case 549:return a.a="Κ","AIUG";case 550:return a.a="κ","AILG";case 551:return a.a="ȷ","AILL";case 552:return this.pushState("TEXTARG"),"MN";case 553:return a.a="Ι","AIUG";case 554:return a.a="ι","AILG";case 555:return a.a="⅋","OP";case 556:return a.a="⨘","OP";case 557:return a.a="⨽","OP";case 558:return a.a="⨼","OP";case 559:return a.a="⋂","OPM";case 560:return a.a="∩","OP";case 561:return a.a="⫴","OP";case 562:return a.a="⊺","OP";case 563:return a.a="∫","OP";case 564:return a.a="⨚","OP"; +case 565:return a.a="⨙","OP";case 566:return a.a="⨎","OP";case 567:return a.a="⨍","OP";case 568:return a.a="∫","OP";case 569:return a.a="∞","NUM";case 570:return a.a="∞","NUM";case 571:return a.a=a.a.slice(1),"FM";case 572:return a.a="∊","OP";case 573:return a.a="⇒","OPS";case 574:return a.a="⇐","OPS";case 575:return a.a="ı","AILL";case 576:return a.a="ℑ","A";case 577:return a.a="∬","OP";case 578:return a.a="∭","OP";case 579:return a.a="⨌","OP";case 580:return a.a="⟺","OPS";case 581:return a.a="ℏ", +"A";case 582:return this.pushState("TEXTARG"),"HREF";case 583:return a.a="↪","OPS";case 584:return a.a="↩","OPS";case 585:return a.a="⤦","OP";case 586:return a.a="⤥","OP";case 587:return a.a="♡","OP";case 588:return a.a="ℏ","A";case 589:return a.a="^","ACCENTNS";case 590:return a.a="≩︀","OP";case 591:return a.a="≩︀","OP";case 592:return a.a="≳","OP";case 593:return a.a="≷","OP";case 594:return a.a="⪌","OP";case 595:return a.a="⋛","OP";case 596:return a.a="⋗","OP";case 597:return a.a="⪆","OP";case 598:return a.a= +">","OP";case 599:return a.a=">","OP";case 600:return a.a="⋧","OP";case 601:return a.a="≩","OP";case 602:return a.a="⪈","OP";case 603:return a.a="⪊","OP";case 604:return a.a="ℷ","A";case 605:return a.a="⋙","OP";case 606:return a.a="≫","OP";case 607:return a.a="⩾","OP";case 608:return a.a="≧","OP";case 609:return a.a="≥","OP";case 610:return a.a="≥","OP";case 611:return a.a="Γ","AIUG";case 612:return a.a="γ","AILG";case 613:return a.a="⌢","OP";case 614:return this.pushState("TEXTARG"),"FRAME";case 615:return"FRAC"; +case 616:return a.a="⫝","OP";case 617:return a.a="⫝̸","OP";case 618:return a.a="∀","OP";case 619:return a.a="♭","OP";case 620:return a.a="⤬","OP";case 621:return a.a="⤯","OP";case 622:return a.a="≒","OP";case 623:return a.a="∃","OP";case 624:return a.a="ð","A";case 625:return a.a="ð","A";case 626:return a.a="Η","AIUG";case 627:return a.a="η","AILG";case 628:return a.a="≡","OP";case 629:return this.pushState("TEXTARG"),"EQROWS";case 630:return this.pushState("TEXTARG"),"EQCOLS";case 631:return a.a= +"⪕","OP";case 632:return a.a="⪖","OP";case 633:return a.a="≂","OP";case 634:return a.a="=∷","OP";case 635:return a.a="≕","OP";case 636:return a.a="−∷","OP";case 637:return a.a="=∷","OP";case 638:return a.a="=∷","OP";case 639:return a.a="=∷","OP";case 640:return a.a="≕","OP";case 641:return a.a="≖","OP";case 642:return a.a="ϵ","AILG";case 643:return"EVVMATRIX";case 644:return"EVMATRIX";case 645:return"ETOGGLE";case 646:return"EALIGNED";case 647:return"ESMALLMATRIX";case 648:return"EPMATRIX";case 649:return"EMATRIX"; +case 650:return"EGATHERED";case 651:return"ECASES";case 652:return"EBBMATRIX";case 653:return"EBMATRIX";case 654:return"EARRAY";case 655:return"EALIGNED";case 656:return a.a="∅","A";case 657:return a.a="∅","A";case 658:return a.a="↪","OPS";case 659:return a.a="ℓ","A";case 660:return a.a="↕","OPS";case 661:return a.a="⧟","OP";case 662:return a.a="⤐","OPS";case 663:return a.a="↕","OPS";case 664:return a.a="⇂","OPS";case 665:return a.a="⇃","OPS";case 666:return a.a="⇊","OPS";case 667:return a.a="⇓", +"OPS";case 668:return a.a="↓","OPS";case 669:return a.a="∬","OP";case 670:return a.a="⩞","OP";case 671:return a.a="⌆","OP";case 672:return a.a="…","OP";case 673:return a.a="∔","OP";case 674:return a.a="∸","OP";case 675:return a.a="≑","OP";case 676:return a.a="≑","OP";case 677:return a.a="≐","OP";case 678:return a.a="˙","ACCENT";case 679:return a.a="⋇","OP";case 680:return a.a="÷","OP";case 681:return"DISPLAYSTYLE";case 682:return a.a="⨈","OPM";case 683:return a.a="ϝ","A";case 684:return a.a="♢","OP"; +case 685:return a.a="⋄","OP";case 686:return a.a="⋄","OP";case 687:return a.a=a.a.slice(1),"FM";case 688:return a.a="Δ","AIUG";case 689:return a.a="δ","AILG";case 690:return a.a="∇","OP";case 691:return a.a="°","OP";case 692:return a.a="⤋","OPS";case 693:return a.a="⩷","OP";case 694:return a.a="⋱","OP";case 695:return a.a="̈","ACCENT";case 696:return a.a="⃛","OP";case 697:return a.a="⃛","ACCENT";case 698:return a.a="⃜","OP";case 699:return a.a="⃜","ACCENT";case 700:return a.a="‡","OP";case 701:return a.a= +"∷","OP";case 702:return a.a="⤏","OPS";case 703:return a.a="⫤","OP";case 704:return a.a="⫣","OP";case 705:return a.a="⊣","OP";case 706:return a.a="⤏","OPS";case 707:return a.a="⤎","OPS";case 708:return a.a="↓","OPS";case 709:return a.a="ℸ","A";case 710:return a.a="†","OP";case 711:return a.a="↷","OP";case 712:return a.a="↶","OP";case 713:return a.a="⤻","OP";case 714:return a.a="⋏","OP";case 715:return a.a="⋎","OP";case 716:return a.a="⋟","OP";case 717:return a.a="⋞","OP";case 718:return a.a="⊍","OP"; +case 719:return a.a="⋓","OP";case 720:return a.a="∪","OP";case 721:return a.a="∐","OPM";case 722:return a.a="∐","OPM";case 723:return a.a="∮","OP";case 724:return a.a="⨇","OPM";case 725:return a.a="∮","OP";case 726:return a.a="≅","OP";case 727:return a.a="∁","OP";case 728:return this.j("TEXTARG"),"COLSPAN";case 729:return this.pushState("TEXTARG"),"COLOR";case 730:return a.a="∷∼","OP";case 731:return a.a="∶∼","OP";case 732:return a.a="⩴","OP";case 733:return a.a="≔","OP";case 734:return a.a="∷−", +"OP";case 735:return a.a="≔","OP";case 736:return a.a="∷≈","OP";case 737:return a.a="∶≈","OP";case 738:return a.a="∷","OP";case 739:return a.a=":","OP";case 740:return this.pushState("TEXTARG"),"COLLINES";case 741:return this.pushState("TEXTARG"),"COLLAYOUT";case 742:return this.j("TEXTARG"),"COLALIGN";case 743:return a.a="♣","OP";case 744:return a.a="¯","ACCENT";case 745:return a.a="⊝","OP";case 746:return a.a="⊚","OP";case 747:return a.a="⊛","OP";case 748:return a.a="⥁","OP";case 749:return a.a= +"⥀","OP";case 750:return a.a="≗","OP";case 751:return a.a="∘","OP";case 752:return"TEXCHOOSE";case 753:return a.a="χ","AILG";case 754:return a.a="ˇ","ACCENTNS";case 755:return"CELLOPTS";case 756:return a.a="⋯","OP";case 757:return a.a="·","OP";case 758:return a.a="⋅","OP";case 759:return a.a="⋒","OP";case 760:return a.a="∩","OP";case 761:return a.a="⪮","OP";case 762:return a.a="≎","OP";case 763:return a.a="≏","OP";case 764:return a.a="•","OP";case 765:return a.a="⨲","OP";case 766:return a.a="⊠","OP"; +case 767:return a.a="⊞","OP";case 768:return a.a="⊟","OP";case 769:return"BOXED";case 770:return a.a="⊡","OP";case 771:return a.a="⧄","OP";case 772:return a.a="⧇","OP";case 773:return a.a="⧅","OP";case 774:return a.a="⧆","OP";case 775:return a.a="□","OP";case 776:return a.a="⋈","OP";case 777:return a.a="⊥","OP";case 778:return a.a="⊥","OP";case 779:return"MATHBF";case 780:return a.a="▸","OP";case 781:return a.a="◂","OP";case 782:return a.a="▾","OP";case 783:return a.a="▴","OP";case 784:return a.a= +"■","OP";case 785:return a.a="⧫","OP";case 786:return a.a="⤍","OPS";case 787:return"BINOM";case 788:return a.a="⋀","OPM";case 789:return a.a="⋁","OPM";case 790:return a.a="⨄","OPM";case 791:return a.a="△","OP";case 792:return a.a="▽","OP";case 793:return a.a="⨉","OPM";case 794:return a.a="★","OP";case 795:return a.a="⨆","OPM";case 796:return a.a="⨅","OPM";case 797:return"BBIG";case 798:return"BIG";case 799:return a.a="⨂","OPM";case 800:return a.a="⨁","OPM";case 801:return a.a="⨀","OPM";case 802:return"BBIGL"; +case 803:return"BIGL";case 804:return a.a="⫼","OPM";case 805:return"BBIGG";case 806:return"BIGG";case 807:return"BBIGGL";case 808:return"BIGGL";case 809:return"BBIGG";case 810:return"BIGG";case 811:return a.a="⨃","OPM";case 812:return a.a="⋃","OPM";case 813:return a.a="○","OP";case 814:return a.a="⋂","OPM";case 815:return"BBIG";case 816:return"BIG";case 817:return this.pushState("TEXTARG"),"BGCOLOR";case 818:return a.a="≬","OP";case 819:return a.a="ℶ","A";case 820:return a.a="Β","AIUG";case 821:return a.a= +"β","AILG";case 822:return"BVVMATRIX";case 823:return"BVMATRIX";case 824:return"BTOGGLE";case 825:return"BALIGNED";case 826:return"BSMALLMATRIX";case 827:return"BPMATRIX";case 828:return"BMATRIX";case 829:return"BGATHERED";case 830:return"BCASES";case 831:return"BBBMATRIX";case 832:return"BBMATRIX";case 833:return this.pushState("TEXTARG"),this.pushState("TEXTOPTARG"),this.pushState("TRYOPTARG"),"BARRAY";case 834:return"BALIGNED";case 835:return a.a="∵","OP";case 836:return a.a="ℿ","A";case 837:return a.a= +"⌅","OP";case 838:return a.a="¯","ACCENTNS";case 839:return a.a="\\","OP";case 840:return a.a="⋍","OP";case 841:return a.a="∽","OP";case 842:return a.a="‵","OPP";case 843:return a.a="϶","OP";case 844:return"TEXATOP";case 845:return a.a="≍","OP";case 846:return a.a="∗","OP";case 847:return"ARRAYOPTS";case 848:return"ARRAY";case 849:return a.a=a.a.slice(1),"F";case 850:return a.a="≊","OP";case 851:return a.a="≈","OP";case 852:return a.a="∠","OP";case 853:return a.a="⨿","OP";case 854:return a.a="Α", +"AIUG";case 855:return a.a="α","AILG";case 856:return this.pushState("TEXTARG"),"ALIGN";case 857:return a.a="ℵ","A";case 858:return"AIUL";case 859:return"AIUG";case 860:return"AILL";case 861:return"AILG";case 862:return a.a="⋰","OP";case 863:return a.a="Å","A";case 864:return"A";case 865:return a.a="$","A";case 866:return a.a="}","OPFS";case 867:return a.a="‖","OPFS";case 868:return a.a="{","OPFS";case 869:return"THICKSPACE";case 870:return"MEDSPACE";case 871:return"THINSPACE";case 872:return a.a= +"&","A";case 873:return a.a="%","A";case 874:return a.a="#","OP";case 875:return"NEGSPACE";case 876:return a.a="−","OP";case 877:return a.a="⁗","OPP";case 878:return a.a="‴","OPP";case 879:return a.a="″","OPP";case 880:return a.a="′","OPP";case 881:return"HIGH_SURROGATE";case 882:return"LOW_SURROGATE";case 883:return"BMP_CHARACTER"}},rules:[/^(?:.)/,/^(?:\$\$|\\\[|\$|\\\()/,/^(?:$)/,/^(?:\\[$\\])/,/^(?:[<&>])/,/^(?:[^])/,/^(?:\s*\[)/,/^(?:.)/,/^(?:([^\\\]]|(\\[\\\]]))+)/,/^(?:\])/,/^(?:\s*\{)/,/^(?:([^\\\}]|(\\[\\\}]))+)/, +/^(?:\})/,/^(?:\])/,/^(?:\s+)/,/^(?:\$\$|\\\]|\$|\\\))/,/^(?:\{)/,/^(?:\})/,/^(?:\^)/,/^(?:_)/,/^(?:\.)/,/^(?:&)/,/^(?:\\\\)/,/^(?:[0-9]+(?:\.[0-9]+)?|[\u0660-\u0669]+(?:\u066B[\u0660-\u0669]+)?|(?:\uD835[\uDFCE-\uDFD7])+|(?:\uD835[\uDFD8-\uDFE1])+|(?:\uD835[\uDFE2-\uDFEB])+|(?:\uD835[\uDFEC-\uDFF5])+|(?:\uD835[\uDFF6-\uDFFF])+)/,/^(?:[a-zA-Z]+)/,/^(?:\\Zeta)/,/^(?:\\zeta)/,/^(?:\\xrightleftharpoons)/,/^(?:\\xRightarrow)/,/^(?:\\xrightarrow)/,/^(?:\\xmapsto)/,/^(?:\\xleftrightharpoons)/,/^(?:\\xLeftrightarrow)/, +/^(?:\\xleftrightarrow)/,/^(?:\\xLeftarrow)/,/^(?:\\xleftarrow)/,/^(?:\\Xi)/,/^(?:\\xi)/,/^(?:\\xhookrightarrow)/,/^(?:\\xhookleftarrow)/,/^(?:\\wr)/,/^(?:\\wp)/,/^(?:\\widevec)/,/^(?:\\widetilde)/,/^(?:\\widehat)/,/^(?:\\widecheck)/,/^(?:\\widebar)/,/^(?:\\wedgeq)/,/^(?:\\Wedge)/,/^(?:\\wedge)/,/^(?:\\Vvert)/,/^(?:\\Vvdash)/,/^(?:\\Vert)/,/^(?:\\vert)/,/^(?:\\veebar)/,/^(?:\\Vee)/,/^(?:\\vee)/,/^(?:\\vec)/,/^(?:\\vdots)/,/^(?:\\VDash)/,/^(?:\\Vdash)/,/^(?:\\vDash)/,/^(?:\\vdash)/,/^(?:\\Vbar)/,/^(?:\\vartriangleright)/, +/^(?:\\vartriangleleft)/,/^(?:\\vartriangle)/,/^(?:\\vartheta)/,/^(?:\\varsupsetneqq)/,/^(?:\\varsupsetneq)/,/^(?:\\varsubsetneqq)/,/^(?:\\varsubsetneqq)/,/^(?:\\varsubsetneq)/,/^(?:\\varsigma)/,/^(?:\\varrho)/,/^(?:\\varpropto)/,/^(?:\\varpi)/,/^(?:\\varphi)/,/^(?:\\varnothing)/,/^(?:\\varkappa)/,/^(?:\\varepsilon)/,/^(?:\\Uuparrow)/,/^(?:\\upuparrows)/,/^(?:\\Upsilon)/,/^(?:\\upsilon)/,/^(?:\\Upsi)/,/^(?:\\uplus)/,/^(?:\\upint)/,/^(?:\\upharpoonright)/,/^(?:\\upharpoonleft)/,/^(?:\\Updownarrow)/, +/^(?:\\updownarrow)/,/^(?:\\updarr)/,/^(?:\\Uparrow)/,/^(?:\\uparrow)/,/^(?:\\uparr)/,/^(?:\\unrhd)/,/^(?:\\unlhd)/,/^(?:\\Union)/,/^(?:\\union)/,/^(?:\\underset)/,/^(?:\\underoverset)/,/^(?:\\underline)/,/^(?:\\underbrace)/,/^(?:\\udots)/,/^(?:\u2ADD\u0338)/,/^(?:\u2ACC\uFE00)/,/^(?:\u2ACB\uFE00)/,/^(?:\u2AB0\u0338)/,/^(?:\u2AAF\u0338)/,/^(?:\u2AA2\u0338)/,/^(?:\u2AA1\u0338)/,/^(?:\u2A7E\u0338)/,/^(?:\u2A7D\u0338)/,/^(?:\u29D0\u0338)/,/^(?:\u29CF\u0338)/,/^(?:\u2290\u0338)/,/^(?:\u228F\u0338)/,/^(?:\u228B\uFE00)/, +/^(?:\u228A\uFE00)/,/^(?:\u2283\u20D2)/,/^(?:\u2282\u20D2)/,/^(?:\u227F\u0338)/,/^(?:\u226B\u0338)/,/^(?:\u226A\u0338)/,/^(?:\u2269\uFE00)/,/^(?:\u2268\uFE00)/,/^(?:\u2266\u0338)/,/^(?:\u224F\u0338)/,/^(?:\u224E\u0338)/,/^(?:\u2242\u0338)/,/^(?:\u223D\u0331)/,/^(?:\u2237\u2248)/,/^(?:\u2237\u223C)/,/^(?:\u2237\u2212)/,/^(?:\u2236\u2248)/,/^(?:\u2236\u223C)/,/^(?:\u2212\u2237)/,/^(?:\u007C\u007C\u007C)/,/^(?:\u007C\u007C)/,/^(?:\u003E\u003D)/,/^(?:\u003D\u2237)/,/^(?:\u003D\u2237)/,/^(?:\u003D\u003D)/, +/^(?:\u003C\u003E)/,/^(?:\u003C\u003D)/,/^(?:\u003A\u003D)/,/^(?:\u002F\u003D)/,/^(?:\u002F\u002F)/,/^(?:\u002E\u002E\u002E)/,/^(?:\u002E\u002E)/,/^(?:\u002D\u003E)/,/^(?:\u002D\u003D)/,/^(?:\u002D\u002D)/,/^(?:\u002B\u003D)/,/^(?:\u002B\u002B)/,/^(?:\u002A\u003D)/,/^(?:\u002A\u002A)/,/^(?:\u0026\u0026)/,/^(?:\u0021\u003D)/,/^(?:\u0021\u0021)/,/^(?:\\twoheadrightarrowtail)/,/^(?:\\twoheadrightarrow)/,/^(?:\\twoheadleftarrow)/,/^(?:\\tripleintegral)/,/^(?:\\trianglerighteq)/,/^(?:\\triangleright)/, +/^(?:\\triangleq)/,/^(?:\\trianglelefteq)/,/^(?:\\triangleleft)/,/^(?:\\triangledown)/,/^(?:\\triangle)/,/^(?:\\towa)/,/^(?:\\tosa)/,/^(?:\\top)/,/^(?:\\tooltip)/,/^(?:\\tona)/,/^(?:\\toggle)/,/^(?:\\toea)/,/^(?:\\to)/,/^(?:\\timesb)/,/^(?:\\times)/,/^(?:\\tilde)/,/^(?:\\thinspace)/,/^(?:\\thickspace)/,/^(?:\\thicksim)/,/^(?:\\thickapprox)/,/^(?:\\Theta)/,/^(?:\\theta)/,/^(?:\\therefore)/,/^(?:\\tfrac)/,/^(?:\\textstyle)/,/^(?:\\textsize)/,/^(?:\\textquotedblright)/,/^(?:\\textquotedblleft)/,/^(?:\\textasciitilde)/, +/^(?:\\textasciigrave)/,/^(?:\\textasciicircumflex)/,/^(?:\\textasciiacute)/,/^(?:\\text)/,/^(?:\\tensor)/,/^(?:\\tbinom)/,/^(?:\\Tau)/,/^(?:\\tau)/,/^(?:\\swArrow)/,/^(?:\\swarrow)/,/^(?:\\swArr)/,/^(?:\\swarr)/,/^(?:\\surd)/,/^(?:\\supsetneqq)/,/^(?:\\supsetneq)/,/^(?:\\supseteqq)/,/^(?:\\supseteq)/,/^(?:\\Supset)/,/^(?:\\supset)/,/^(?:\\sum)/,/^(?:\\succsim)/,/^(?:\\succnsim)/,/^(?:\\succneqq)/,/^(?:\\succnapprox)/,/^(?:\\succeq)/,/^(?:\\succcurlyeq)/,/^(?:\\succapprox)/,/^(?:\\succ)/,/^(?:\\substack)/, +/^(?:\\subsetneqq)/,/^(?:\\subsetneq)/,/^(?:\\subseteqq)/,/^(?:\\subseteq)/,/^(?:\\Subset)/,/^(?:\\subset)/,/^(?:\\statusline)/,/^(?:\\star)/,/^(?:\\stackrel)/,/^(?:\\sslash)/,/^(?:\\square)/,/^(?:\\sqsupseteq)/,/^(?:\\sqsupset)/,/^(?:\\sqsubseteq)/,/^(?:\\sqsubset)/,/^(?:\\sqrt)/,/^(?:\\sqcup)/,/^(?:\\sqcap)/,/^(?:\\sphericalangle)/,/^(?:\\spadesuit)/,/^(?:\\space)/,/^(?:\\smile)/,/^(?:\\smallsmile)/,/^(?:\\smallsetminus)/,/^(?:\\smallfrown)/,/^(?:\\slash)/,/^(?:\\simeq)/,/^(?:\\sim)/,/^(?:\\Sigma)/, +/^(?:\\sigma)/,/^(?:\\shuffle)/,/^(?:\\shortparallel)/,/^(?:\\shortmid)/,/^(?:\\sharp)/,/^(?:\\setminus)/,/^(?:\\seovnearrow)/,/^(?:\\seArrow)/,/^(?:\\searrow)/,/^(?:\\seArr)/,/^(?:\\searr)/,/^(?:\\scriptsize)/,/^(?:\\scriptscriptsize)/,/^(?:\\rtimes)/,/^(?:\\Rsh)/,/^(?:\\Rrightarrow)/,/^(?:\\rrangle)/,/^(?:\\rq)/,/^(?:\\rowspan)/,/^(?:\\rowopts)/,/^(?:\\rowlines)/,/^(?:\\rowalign)/,/^(?:\\root)/,/^(?:\\rmoustache)/,/^(?:\\risingdotseq)/,/^(?:\\righttoleftarrow)/,/^(?:\\rightthreetimes)/,/^(?:\\rightsquigarrow)/, +/^(?:\\rightrightarrows)/,/^(?:\\rightleftharpoons)/,/^(?:\\rightleftarrows)/,/^(?:\\rightharpoonup)/,/^(?:\\rightharpoondown)/,/^(?:\\rightarrowtriangle)/,/^(?:\\rightarrowtail)/,/^(?:\\Rightarrow)/,/^(?:\\rightarrow)/,/^(?:\\right)/,/^(?:\\Rho)/,/^(?:\\rho)/,/^(?:\\rhd)/,/^(?:\\rfloor)/,/^(?:\\Re)/,/^(?:\\rdiagovsearrow)/,/^(?:\\rdiagovfdiag)/,/^(?:\\rceil)/,/^(?:\\rbrack)/,/^(?:\\rbrace)/,/^(?:\\rangle)/,/^(?:\\rang)/,/^(?:\\questeq)/,/^(?:\\quadrupleintegral)/,/^(?:\\quad)/,/^(?:\\qquad)/,/^(?:\\qed)/, +/^(?:\\Psi)/,/^(?:\\psi)/,/^(?:\\propto)/,/^(?:\\product)/,/^(?:\\prod)/,/^(?:\\prime)/,/^(?:\\precsim)/,/^(?:\\precnsim)/,/^(?:\\precneqq)/,/^(?:\\precnapprox)/,/^(?:\\preceq)/,/^(?:\\preccurlyeq)/,/^(?:\\precapprox)/,/^(?:\\prec)/,/^(?:\\pmod)/,/^(?:\\pm)/,/^(?:\\plusdot)/,/^(?:\\plusb)/,/^(?:\\pitchfork)/,/^(?:\\Pi)/,/^(?:\\pi)/,/^(?:\\Phi)/,/^(?:\\phi)/,/^(?:\\phantom)/,/^(?:\\Perp)/,/^(?:\\perp)/,/^(?:\\partialmeetcontraction)/,/^(?:\\partial)/,/^(?:\\parr)/,/^(?:\\parallel)/,/^(?:\\padding)/, +/^(?:\\overset)/,/^(?:\\overline)/,/^(?:\\overbrace)/,/^(?:\\over)/,/^(?:\\Otimes)/,/^(?:\\otimes)/,/^(?:\\oslash)/,/^(?:[\u007E\u00AF\u02C6\u02C7\u02C9\u02CD\u02DC\u02F7\u0302\u203E\u2044\u2190-\u2199\u219C-\u21AD\u21AF-\u21B5\u21B9\u21BC-\u21CC\u21D0-\u21DD\u21E0-\u21F0\u21F3\u21F5\u21F6\u21FD-\u21FF\u2215\u221A\u23B4\u23B5\u23DC-\u23E1\u27F0\u27F1\u27F5-\u27FF\u290A-\u2910\u2912\u2913\u2921\u2922\u294E-\u2961\u296E\u296F\u2B45\u2B46])/,/^(?:[\u2032-\u2035\u2057])/,/^(?:[\u220F-\u2211\u22C0-\u22C3\u2A00-\u2A0A\u2A10-\u2A14\u2AFC\u2AFF])/, +/^(?:\\Oplus)/,/^(?:\\oplus)/,/^(?:[\u0028\u0029\u005B\u005D\u007C\u2016\u2308-\u230B\u2329\u232A\u2772\u2773\u27E6-\u27EF\u2980\u2983-\u2998\u29FC\u29FD])/,/^(?:[\u2018\u2019\u201C\u201D])/,/^(?:\\operatorname)/,/^(?:[\u0021-\u0023\u002A-\u002C\u002F\u003A-\u0040\u0060\u00A8\u00AA\u00AC\u00B0-\u00B4\u00B7-\u00BA\u00D7\u00F7\u02CA\u02CB\u02D8-\u02DA\u02DD\u0311\u03F6\u201A\u201B\u201E-\u2022\u2026\u2036\u2037\u2043\u2061-\u2064\u20DB\u20DC\u2145\u2146\u214B\u219A\u219B\u21AE\u21B6-\u21B8\u21BA\u21BB\u21CD-\u21CF\u21DE\u21DF\u21F1\u21F2\u21F4\u21F7-\u21FC\u2200-\u2204\u2206-\u220E\u2212-\u2214\u2216-\u2219\u221B-\u221D\u221F-\u22BF\u22C4-\u22FF\u2305\u2306\u2322\u2323\u23B0\u23B1\u25A0\u25A1\u25AA\u25AB\u25AD-\u25B9\u25BC-\u25CF\u25D6\u25D7\u25E6\u2605\u2660-\u2663\u266D-\u266F\u2758\u27F2\u27F3\u2900-\u2909\u2911\u2914-\u2920\u2923-\u294D\u2962-\u296D\u2970-\u297F\u2981\u2982\u2999-\u29D9\u29DB-\u29FB\u29FE\u29FF\u2A0B-\u2A0F\u2A15-\u2ADB\u2ADD-\u2AFB\u2AFD\u2AFE])/, +/^(?:\\ominus)/,/^(?:\\omicron)/,/^(?:\\Omega)/,/^(?:\\omega)/,/^(?:\\oint)/,/^(?:\\oiint)/,/^(?:\\oiiint)/,/^(?:\\odot)/,/^(?:\\odash)/,/^(?:\\obslash)/,/^(?:\\nwovnearrow)/,/^(?:\\nwArrow)/,/^(?:\\nwarrow)/,/^(?:\\nwArr)/,/^(?:\\nwarr)/,/^(?:\\nVDash)/,/^(?:\\nVdash)/,/^(?:\\nvDash)/,/^(?:\\nvdash)/,/^(?:\u221E)/,/^(?:\\Nu)/,/^(?:\\nu)/,/^(?:\\ntrianglerighteq)/,/^(?:\\ntriangleright)/,/^(?:\\ntrianglelefteq)/,/^(?:\\ntriangleleft)/,/^(?:\\nsupseteq)/,/^(?:\\nsupset)/,/^(?:\\nsuccsim)/,/^(?:\\nsucceq)/, +/^(?:\\nsucc)/,/^(?:\\nsubseteqq)/,/^(?:\\nsubseteq)/,/^(?:\\nsubset)/,/^(?:\\nsime)/,/^(?:\\nsim)/,/^(?:\\nshortparallel)/,/^(?:\\nshortmid)/,/^(?:\\nRightarrow)/,/^(?:\\nrightarrow)/,/^(?:\\npreceq)/,/^(?:\\nprec)/,/^(?:\\nparallel)/,/^(?:\\notni)/,/^(?:\\notin)/,/^(?:\\not)/,/^(?:\\nmid)/,/^(?:\\nless)/,/^(?:\\nleqslant)/,/^(?:\\nleqq)/,/^(?:\\nleq)/,/^(?:\\nLeftrightarrow)/,/^(?:\\nleftrightarrow)/,/^(?:\\nLeftarrow)/,/^(?:\\nleftarrow)/,/^(?:\\ni)/,/^(?:\\ngtr)/,/^(?:\\ngeqslant)/,/^(?:\\ngeqq)/, +/^(?:\\ngeq)/,/^(?:\\nexists)/,/^(?:\\nequiv)/,/^(?:\\neqsim)/,/^(?:\\neq)/,/^(?:\\neovsearrow)/,/^(?:\\neovnwarrow)/,/^(?:\\negthickspace)/,/^(?:\\negspace)/,/^(?:\\negmedspace)/,/^(?:\\neg)/,/^(?:\\neArrow)/,/^(?:\\nearrow)/,/^(?:\\neArr)/,/^(?:\\nearr)/,/^(?:\\ne)/,/^(?:\\ncong)/,/^(?:\\nBumpeq)/,/^(?:\\nbumpeq)/,/^(?:\\natural)/,/^(?:\\napprox)/,/^(?:\\nabla)/,/^(?:\\multiscripts)/,/^(?:\\multimap)/,/^(?:\\Mu)/,/^(?:\\mu)/,/^(?:\\mtext)/,/^(?:\\ms)/,/^(?:\\mp)/,/^(?:\\models)/,/^(?:\\mod)/,/^(?:\\mo)/, +/^(?:\\mn)/,/^(?:\\mlcp)/,/^(?:\\minusdot)/,/^(?:\\minusb)/,/^(?:\\minus)/,/^(?:\\min)/,/^(?:\\mid)/,/^(?:\\mi)/,/^(?:\\mho)/,/^(?:\\mho)/,/^(?:\\medspace)/,/^(?:\\measuredangle)/,/^(?:\\mathtt)/,/^(?:\\mathsf)/,/^(?:\\mathscr)/,/^(?:\\mathrm)/,/^(?:\\mathrlap)/,/^(?:\\mathrel)/,/^(?:\\mathraisebox)/,/^(?:\\mathop)/,/^(?:\\mathmit)/,/^(?:\\mathllap)/,/^(?:\\mathit)/,/^(?:\\mathfrak)/,/^(?:\\mathfr)/,/^(?:\\mathclap)/,/^(?:\\mathcal)/,/^(?:\\mathbscr)/,/^(?:\\mathbit)/,/^(?:\\mathbin)/,/^(?:\\mathbf)/, +/^(?:\\mathbcal)/,/^(?:\\mathbb)/,/^(?:\\Mapsto)/,/^(?:\\mapsto)/,/^(?:\\Mapsfrom)/,/^(?:\\map)/,/^(?:\\lvertneqq)/,/^(?:\\lvertneqq)/,/^(?:\\ltimes)/,/^(?:\\lt)/,/^(?:\\Lsh)/,/^(?:\\lq)/,/^(?:\\lozenge)/,/^(?:\\lowint)/,/^(?:\\looparrowright)/,/^(?:\\looparrowleft)/,/^(?:\\Longrightarrow)/,/^(?:\\longrightarrow)/,/^(?:\\longmapsto)/,/^(?:\\Longleftrightarrow)/,/^(?:\\longleftrightarrow)/,/^(?:\\Longleftarrow)/,/^(?:\\longleftarrow)/,/^(?:\\lnsim)/,/^(?:\\lneqq)/,/^(?:\\lneq)/,/^(?:\\lnapprox)/,/^(?:\\lmoustache)/, +/^(?:\\lll)/,/^(?:\\Lleftarrow)/,/^(?:\\llangle)/,/^(?:\\ll)/,/^(?:\\lhd)/,/^(?:\\lfloor)/,/^(?:\\lesssim)/,/^(?:\\lessgtr)/,/^(?:\\lesseqqgtr)/,/^(?:\\lesseqgtr)/,/^(?:\\lessdot)/,/^(?:\\lessapprox)/,/^(?:\\less)/,/^(?:\\leqslant)/,/^(?:\\leqq)/,/^(?:\\leq)/,/^(?:\\lefttorightarrow)/,/^(?:\\leftthreetimes)/,/^(?:\\leftsquigarrow)/,/^(?:\\leftrightsquigarrow)/,/^(?:\\leftrightharpoons)/,/^(?:\\leftrightarrowtria\*)/,/^(?:\\leftrightarrows)/,/^(?:\\Leftrightarrow)/,/^(?:\\leftrightarrow)/,/^(?:\\leftleftarrows)/, +/^(?:\\leftharpoonup)/,/^(?:\\leftharpoondown)/,/^(?:\\leftarrowtriangle)/,/^(?:\\leftarrowtail)/,/^(?:\\Leftarrow)/,/^(?:\\leftarrow)/,/^(?:\\left)/,/^(?:\\le)/,/^(?:\\ldots)/,/^(?:\\lceil)/,/^(?:\\lbrack)/,/^(?:\\lbrace)/,/^(?:\\langle)/,/^(?:\\lang)/,/^(?:\\Lambda)/,/^(?:\\lambda)/,/^(?:\\kernelcontraction)/,/^(?:\\Kappa)/,/^(?:\\kappa)/,/^(?:\\jmath)/,/^(?:\\itexnum)/,/^(?:\\Iota)/,/^(?:\\iota)/,/^(?:\\invamp)/,/^(?:\\intx)/,/^(?:\\intprodr)/,/^(?:\\intprod)/,/^(?:\\Intersection)/,/^(?:\\intersection)/, +/^(?:\\interleave)/,/^(?:\\intercal)/,/^(?:\\integral)/,/^(?:\\intcup)/,/^(?:\\intcap)/,/^(?:\\intBar)/,/^(?:\\intbar)/,/^(?:\\int)/,/^(?:\\infty)/,/^(?:\\infinity)/,/^(?:\\inf)/,/^(?:\\in)/,/^(?:\\implies)/,/^(?:\\impliedby)/,/^(?:\\imath)/,/^(?:\\Im)/,/^(?:\\iint)/,/^(?:\\iiint)/,/^(?:\\iiiint)/,/^(?:\\iff)/,/^(?:\\hslash)/,/^(?:\\href)/,/^(?:\\hookrightarrow)/,/^(?:\\hookleftarrow)/,/^(?:\\hkswarow)/,/^(?:\\hksearow)/,/^(?:\\heartsuit)/,/^(?:\\hbar)/,/^(?:\\hat)/,/^(?:\\gvertneqq)/,/^(?:\\gvertneqq)/, +/^(?:\\gtrsim)/,/^(?:\\gtrless)/,/^(?:\\gtreqqless)/,/^(?:\\gtreqless)/,/^(?:\\gtrdot)/,/^(?:\\gtrapprox)/,/^(?:\\gt)/,/^(?:\\greater)/,/^(?:\\gnsim)/,/^(?:\\gneqq)/,/^(?:\\gneq)/,/^(?:\\gnapprox)/,/^(?:\\gimel)/,/^(?:\\ggg)/,/^(?:\\gg)/,/^(?:\\geqslant)/,/^(?:\\geqq)/,/^(?:\\geq)/,/^(?:\\ge)/,/^(?:\\Gamma)/,/^(?:\\gamma)/,/^(?:\\frown)/,/^(?:\\frame)/,/^(?:\\frac)/,/^(?:\\forksnot)/,/^(?:\\forks)/,/^(?:\\forall)/,/^(?:\\flat)/,/^(?:\\fdiagovrdiag)/,/^(?:\\fdiagovnearrow)/,/^(?:\\fallingdotseq)/, +/^(?:\\exists)/,/^(?:\\eth)/,/^(?:\\eth)/,/^(?:\\Eta)/,/^(?:\\eta)/,/^(?:\\equiv)/,/^(?:\\equalrows)/,/^(?:\\equalcols)/,/^(?:\\eqslantless)/,/^(?:\\eqslantgtr)/,/^(?:\\eqsim)/,/^(?:\\Eqqcolon)/,/^(?:\\eqqcolon)/,/^(?:\\Eqcolon)/,/^(?:\\Eqcolon)/,/^(?:\\Eqcolon)/,/^(?:\\Eqcolon)/,/^(?:\\eqcolon)/,/^(?:\\eqcirc)/,/^(?:\\epsilon)/,/^(?:\\end\{Vmatrix\})/,/^(?:\\end\{vmatrix\})/,/^(?:\\endtoggle)/,/^(?:\\end\{split\})/,/^(?:\\end\{smallmatrix\})/,/^(?:\\end\{pmatrix\})/,/^(?:\\end\{matrix\})/,/^(?:\\end\{gathered\})/, +/^(?:\\end\{cases\})/,/^(?:\\end\{Bmatrix\})/,/^(?:\\end\{bmatrix\})/,/^(?:\\end\{array\})/,/^(?:\\end\{aligned\})/,/^(?:\\emptyset)/,/^(?:\\empty)/,/^(?:\\embedsin)/,/^(?:\\ell)/,/^(?:\\duparr)/,/^(?:\\dualmap)/,/^(?:\\drbkarrow)/,/^(?:\\downuparrow)/,/^(?:\\downharpoonright)/,/^(?:\\downharpoonleft)/,/^(?:\\downdownarrows)/,/^(?:\\Downarrow)/,/^(?:\\downarrow)/,/^(?:\\doubleintegral)/,/^(?:\\doublebarwedge)/,/^(?:\\doublebarwedge)/,/^(?:\\dots)/,/^(?:\\dotplus)/,/^(?:\\dotminus)/,/^(?:\\doteqdot)/, +/^(?:\\Doteq)/,/^(?:\\doteq)/,/^(?:\\dot)/,/^(?:\\divideontimes)/,/^(?:\\div)/,/^(?:\\displaystyle)/,/^(?:\\disjquant)/,/^(?:\\digamma)/,/^(?:\\diamondsuit)/,/^(?:\\Diamond)/,/^(?:\\diamond)/,/^(?:\\det|\\gcd|\\liminf|\\limsup|\\lim|\\max|\\Pr|\\sup)/,/^(?:\\Delta)/,/^(?:\\delta)/,/^(?:\\Del)/,/^(?:\\degree)/,/^(?:\\Ddownarrow)/,/^(?:\\ddotseq)/,/^(?:\\ddots)/,/^(?:\\ddot)/,/^(?:\\dddot)/,/^(?:\\dddot)/,/^(?:\\ddddot)/,/^(?:\\ddddot)/,/^(?:\\ddagger)/,/^(?:\\dblcolon)/,/^(?:\\dbkarow)/,/^(?:\\Dashv)/, +/^(?:\\dashV)/,/^(?:\\dashv)/,/^(?:\\dashrightarrow)/,/^(?:\\dashleftarrow)/,/^(?:\\darr)/,/^(?:\\daleth)/,/^(?:\\dagger)/,/^(?:\\curvearrowright)/,/^(?:\\curvearrowleft)/,/^(?:\\curvearrowbotright)/,/^(?:\\curlywedge)/,/^(?:\\curlyvee)/,/^(?:\\curlyeqsucc)/,/^(?:\\curlyeqprec)/,/^(?:\\cupdot)/,/^(?:\\Cup)/,/^(?:\\cup)/,/^(?:\\coproduct)/,/^(?:\\coprod)/,/^(?:\\contourintegral)/,/^(?:\\conjquant)/,/^(?:\\conint)/,/^(?:\\cong)/,/^(?:\\complement)/,/^(?:\\colspan)/,/^(?:\\color)/,/^(?:\\Colonsim)/, +/^(?:\\colonsim)/,/^(?:\\Coloneqq)/,/^(?:\\coloneqq)/,/^(?:\\Coloneq)/,/^(?:\\coloneq)/,/^(?:\\Colonapprox)/,/^(?:\\colonapprox)/,/^(?:\\Colon)/,/^(?:\\colon)/,/^(?:\\collines)/,/^(?:\\collayout)/,/^(?:\\colalign)/,/^(?:\\clubsuit)/,/^(?:\\closure)/,/^(?:\\circleddash)/,/^(?:\\circledcirc)/,/^(?:\\circledast)/,/^(?:\\circlearrowright)/,/^(?:\\circlearrowleft)/,/^(?:\\circeq)/,/^(?:\\circ)/,/^(?:\\choose)/,/^(?:\\chi)/,/^(?:\\check)/,/^(?:\\cellopts)/,/^(?:\\cdots)/,/^(?:\\cdotp)/,/^(?:\\cdot)/,/^(?:\\Cap)/, +/^(?:\\cap)/,/^(?:\\bumpeqq)/,/^(?:\\Bumpeq)/,/^(?:\\bumpeq)/,/^(?:\\bullet)/,/^(?:\\btimes)/,/^(?:\\boxtimes)/,/^(?:\\boxplus)/,/^(?:\\boxminus)/,/^(?:\\boxed)/,/^(?:\\boxdot)/,/^(?:\\boxdiag)/,/^(?:\\boxcircle)/,/^(?:\\boxbslash)/,/^(?:\\boxast)/,/^(?:\\Box)/,/^(?:\\bowtie)/,/^(?:\\bottom)/,/^(?:\\bot)/,/^(?:\\boldsymbol)/,/^(?:\\blacktriangleright)/,/^(?:\\blacktriangleleft)/,/^(?:\\blacktriangledown)/,/^(?:\\blacktriangle)/,/^(?:\\blacksquare)/,/^(?:\\blacklozenge)/,/^(?:\\bkarow)/,/^(?:\\binom)/, +/^(?:\\bigwedge)/,/^(?:\\bigvee)/,/^(?:\\biguplus)/,/^(?:\\bigtriangleup)/,/^(?:\\bigtriangledown)/,/^(?:\\bigtimes)/,/^(?:\\bigstar)/,/^(?:\\bigsqcup)/,/^(?:\\bigsqcap)/,/^(?:\\Bigr)/,/^(?:\\bigr)/,/^(?:\\bigotimes)/,/^(?:\\bigoplus)/,/^(?:\\bigodot)/,/^(?:\\Bigl)/,/^(?:\\bigl)/,/^(?:\\biginterleave)/,/^(?:\\Biggr)/,/^(?:\\biggr)/,/^(?:\\Biggl)/,/^(?:\\biggl)/,/^(?:\\Bigg)/,/^(?:\\bigg)/,/^(?:\\bigcupdot)/,/^(?:\\bigcup)/,/^(?:\\bigcirc)/,/^(?:\\bigcap)/,/^(?:\\Big)/,/^(?:\\big)/,/^(?:\\bgcolor)/, +/^(?:\\between)/,/^(?:\\beth)/,/^(?:\\Beta)/,/^(?:\\beta)/,/^(?:\\begin\{Vmatrix\})/,/^(?:\\begin\{vmatrix\})/,/^(?:\\begintoggle)/,/^(?:\\begin\{split\})/,/^(?:\\begin\{smallmatrix\})/,/^(?:\\begin\{pmatrix\})/,/^(?:\\begin\{matrix\})/,/^(?:\\begin\{gathered\})/,/^(?:\\begin\{cases\})/,/^(?:\\begin\{Bmatrix\})/,/^(?:\\begin\{bmatrix\})/,/^(?:\\begin\{array\})/,/^(?:\\begin\{aligned\})/,/^(?:\\because)/,/^(?:\\BbbPi)/,/^(?:\\barwedge)/,/^(?:\\bar)/,/^(?:\\backslash)/,/^(?:\\backsimeq)/,/^(?:\\backsim)/, +/^(?:\\backprime)/,/^(?:\\backepsilon)/,/^(?:\\atop)/,/^(?:\\asymp)/,/^(?:\\ast)/,/^(?:\\arrayopts)/,/^(?:\\array)/,/^(?:\\arccos|\\arcsin|\\arctan|\\arg|\\cosh|\\cos|\\coth|\\cot|\\csc|\\deg|\\dim|\\exp|\\hom|\\ker|\\lg|\\ln|\\log|\\sec|\\sinh|\\sin|\\tanh|\\tan)/,/^(?:\\approxeq)/,/^(?:\\approx)/,/^(?:\\angle)/,/^(?:\\amalg)/,/^(?:\\Alpha)/,/^(?:\\alpha)/,/^(?:\\align)/,/^(?:\\aleph)/,/^(?:[\u0041-\u005A])/,/^(?:[\u0391-\u03A1\u03A3\u03A4\u03A6-\u03A9])/,/^(?:[\u0061-\u007A\u0131\u0237])/,/^(?:[\u03B1-\u03C1\u03C3-\u03C9\u03D1\u03D5\u03D6\u03F0\u03F1\u03F4\u03F5])/, +/^(?:\\adots)/,/^(?:\\AA)/,/^(?:[\u00F0\u03C2\u03D0\u03D2\u03DA-\u03DD\u03E0\u03E1\u0428\u0608\u0627-\u063A\u2102\u210A-\u210D\u210F-\u2113\u2115\u2118-\u211D\u2124\u2127\u2128\u212B-\u212D\u212F-\u2131\u2133-\u2138\u213C\u213D\u213F\u2205]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB\uDEF0\uDEF1]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDFCB])/, +/^(?:\\\$)/,/^(?:\\\})/,/^(?:\\\|)/,/^(?:\\\{)/,/^(?:\\;)/,/^(?:\\:)/,/^(?:\\,)/,/^(?:\\&)/,/^(?:\\%)/,/^(?:\\#)/,/^(?:\\!)/,/^(?:-)/,/^(?:'''')/,/^(?:''')/,/^(?:'')/,/^(?:')/,/^(?:[\uD800-\uDBFF])/,/^(?:[\uDC00-\uDFFF])/,/^(?:.)/],M:{MATH0:{rules:[14,15,16,17,18,19,20,21,22,23,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98, +99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224, +225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350, +351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476, +477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,501,502,503,504,505,506,507,508,509,510,511,512,513,514,515,516,517,518,519,520,521,522,523,524,525,526,527,528,529,530,531,532,533,534,535,536,537,538,539,540,541,542,543,544,545,546,547,548,549,550,551,552,553,554,555,556,557,558,559,560,561,562,563,564,565,566,567,568,569,570,571,572,573,574,575,576,577,578,579,580,581,582,583,584,585,586,587,588,589,590,591,592,593,594,595,596,597,598,599,600,601,602, +603,604,605,606,607,608,609,610,611,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,627,628,629,630,631,632,633,634,635,636,637,638,639,640,641,642,643,644,645,646,647,648,649,650,651,652,653,654,655,656,657,658,659,660,661,662,663,664,665,666,667,668,669,670,671,672,673,674,675,676,677,678,679,680,681,682,683,684,685,686,687,688,689,690,691,692,693,694,695,696,697,698,699,700,701,702,703,704,705,706,707,708,709,710,711,712,713,714,715,716,717,718,719,720,721,722,723,724,725,726,727,728, +729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,747,748,749,750,751,752,753,754,755,756,757,758,759,760,761,762,763,764,765,766,767,768,769,770,771,772,773,774,775,776,777,778,779,780,781,782,783,784,785,786,787,788,789,790,791,792,793,794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809,810,811,812,813,814,815,816,817,818,819,820,821,822,823,824,825,826,827,828,829,830,831,832,833,834,835,836,837,838,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,854, +855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,880,881,882,883],inclusive:tb},MATH1:{rules:[14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124, +125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250, +251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376, +377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,501,502, +503,504,505,506,507,508,509,510,511,512,513,514,515,516,517,518,519,520,521,522,523,524,525,526,527,528,529,530,531,532,533,534,535,536,537,538,539,540,541,542,543,544,545,546,547,548,549,550,551,552,553,554,555,556,557,558,559,560,561,562,563,564,565,566,567,568,569,570,571,572,573,574,575,576,577,578,579,580,581,582,583,584,585,586,587,588,589,590,591,592,593,594,595,596,597,598,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,627,628, +629,630,631,632,633,634,635,636,637,638,639,640,641,642,643,644,645,646,647,648,649,650,651,652,653,654,655,656,657,658,659,660,661,662,663,664,665,666,667,668,669,670,671,672,673,674,675,676,677,678,679,680,681,682,683,684,685,686,687,688,689,690,691,692,693,694,695,696,697,698,699,700,701,702,703,704,705,706,707,708,709,710,711,712,713,714,715,716,717,718,719,720,721,722,723,724,725,726,727,728,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,747,748,749,750,751,752,753,754, +755,756,757,758,759,760,761,762,763,764,765,766,767,768,769,770,771,772,773,774,775,776,777,778,779,780,781,782,783,784,785,786,787,788,789,790,791,792,793,794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809,810,811,812,813,814,815,816,817,818,819,820,821,822,823,824,825,826,827,828,829,830,831,832,833,834,835,836,837,838,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,854,855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,880, +881,882,883],inclusive:tb},OPTARG:{rules:[13,14,15,16,17,18,19,20,21,22,23,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150, +151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276, +277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402, +403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,501,502,503,504,505,506,507,508,509,510,511,512,513,514,515,516,517,518,519,520,521,522,523,524,525,526,527,528, +529,530,531,532,533,534,535,536,537,538,539,540,541,542,543,544,545,546,547,548,549,550,551,552,553,554,555,556,557,558,559,560,561,562,563,564,565,566,567,568,569,570,571,572,573,574,575,576,577,578,579,580,581,582,583,584,585,586,587,588,589,590,591,592,593,594,595,596,597,598,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,627,628,629,630,631,632,633,634,635,636,637,638,639,640,641,642,643,644,645,646,647,648,649,650,651,652,653,654, +655,656,657,658,659,660,661,662,663,664,665,666,667,668,669,670,671,672,673,674,675,676,677,678,679,680,681,682,683,684,685,686,687,688,689,690,691,692,693,694,695,696,697,698,699,700,701,702,703,704,705,706,707,708,709,710,711,712,713,714,715,716,717,718,719,720,721,722,723,724,725,726,727,728,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,747,748,749,750,751,752,753,754,755,756,757,758,759,760,761,762,763,764,765,766,767,768,769,770,771,772,773,774,775,776,777,778,779,780, +781,782,783,784,785,786,787,788,789,790,791,792,793,794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809,810,811,812,813,814,815,816,817,818,819,820,821,822,823,824,825,826,827,828,829,830,831,832,833,834,835,836,837,838,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,854,855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,880,881,882,883],inclusive:tb},DOCUMENT:{rules:[1,2,3,4,5],inclusive:yb},TRYOPTARG:{rules:[6,7],inclusive:yb}, +TEXTOPTARG:{rules:[8,9],inclusive:yb},TEXTARG:{rules:[10,11,12],inclusive:yb},INITIAL:{rules:[0,14,15,16,17,18,19,20,21,22,23,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137, +138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263, +264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389, +390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,501,502,503,504,505,506,507,508,509,510,511,512,513,514,515, +516,517,518,519,520,521,522,523,524,525,526,527,528,529,530,531,532,533,534,535,536,537,538,539,540,541,542,543,544,545,546,547,548,549,550,551,552,553,554,555,556,557,558,559,560,561,562,563,564,565,566,567,568,569,570,571,572,573,574,575,576,577,578,579,580,581,582,583,584,585,586,587,588,589,590,591,592,593,594,595,596,597,598,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,627,628,629,630,631,632,633,634,635,636,637,638,639,640,641, +642,643,644,645,646,647,648,649,650,651,652,653,654,655,656,657,658,659,660,661,662,663,664,665,666,667,668,669,670,671,672,673,674,675,676,677,678,679,680,681,682,683,684,685,686,687,688,689,690,691,692,693,694,695,696,697,698,699,700,701,702,703,704,705,706,707,708,709,710,711,712,713,714,715,716,717,718,719,720,721,722,723,724,725,726,727,728,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,747,748,749,750,751,752,753,754,755,756,757,758,759,760,761,762,763,764,765,766,767, +768,769,770,771,772,773,774,775,776,777,778,779,780,781,782,783,784,785,786,787,788,789,790,791,792,793,794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809,810,811,812,813,814,815,816,817,818,819,820,821,822,823,824,825,826,827,828,829,830,831,832,833,834,835,836,837,838,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,854,855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,880,881,882,883],inclusive:tb}}}}();Rb.prototype=rb;rb.pa= +Rb;return new Rb}();window.TeXZilla=zb;window.TeXZilla.setDOMParser=zb.fa;window.TeXZilla.setXMLSerializer=zb.ja;window.TeXZilla.setSafeMode=zb.ia;window.TeXZilla.setItexIdentifierMode=zb.ha;window.TeXZilla.getTeXSource=zb.ca;window.TeXZilla.toMathMLString=zb.Z;window.TeXZilla.toMathML=zb.Y;window.TeXZilla.toImage=zb.na;window.TeXZilla.filterString=zb.Q;window.TeXZilla.filterElement=zb.P; +})(); diff --git a/comm/suite/editor/components/texzilla/jar.mn b/comm/suite/editor/components/texzilla/jar.mn new file mode 100644 index 0000000000..28d5a4cc3e --- /dev/null +++ b/comm/suite/editor/components/texzilla/jar.mn @@ -0,0 +1,6 @@ +# 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/. + +comm.jar: + content/editor/TeXZilla.js (content/TeXZilla.js) diff --git a/comm/suite/editor/components/texzilla/moz.build b/comm/suite/editor/components/texzilla/moz.build new file mode 100644 index 0000000000..de5cd1bf81 --- /dev/null +++ b/comm/suite/editor/components/texzilla/moz.build @@ -0,0 +1,6 @@ +# vim: set filetype=python: +# 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/. + +JAR_MANIFESTS += ["jar.mn"] |