xyc
2025-02-21 664db98c9e8595ce4dd636a27f480e3a08b81ff5
1
2
3
4
5
6
7
8
9
10
11
12
13
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
/* globals jQuery, MathJax */
/**
 * ext-mathjax.js
 *
 * @license Apache
 *
 * @copyright 2013 Jo Segaert
 *
 */
// Todo: Wait for Mathjax 3.0 to get ES Module/avoid global
import {importScript} from '../external/dynamic-import-polyfill/importModule.js';
 
export default {
  name: 'mathjax',
  async init ({importLocale}) {
    const strings = await importLocale();
    const svgEditor = this;
    const $ = jQuery;
    const svgCanvas = svgEditor.canvas;
 
    // Configuration of the MathJax extention.
 
    // This will be added to the head tag before MathJax is loaded.
    const /* mathjaxConfiguration = `<script type="text/x-mathjax-config">
        MathJax.Hub.Config({
          extensions: ['tex2jax.js'],
          jax: ['input/TeX', 'output/SVG'],
          showProcessingMessages: true,
          showMathMenu: false,
          showMathMenuMSIE: false,
          errorSettings: {
            message: ['[Math Processing Error]'],
            style: {color: '#CC0000', 'font-style': 'italic'}
          },
          elements: [],
            tex2jax: {
            ignoreClass: 'tex2jax_ignore2', processClass: 'tex2jax_process2',
          },
          TeX: {
            extensions: ['AMSmath.js', 'AMSsymbols.js', 'noErrors.js', 'noUndefined.js']
          },
          SVG: {
          }
      });
      </script>`, */
      // mathjaxSrc = 'http://cdn.mathjax.org/mathjax/latest/MathJax.js',
      // Had been on https://c328740.ssl.cf1.rackcdn.com/mathjax/latest/MathJax.js?config=TeX-AMS-MML_SVG.js
      // Obtained Text-AMS-MML_SVG.js from https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.3/config/TeX-AMS-MML_SVG.js
      mathjaxSrcSecure = 'mathjax/MathJax.min.js?config=TeX-AMS-MML_SVG.js',
      {uiStrings} = svgEditor;
    let
      math,
      locationX,
      locationY,
      mathjaxLoaded = false;
 
    // TODO: Implement language support. Move these uiStrings to the locale files and the code to the langReady callback.
    $.extend(uiStrings, {
      mathjax: {
        embed_svg: 'Save as mathematics',
        embed_mathml: 'Save as figure',
        svg_save_warning: 'The math will be transformed into a figure is manipulatable like everything else. You will not be able to manipulate the TeX-code anymore. ',
        mathml_save_warning: 'Advised. The math will be saved as a figure.',
        title: 'Mathematics code editor'
      }
    });
 
    function saveMath () {
      const code = $('#mathjax_code_textarea').val();
      // displaystyle to force MathJax NOT to use the inline style. Because it is
      // less fancy!
      MathJax.Hub.queue.Push(['Text', math, '\\displaystyle{' + code + '}']);
 
      /*
       * The MathJax library doesn't want to bloat your webpage so it creates
       * every symbol (glymph) you need only once. These are saved in a `<svg>` on
       * the top of your html document, just under the body tag. Each glymph has
       * its unique id and is saved as a `<path>` in the `<defs>` tag of the `<svg>`
       *
       * Then when the symbols are needed in the rest of your html document they
       * are refferd to by a `<use>` tag.
       * Because of bug 1076 we can't just grab the defs tag on the top and add it
       * to your formula's `<svg>` and copy the lot. So we have to replace each
       * `<use>` tag by its `<path>`.
       */
      MathJax.Hub.queue.Push(
        function () {
          const mathjaxMath = $('.MathJax_SVG');
          const svg = $(mathjaxMath.html());
          svg.find('use').each(function () {
            // TODO: find a less pragmatic and more elegant solution to this.
            const id = $(this).attr('href')
              ? $(this).attr('href').slice(1) // Works in Chrome.
              : $(this).attr('xlink:href').slice(1); // Works in Firefox.
            const glymph = $('#' + id).clone().removeAttr('id');
            const x = $(this).attr('x');
            const y = $(this).attr('y');
            const transform = $(this).attr('transform');
            if (transform && (x || y)) {
              glymph.attr('transform', transform + ' translate(' + x + ',' + y + ')');
            } else if (transform) {
              glymph.attr('transform', transform);
            } else if (x || y) {
              glymph.attr('transform', 'translate(' + x + ',' + y + ')');
            }
            $(this).replaceWith(glymph);
          });
          // Remove the style tag because it interferes with SVG-Edit.
          svg.removeAttr('style');
          svg.attr('xmlns', 'http://www.w3.org/2000/svg');
          svgCanvas.importSvgString($('<div>').append(svg.clone()).html(), true);
          svgCanvas.ungroupSelectedElement();
          // TODO: To undo the adding of the Formula you now have to undo twice.
          // This should only be once!
          svgCanvas.moveSelectedElements(locationX, locationY, true);
        }
      );
    }
 
    const buttons = [{
      id: 'tool_mathjax',
      type: 'mode',
      icon: svgEditor.curConfig.extIconsPath + 'mathjax.png',
      events: {
        click () {
          // Only load Mathjax when needed, we don't want to strain Svg-Edit any more.
          // From this point on it is very probable that it will be needed, so load it.
          if (mathjaxLoaded === false) {
            $('<div id="mathjax">' +
              '<!-- Here is where MathJax creates the math -->' +
                '<div id="mathjax_creator" class="tex2jax_process" style="display:none">' +
                  '$${}$$' +
                '</div>' +
                '<div id="mathjax_overlay"></div>' +
                '<div id="mathjax_container">' +
                  '<div id="tool_mathjax_back" class="toolbar_button">' +
                    '<button id="tool_mathjax_save">OK</button>' +
                    '<button id="tool_mathjax_cancel">Cancel</button>' +
                  '</div>' +
                  '<fieldset>' +
                    '<legend id="mathjax_legend">Mathematics Editor</legend>' +
                    '<label>' +
                      '<span id="mathjax_explication">Please type your mathematics in ' +
                      '<a href="https://en.wikipedia.org/wiki/Help:Displaying_a_formula" target="_blank">TeX</a> code.</span></label>' +
                    '<textarea id="mathjax_code_textarea" spellcheck="false"></textarea>' +
                  '</fieldset>' +
                '</div>' +
              '</div>'
            ).insertAfter('#svg_prefs').hide();
 
            // Make the MathEditor draggable.
            $('#mathjax_container').draggable({
              cancel: 'button,fieldset',
              containment: 'window'
            });
 
            // Add functionality and picture to cancel button.
            $('#tool_mathjax_cancel').prepend($.getSvgIcon('cancel', true))
              .on('click touched', function () {
                $('#mathjax').hide();
              });
 
            // Add functionality and picture to the save button.
            $('#tool_mathjax_save').prepend($.getSvgIcon('ok', true))
              .on('click touched', function () {
                saveMath();
                $('#mathjax').hide();
              });
 
            // MathJax preprocessing has to ignore most of the page.
            $('body').addClass('tex2jax_ignore');
 
            // Now get (and run) the MathJax Library.
            // Todo: insert script with modules once widely supported
            //   and if MathJax (and its `TeX-AMS-MML_SVG.js` dependency) ends up
            //   providing an ES6 module export: https://github.com/mathjax/MathJax/issues/1998
            /*
            const modularVersion = !('svgEditor' in window) ||
              !window.svgEditor ||
              window.svgEditor.modules !== false;
            // Add as second argument to `importScript`
            {
              type: modularVersion
                ? 'module' // Make this the default when widely supported
                : 'text/javascript'
            }
            // If only using modules, just use this:
            const {default: MathJax} = await importModule( // or `import()` when widely supported
              svgEditor.curConfig.extIconsPath + mathjaxSrcSecure
            );
            */
            // We use `extIconsPath` here for now as it does not vary with
            //  the modular type as does `extPath`
            importScript(svgEditor.curConfig.extIconsPath + mathjaxSrcSecure).then(() => {
              // When MathJax is loaded get the div where the math will be rendered.
              MathJax.Hub.queue.Push(function () {
                math = MathJax.Hub.getAllJax('#mathjax_creator')[0];
                console.log(math);
                mathjaxLoaded = true;
                console.log('MathJax Loaded');
              });
            }).catch(() => {
              console.log('Failed loadeing MathJax.');
              $.alert('Failed loading MathJax. You will not be able to change the mathematics.');
            });
          }
          // Set the mode.
          svgCanvas.setMode('mathjax');
        }
      }
    }];
 
    return {
      name: strings.name,
      svgicons: svgEditor.curConfig.extIconsPath + 'mathjax-icons.xml',
      buttons: strings.buttons.map((button, i) => {
        return Object.assign(buttons[i], button);
      }),
 
      mouseDown () {
        if (svgCanvas.getMode() === 'mathjax') {
          return {started: true};
        }
      },
      mouseUp (opts) {
        if (svgCanvas.getMode() === 'mathjax') {
          // Get the coordinates from your mouse.
          const zoom = svgCanvas.getZoom();
          // Get the actual coordinate by dividing by the zoom value
          locationX = opts.mouse_x / zoom;
          locationY = opts.mouse_y / zoom;
 
          $('#mathjax').show();
          return {started: false}; // Otherwise the last selected object dissapears.
        }
      },
      callback () {
        $('<style>').text(
          '#mathjax fieldset{' +
            'padding: 5px;' +
            'margin: 5px;' +
            'border: 1px solid #DDD;' +
          '}' +
          '#mathjax label{' +
            'display: block;' +
            'margin: .5em;' +
          '}' +
          '#mathjax legend {' +
            'max-width:195px;' +
          '}' +
          '#mathjax_overlay {' +
            'position: absolute;' +
            'top: 0;' +
            'left: 0;' +
            'right: 0;' +
            'bottom: 0;' +
            'background-color: black;' +
            'opacity: 0.6;' +
            'z-index: 20000;' +
          '}' +
          '#mathjax_container {' +
            'position: absolute;' +
            'top: 50px;' +
            'padding: 10px;' +
            'background-color: #B0B0B0;' +
            'border: 1px outset #777;' +
            'opacity: 1.0;' +
            'font-family: Verdana, Helvetica, sans-serif;' +
            'font-size: .8em;' +
            'z-index: 20001;' +
          '}' +
          '#tool_mathjax_back {' +
            'margin-left: 1em;' +
            'overflow: auto;' +
          '}' +
          '#mathjax_legend{' +
            'font-weight: bold;' +
            'font-size:1.1em;' +
          '}' +
          '#mathjax_code_textarea {\\n' +
            'margin: 5px .7em;' +
            'overflow: hidden;' +
            'width: 416px;' +
            'display: block;' +
            'height: 100px;' +
          '}'
        ).appendTo('head');
 
        // Add the MathJax configuration.
        // $(mathjaxConfiguration).appendTo('head');
      }
    };
  }
};