diff options
Diffstat (limited to 'comm/mail/components/compose/content/dialogs/EdInsertMath.js')
-rw-r--r-- | comm/mail/components/compose/content/dialogs/EdInsertMath.js | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/comm/mail/components/compose/content/dialogs/EdInsertMath.js b/comm/mail/components/compose/content/dialogs/EdInsertMath.js new file mode 100644 index 0000000000..a60a3affcc --- /dev/null +++ b/comm/mail/components/compose/content/dialogs/EdInsertMath.js @@ -0,0 +1,317 @@ +/* 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 ../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.querySelector("dialog").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://messenger/content/messengercompose/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.firstElementChild); + 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 the <table> element with the <tr>. + var table = document.createElementNS( + "http://www.w3.org/1999/xhtml", + "table" + ); + + var i = 0, + row; + for (var command of commands) { + if (i % columnCount == 0) { + // Create a new row. + row = document.createElementNS("http://www.w3.org/1999/xhtml", "tr"); + table.appendChild(row); + } + + // Create a new button to insert the symbol. + var button = document.createXULElement("toolbarbutton"); + var td = document.createElementNS("http://www.w3.org/1999/xhtml", "td"); + button.setAttribute("class", "tabbable"); + button.appendChild(TeXZilla.toMathML(command)); + td.append(button); + row.appendChild(td); + + i++; + } + + // 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(table); + } +} + +function createSymbolPanels(aSymbolPanelList) { + const columnCount = 13, + tabLabelLength = 3; + + for (var symbols of aSymbolPanelList) { + // Create the <table> element with the <tr>. + var table = document.createElementNS( + "http://www.w3.org/1999/xhtml", + "table" + ); + var i = 0, + tabLabel = "", + row; + for (var symbol of symbols) { + if (i % columnCount == 0) { + // Create a new row. + row = document.createElementNS("http://www.w3.org/1999/xhtml", "tr"); + table.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"); + var td = document.createElementNS("http://www.w3.org/1999/xhtml", "td"); + button.setAttribute("label", symbol); + button.setAttribute("class", "tabbable"); + td.append(button); + row.appendChild(td); + + i++; + } + + // 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(table); + } +} + +function onAccept(event) { + if (gDialog.output.firstElementChild) { + var editor = GetCurrentEditor(); + editor.beginTransaction(); + + try { + var newMath = editor.document.importNode( + gDialog.output.firstElementChild, + 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.firstElementChild) { + gDialog.output.firstElementChild.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.firstElementChild; +} + +function updateMode() { + if (gDialog.output.firstElementChild) { + gDialog.output.firstElementChild.setAttribute( + "display", + gDialog.mode.selectedIndex ? "block" : "inline" + ); + } +} + +function updateDirection() { + if (gDialog.output.firstElementChild) { + gDialog.output.firstElementChild.setAttribute( + "dir", + gDialog.direction.selectedIndex ? "rtl" : "ltr" + ); + } +} |