@wiris/mathtype-viewer 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/properties.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { configurationJson, StatusError } from './services';
2
2
 
3
3
  // Helper types for Config below
4
- type Viewer = 'image' | 'none';
4
+ type Viewer = 'none' | 'image' | 'mathml' | 'latex';
5
5
  type Wirispluginperformance = 'true' | 'false';
6
6
 
7
7
  /**
@@ -41,20 +41,25 @@ const defaultValues: Config = {
41
41
  /**
42
42
  * This class will handle the parameters defined by the user.
43
43
  */
44
- export abstract class Properties {
44
+ export class Properties {
45
45
 
46
- static render: () => Promise<void> = async () => {};
46
+ render: () => Promise<void> = async () => {};
47
47
 
48
- // Flag for the static parameters that access the backend.
49
- private static backendObtained: boolean = false;
48
+ // Get URL properties (retrocompatibility).
49
+ config: Config = defaultValues;
50
50
 
51
- private static waitForBackend() {
52
- while (!this.backendObtained);
53
- }
51
+ /**
52
+ * Do not use this method. Instead, use {@link Properties.generate}.
53
+ * Constructors cannot be async so we make it private and force instantiation through an alternative static method.
54
+ */
55
+ private new() {}
54
56
 
55
- // Get URL properties (retrocompatibility).
56
- static config: Config = defaultValues;
57
- static {
57
+ /**
58
+ * Creates and sets up a new instance of class Properties
59
+ */
60
+ static async generate(): Promise<Properties> {
61
+
62
+ const instance = new Properties();
58
63
 
59
64
  // Get URL parameters from <script>
60
65
  const pluginName = 'WIRISplugins.js';
@@ -67,69 +72,58 @@ export abstract class Properties {
67
72
  const urlParams = new URLSearchParams(params);
68
73
 
69
74
  if (urlParams.get('dpi') !== null && urlParams.get('dpi') !== undefined) {
70
- this.config.dpi = +urlParams.get('dpi');
75
+ instance.config.dpi = +urlParams.get('dpi');
71
76
  }
72
77
  if (urlParams.get('element') !== null && urlParams.get('element') !== undefined) {
73
- this.config.element = urlParams.get('element');
78
+ instance.config.element = urlParams.get('element');
74
79
  }
75
80
  if (urlParams.get('lang') !== null && urlParams.get('lang') !== undefined) {
76
- this.config.lang = urlParams.get('lang');
81
+ instance.config.lang = urlParams.get('lang');
77
82
  }
78
83
  if (urlParams.get('viewer') !== null && urlParams.get('viewer') !== undefined) {
79
- this.config.viewer = (urlParams.get('viewer') as Viewer);
84
+ instance.config.viewer = (urlParams.get('viewer') as Viewer);
80
85
  }
81
86
  if (urlParams.get('zoom') !== null && urlParams.get('zoom') !== undefined) {
82
- this.config.zoom = +urlParams.get('zoom');
87
+ instance.config.zoom = +urlParams.get('zoom');
83
88
  }
84
89
 
85
90
  }
86
91
 
87
92
  // Get backend parameters calling the configurationjson service
88
- (async () => {
89
- try {
90
- this.config.backendConfig = await configurationJson(
91
- ['wirispluginperformance', 'wiriseditormathmlattribute'],
92
- Properties.editorServicesRoot,
93
- Properties.editorServicesExtension,
94
- );
95
- } catch(e) {
96
- if (e instanceof StatusError) {
97
- // Do nothing; leave default values.
98
- console.error(e);
99
- } else {
100
- throw e;
101
- }
102
- } finally {
103
- // Stop looking for the backend (even if the request fails)
104
- this.backendObtained = true;
93
+ try {
94
+ instance.config.backendConfig = await configurationJson(
95
+ ['wirispluginperformance', 'wiriseditormathmlattribute'],
96
+ instance.editorServicesRoot,
97
+ instance.editorServicesExtension,
98
+ );
99
+ } catch(e) {
100
+ if (e instanceof StatusError) {
101
+ // Do nothing; leave default values.
102
+ console.error(e);
103
+ } else {
104
+ throw e;
105
105
  }
106
- })();
107
-
108
- }
106
+ }
109
107
 
110
- /**
111
- * Set the config values manually.
112
- */
113
- static init(config: Config) {
114
- Properties.config = {...defaultValues, ...config};
108
+ return instance;
115
109
  }
116
110
 
117
- static get editorServicesRoot(): string {
111
+ get editorServicesRoot(): string {
118
112
  return this.config.editorServicesRoot ||
119
113
  defaultValues.editorServicesRoot;
120
114
  }
121
115
 
122
- static set editorServicesRoot(editorServicesRoot: string) {
116
+ set editorServicesRoot(editorServicesRoot: string) {
123
117
  this.config.editorServicesRoot = editorServicesRoot;
124
118
  this.render();
125
119
  }
126
120
 
127
- static get editorServicesExtension(): string {
121
+ get editorServicesExtension(): string {
128
122
  return this.config.editorServicesExtension ||
129
123
  defaultValues.editorServicesExtension;
130
124
  }
131
125
 
132
- static set editorServicesExtension(editorServicesExtension: string) {
126
+ set editorServicesExtension(editorServicesExtension: string) {
133
127
  this.config.editorServicesExtension = editorServicesExtension;
134
128
  this.render();
135
129
  }
@@ -143,7 +137,7 @@ export abstract class Properties {
143
137
  * - English, by default.
144
138
  * @returns {string} Encoded language string.
145
139
  */
146
- static get lang(): string {
140
+ get lang(): string {
147
141
  const configLang = (this.config.lang === 'inherit') ? null : this.config.lang;
148
142
  return configLang ||
149
143
  document.getElementsByTagName('html')[0].lang ||
@@ -151,7 +145,7 @@ export abstract class Properties {
151
145
  defaultValues.lang;
152
146
  }
153
147
 
154
- static set lang(lang: string) {
148
+ set lang(lang: string) {
155
149
  this.config.lang = lang;
156
150
  this.render();
157
151
  }
@@ -162,12 +156,12 @@ export abstract class Properties {
162
156
  * - The viewer parameter set in the <script> (WIRISplugin.js?viewer=...)
163
157
  * - none, by default.
164
158
  */
165
- static get viewer(): Viewer {
159
+ get viewer(): Viewer {
166
160
  return this.config.viewer ||
167
161
  defaultValues.viewer;
168
162
  }
169
163
 
170
- static set viewer(viewer: Viewer) {
164
+ set viewer(viewer: Viewer) {
171
165
  this.config.viewer = viewer;
172
166
  this.render();
173
167
  }
@@ -178,12 +172,12 @@ export abstract class Properties {
178
172
  * - The dpi parameter set in the <script> (WIRISplugin.js?dpi=...)
179
173
  * - 96, by default.
180
174
  */
181
- static get dpi(): number {
175
+ get dpi(): number {
182
176
  return this.config.dpi ||
183
177
  defaultValues.dpi;
184
178
  }
185
179
 
186
- static set dpi(dpi: number) {
180
+ set dpi(dpi: number) {
187
181
  this.config.dpi = dpi;
188
182
  this.render();
189
183
  }
@@ -194,12 +188,12 @@ export abstract class Properties {
194
188
  * - The zoom parameter set in the <script> (WIRISplugin.js?zoom=...)
195
189
  * - 1, by default.
196
190
  */
197
- static get zoom(): number {
191
+ get zoom(): number {
198
192
  return this.config.zoom ||
199
193
  defaultValues.zoom;
200
194
  }
201
195
 
202
- static set zoom(zoom: number) {
196
+ set zoom(zoom: number) {
203
197
  this.config.zoom = zoom;
204
198
  this.render();
205
199
  }
@@ -210,12 +204,12 @@ export abstract class Properties {
210
204
  * - The zoom parameter set in the <script> (WIRISplugin.js?element=...)
211
205
  * - 'body', by default.
212
206
  */
213
- static get element(): string {
207
+ get element(): string {
214
208
  return this.config.element ||
215
209
  defaultValues.element;
216
210
  }
217
211
 
218
- static set element(element: string) {
212
+ set element(element: string) {
219
213
  this.config.element = element;
220
214
  this.render();
221
215
  }
@@ -226,13 +220,12 @@ export abstract class Properties {
226
220
  * - The backend configuration of the parameter.
227
221
  * - true, by default.
228
222
  */
229
- static get wirispluginperformance(): Wirispluginperformance {
230
- this.waitForBackend();
223
+ get wirispluginperformance(): Wirispluginperformance {
231
224
  return this.config.backendConfig.wirispluginperformance ||
232
225
  defaultValues.backendConfig.wirispluginperformance;
233
226
  }
234
227
 
235
- static set wirispluginperformance(wirispluginperformance: Wirispluginperformance) {
228
+ set wirispluginperformance(wirispluginperformance: Wirispluginperformance) {
236
229
  this.config.backendConfig.wirispluginperformance = wirispluginperformance;
237
230
  this.render();
238
231
  }
@@ -243,13 +236,12 @@ export abstract class Properties {
243
236
  * - The backend configuration of the parameter.
244
237
  * - data-mathml, by default.
245
238
  */
246
- static get wiriseditormathmlattribute(): string {
247
- this.waitForBackend();
239
+ get wiriseditormathmlattribute(): string {
248
240
  return this.config.backendConfig.wiriseditormathmlattribute ||
249
241
  defaultValues.backendConfig.wiriseditormathmlattribute;
250
242
  }
251
243
 
252
- static set wiriseditormathmlattribute(wiriseditormathmlattribute: string) {
244
+ set wiriseditormathmlattribute(wiriseditormathmlattribute: string) {
253
245
  this.config.backendConfig.wiriseditormathmlattribute = wiriseditormathmlattribute;
254
246
  this.render();
255
247
  }
package/src/retro.ts ADDED
@@ -0,0 +1,255 @@
1
+ import { renderLatex } from "./latex";
2
+ import { renderMathML } from "./mathml";
3
+ import { Properties } from "./properties";
4
+
5
+ /**
6
+ * Exposes the {@link JsPluginViewer} singleton instance as window.com.wiris.js.JsPluginViewer.
7
+ * @param {Properties} properties - Properties of the viewer.
8
+ * @param {Window} w - Instance of the global window.
9
+ * @deprecated Consider using {@link renderLatex} or {@link renderMathML}.
10
+ */
11
+ export function bypassEncapsulation(properties: Properties, w: Window) {
12
+ const wany = w as any;
13
+
14
+ if (typeof wany.com === 'undefined') {
15
+ wany.com = {};
16
+ }
17
+
18
+ if (typeof wany.com.wiris === 'undefined') {
19
+ wany.com.wiris = {};
20
+ }
21
+
22
+ if (typeof wany.com.wiris.js === 'undefined') {
23
+ wany.com.wiris.js = {};
24
+ }
25
+
26
+ if (typeof wany.com.wiris.js.JsPluginViewer === 'undefined') {
27
+ wany.com.wiris.js.JsPluginViewer = JsPluginViewer.getInstance();
28
+ JsPluginViewer.properties = properties;
29
+ }
30
+ }
31
+
32
+ /**
33
+ * This class is a compatibility layer with the old Viewer.
34
+ * See haxe/src-haxe/com/wiris/js/JsPluginViewer.hx in in the repo wiris/plugins previous to commit df1248a.
35
+ *
36
+ * WARNING: the methods in this class may contain direct references to globals such as window and document.
37
+ *
38
+ * @deprecated Consider using {@link renderLatex} or {@link renderMathML}.
39
+ */
40
+ class JsPluginViewer {
41
+ static instance: JsPluginViewer;
42
+ static properties: Properties;
43
+
44
+ static getInstance(): JsPluginViewer {
45
+ if (JsPluginViewer.instance == null) {
46
+ JsPluginViewer.instance = new JsPluginViewer();
47
+ }
48
+
49
+ return JsPluginViewer.instance;
50
+ }
51
+
52
+ /**
53
+ * Render all the formulas written in SafeMathML inside the given element.
54
+ * @param {HTMLElement} element - Element wherein to render SafeMathML formulas.
55
+ * @param asynchronously - Currently ignored, only included for retrocompatibility purposes.
56
+ * @param callbackFunc - Currently ignored, only included for retrocompatibility purposes.
57
+ * @deprecated There is currently no replacement for rendering SafeMathML formulas.
58
+ * Please consider using {@link renderLatex} or {@link renderMathML}.
59
+ */
60
+ parseSafeMathMLElement(element: HTMLElement, asynchronously?: boolean, callbackFunc?: () => void): void {
61
+ var mathmlPositions = [];
62
+ JsPluginViewer.getMathMLPositionsAtElementAndChildren(element, mathmlPositions);
63
+ for (let i = 0; i < mathmlPositions.length; i++) {
64
+ var mathmlPosition = mathmlPositions[i];
65
+ var newNode = document.createElement("math");
66
+ mathmlPosition.nextElement.parentNode.insertBefore(newNode, mathmlPosition.nextElement);
67
+ newNode.outerHTML = JsPluginViewer.decodeSafeMathML(mathmlPosition.safeMML);
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Render all the formulas in the document.
73
+ * @param asynchronously - Currently ignored, only included for retrocompatibility purposes.
74
+ * @param callbackFunc - Currently ignored, only included for retrocompatibility purposes.
75
+ * @param safeXml - Currently ignored, only included for retrocompatibility purposes.
76
+ * @deprecated Please consider using {@link renderMathML}.
77
+ */
78
+ async parseDocument(asynchronously?: boolean, callbackFunc?: () => void, safeXml?: boolean): Promise<void> {
79
+ return renderMathML(JsPluginViewer.properties, document.documentElement);
80
+ }
81
+
82
+ /**
83
+ * Render all the formulas inside the given element.
84
+ * @param {HTMLElement} element - Element wherein to render formulas.
85
+ * @param asynchronously - Currently ignored, only included for retrocompatibility purposes.
86
+ * @param callbackFunc - Currently ignored, only included for retrocompatibility purposes.
87
+ * @deprecated Please consider using {@link renderMathML}.
88
+ */
89
+ async parseElement(element: HTMLElement, asynchronously?: boolean, callbackFunc?: () => void): Promise<void> {
90
+ return renderMathML(JsPluginViewer.properties, element);
91
+ }
92
+
93
+ /**
94
+ * Convert all the LaTeX formulas in the document to MathML.
95
+ * @param asynchronously - Currently ignored, only included for retrocompatibility purposes.
96
+ * @param callbackFunc - Currently ignored, only included for retrocompatibility purposes.
97
+ * @deprecated Please consider using {@link renderLatex}.
98
+ */
99
+ async parseLatexDocument(asynchronously?: boolean, callbackFunc?: () => void): Promise<void> {
100
+ return renderLatex(JsPluginViewer.properties, document.documentElement);
101
+ }
102
+
103
+ /**
104
+ * Convert all the LaTeX formulas inside the given element to MathML.
105
+ * @param {HTMLElement} element - Element wherein to convert formulas.
106
+ * @param asynchronously - Currently ignored, only included for retrocompatibility purposes.
107
+ * @param callbackFunc - Currently ignored, only included for retrocompatibility purposes.
108
+ * @deprecated Please consider using {@link renderLatex}.
109
+ */
110
+ async parseLatexElement(element: HTMLElement, asynchronously?: boolean, callbackFunc?: () => void): Promise<void> {
111
+ return renderLatex(JsPluginViewer.properties, element);
112
+ }
113
+
114
+ private static decodeSafeMathML(input: string): string {
115
+ var safeXMLCharactersEntities = JsCharacters.getSafeXMLCharactersEntities();
116
+ var xmlCharacters = JsCharacters.getXMLCharacters();
117
+ var safeXMLCharacters = JsCharacters.getSafeXMLCharacters();
118
+
119
+ var tagOpenerEntity = safeXMLCharactersEntities.tagOpener;
120
+ var tagCloserEntity = safeXMLCharactersEntities.tagCloser;
121
+ var doubleQuoteEntity = safeXMLCharactersEntities.doubleQuote;
122
+ var realDoubleQuoteEntity = safeXMLCharactersEntities.realDoubleQuote;
123
+
124
+
125
+ // Important to not change function parameter.
126
+ var inputCopy = input.slice();
127
+
128
+ // Decoding entities.
129
+ inputCopy = inputCopy.split(tagOpenerEntity).join(safeXMLCharacters.tagOpener);
130
+ inputCopy = inputCopy.split(tagCloserEntity).join(safeXMLCharacters.tagCloser);
131
+ inputCopy = inputCopy.split(doubleQuoteEntity).join(safeXMLCharacters.doubleQuote);
132
+ inputCopy = inputCopy.split(realDoubleQuoteEntity).join(safeXMLCharacters.realDoubleQuote);
133
+
134
+ var tagOpener = safeXMLCharacters.tagOpener;
135
+ var tagCloser = safeXMLCharacters.tagCloser;
136
+ var doubleQuote = safeXMLCharacters.doubleQuote;
137
+ var realDoubleQuote = safeXMLCharacters.realDoubleQuote;
138
+ var ampersand = safeXMLCharacters.ampersand;
139
+ var quote = safeXMLCharacters.quote;
140
+
141
+ // Decoding characters.
142
+ inputCopy = inputCopy.split(tagOpener).join(xmlCharacters.tagOpener);
143
+ inputCopy = inputCopy.split(tagCloser).join(xmlCharacters.tagCloser);
144
+ inputCopy = inputCopy.split(doubleQuote).join(xmlCharacters.doubleQuote);
145
+ inputCopy = inputCopy.split(ampersand).join(xmlCharacters.ampersand);
146
+ inputCopy = inputCopy.split(quote).join(xmlCharacters.quote);
147
+
148
+ // We are replacing $ by & when its part of an entity for retrocompatibility.
149
+ // Now, the standard is replace § by &.
150
+ var returnValue = '';
151
+ var currentEntity = null;
152
+
153
+ var i = 0;
154
+ while (i < inputCopy.length) {
155
+ var character = inputCopy.charAt(i);
156
+ if (currentEntity == null) {
157
+ if (character == '$') {
158
+ currentEntity = '';
159
+ } else {
160
+ returnValue += character;
161
+ }
162
+ } else if (character == ';') {
163
+ returnValue += '&' + currentEntity;
164
+ currentEntity = null;
165
+ } else if (character.match(/([a-zA-Z0-9#._-] | '-')/)) { // Character is part of an entity.
166
+ currentEntity += character;
167
+ } else {
168
+ returnValue += '$' + 'currentEntity'; // Is not an entity.
169
+ currentEntity = null;
170
+ i -= 1; // Parse again the current character.
171
+ }
172
+ i++;
173
+ }
174
+
175
+ return returnValue;
176
+ }
177
+
178
+ private static getMathMLPositionsAtElementAndChildren(element: Node, mathmlPositions) {
179
+ JsPluginViewer.getMathMLPositionsAtNode(element, mathmlPositions);
180
+ // Copy current children because DOM will be changed and element.childNodes won't be
181
+ // consistent on call getMathMLPositionsAtElementAndChildren().
182
+ var childNodes = Array.from(element.childNodes);
183
+ if (childNodes.length > 0) {
184
+ for (let i = 0; i < childNodes.length; i++) {
185
+ var child = childNodes[i];
186
+ JsPluginViewer.getMathMLPositionsAtElementAndChildren(child, mathmlPositions);
187
+ }
188
+ }
189
+ }
190
+
191
+ private static getMathMLPositionsAtNode(node: Node, mathmlPositions) {
192
+ var safeXMLCharacters = JsCharacters.getSafeXMLCharacters();
193
+ if(node.nodeType == 3) {
194
+ var startMathmlTag = safeXMLCharacters.tagOpener + "math";
195
+ var endMathmlTag = safeXMLCharacters.tagOpener + "/math" + safeXMLCharacters.tagCloser;
196
+ var start = node.textContent.indexOf(startMathmlTag);
197
+ var end = 0;
198
+ while(start != -1) {
199
+ end = node.textContent.indexOf(endMathmlTag,start + startMathmlTag.length);
200
+
201
+ if(end == -1) break;
202
+
203
+ var nextMathML = node.textContent.indexOf(startMathmlTag,end + endMathmlTag.length);
204
+
205
+ if(nextMathML >= 0 && end > nextMathML) break;
206
+
207
+ var safeMathml = node.textContent.substring(start,end + endMathmlTag.length);
208
+
209
+ node.textContent = node.textContent.substring(0,start) + node.textContent.substring(end + endMathmlTag.length);
210
+ node = (node as Text).splitText(start);
211
+ start = node.textContent.indexOf(startMathmlTag);
212
+
213
+ mathmlPositions.push({
214
+ safeMML: safeMathml,
215
+ nextElement: node
216
+ });
217
+ }
218
+ }
219
+ }
220
+
221
+ }
222
+
223
+ class JsCharacters {
224
+ static getSafeXMLCharactersEntities(): any {
225
+ return {
226
+ tagOpener: '&laquo;',
227
+ tagCloser: '&raquo;',
228
+ doubleQuote: '&uml;',
229
+ realDoubleQuote: '&quot;',
230
+ };
231
+ }
232
+
233
+ static getXMLCharacters(): any {
234
+ return {
235
+ id: 'xmlCharacters',
236
+ tagOpener: '<', // Hex: \x3C.
237
+ tagCloser: '>', // Hex: \x3E.
238
+ doubleQuote: '"', // Hex: \x22.
239
+ ampersand: '&', // Hex: \x26.
240
+ quote: '\'', // Hex: \x27.
241
+ };
242
+ }
243
+
244
+ static getSafeXMLCharacters(): any {
245
+ return {
246
+ id: 'safeXmlCharacters',
247
+ tagOpener: '«', // Hex: \xAB.
248
+ tagCloser: '»', // Hex: \xBB.
249
+ doubleQuote: '¨', // Hex: \xA8.
250
+ ampersand: '§', // Hex: \xA7.
251
+ quote: '`', // Hex: \x60.
252
+ realDoubleQuote: '¨',
253
+ };
254
+ }
255
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Takes a string possibly containing encoded entities (e.g. &nbsp; &#160;) and
3
+ * returns the same string with these replaced by the UTF-8 characters they represent
4
+ * @param {string} text - the string with encoded entities
5
+ * @returns the same string with the entities decoded
6
+ */
7
+ function decodeEntities(text: string): string {
8
+ const element = document.createElement('textarea');
9
+ element.innerHTML = text;
10
+ return element.value;
11
+ }
12
+
13
+ /**
14
+ * Takes a string possibly containing encoded named HTML entities (e.g. &nbsp;) and
15
+ * returns the same string with these replaced by the encoding that uses numbers (e.g. &#160;).
16
+ * By its implementation, this method also possibly encodes special characters that were
17
+ * previously unencoded.
18
+ * @param {string} text - the string with encoded entities
19
+ * @returns the same string with the entities decoded
20
+ */
21
+ export function htmlEntitiesToXmlEntities(text: string): string {
22
+ text = decodeEntities(text);
23
+
24
+ // Replaces the < & > characters to its HTMLEntity to avoid render issues.
25
+ text = text.split('"<"').join('"&lt;"')
26
+ .split('">"')
27
+ .join('"&gt;"')
28
+ .split('><<')
29
+ .join('>&lt;<');
30
+
31
+ let result = '';
32
+ for (let i = 0; i < text.length; i++) {
33
+ const character = text.charAt(i);
34
+ if (text.charCodeAt(i) > 128) {
35
+ result += '&#' + text.charCodeAt(i) + ';';
36
+ } else {
37
+ result += character;
38
+ }
39
+ }
40
+ return result;
41
+ }
42
+
43
+ // Set of mathml and characters which don't have an accessible text associated
44
+ // and can not be translated or transformed to LaTeX.
45
+ export const corruptMathML = ['⟦', '&#10214;', '⟧', '&#10215;', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow'];
@@ -0,0 +1,2 @@
1
+ declare module "Parser";
2
+ declare module '@wiris/mathtype-html-integration-devkit/src/parser';