@windward/core 0.4.0 → 0.4.1
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/components/Content/Blocks/BlockQuote.vue +2 -2
- package/components/Content/Blocks/ClickableIcons.vue +1 -1
- package/components/Content/Blocks/Email.vue +10 -6
- package/components/Content/Blocks/Feedback.vue +12 -5
- package/components/Content/Blocks/OpenResponse.vue +7 -3
- package/components/Content/Blocks/OpenResponseCollate.vue +1 -1
- package/components/Content/Blocks/ScenarioChoice.vue +2 -2
- package/components/Content/Blocks/UserUpload/DisplayUserFilesTable.vue +2 -0
- package/components/Content/Blocks/UserUpload.vue +1 -0
- package/components/Content/Blocks/Video.vue +2 -2
- package/components/Navigation/Items/AskTheExpert.vue +1 -0
- package/components/Settings/AccordionSettings.vue +2 -0
- package/components/Settings/ClickableIconsSettings.vue +5 -1
- package/components/Settings/EmailSettings.vue +6 -1
- package/components/Settings/FeedbackSettings.vue +1 -0
- package/components/Settings/MathSettings.vue +1 -1
- package/components/Settings/ScenarioChoiceSettings.vue +1 -0
- package/components/Settings/TabSettings.vue +7 -1
- package/components/Settings/TextEditorSettings.vue +3 -2
- package/components/Settings/UserUploadSettings.vue +1 -1
- package/components/Settings/VideoSettings.vue +2 -0
- package/components/utils/FillInBlank/FillInBlankInput.vue +3 -0
- package/components/utils/MathExpressionEditor.vue +30 -9
- package/components/utils/MathLiveWrapper.vue +47 -25
- package/components/utils/TinyMCEWrapper.vue +102 -18
- package/components/utils/glossary/CourseGlossary.vue +2 -0
- package/config/tinymce.config.ts +17 -13
- package/helpers/MathHelper.ts +59 -0
- package/helpers/tinymce/WindwardPlugins.ts +335 -0
- package/i18n/en-US/components/utils/tiny_mce_wrapper.ts +1 -0
- package/i18n/es-ES/components/utils/tiny_mce_wrapper.ts +1 -0
- package/i18n/sv-SE/components/utils/tiny_mce_wrapper.ts +1 -0
- package/package.json +3 -2
- package/test/helpers/MathHelper.spec.js +14 -1
- package/tsconfig.json +4 -1
- package/helpers/tinymce/plugin.ts +0 -210
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Class representing the WindwardPlugins.
|
|
5
|
+
*/
|
|
6
|
+
export class WindwardPlugins {
|
|
7
|
+
editor: any
|
|
8
|
+
formula: any
|
|
9
|
+
fillInBlank: any
|
|
10
|
+
private window: any;
|
|
11
|
+
|
|
12
|
+
constructor(editor: any) {
|
|
13
|
+
|
|
14
|
+
this.editor = editor
|
|
15
|
+
this.formula = undefined
|
|
16
|
+
this.fillInBlank = undefined
|
|
17
|
+
this.window = window
|
|
18
|
+
this.register()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* registers an icon with the given icon name and SVG path.
|
|
24
|
+
*
|
|
25
|
+
* @param {string} iconName - The name of the icon.
|
|
26
|
+
* @param {string} svgPath - The SVG path for the icon.
|
|
27
|
+
*
|
|
28
|
+
* @return {void}
|
|
29
|
+
*/
|
|
30
|
+
private registerIcon(iconName: string, svgPath: string) {
|
|
31
|
+
this.editor.ui.registry.addIcon(
|
|
32
|
+
iconName,
|
|
33
|
+
svgPath
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Adds icons to the editor's UI registry.
|
|
38
|
+
*/
|
|
39
|
+
private addIcons() {
|
|
40
|
+
this.registerIcon('insertMath', '<svg x="0px" y="0px"width="20px" height="20px" viewBox="0 0 445.878 445.878" style="enable-background:new 0 0 445.878 445.878;"><path d="M426.024,86.447H209.705l-84.911,298.911c-2.568,7.967-9.854,13.482-18.22,13.771c-0.236,0-0.464,0.006-0.688,0.006 c-8.092,0-15.41-4.924-18.436-12.478l-34.714-86.782H19.851C8.884,299.876,0,290.986,0,280.022 c0-10.965,8.893-19.854,19.851-19.854H66.18c8.109,0,15.421,4.941,18.436,12.483l19.237,48.09l72.472-260.218 c2.639-8.213,10.279-13.781,18.903-13.781h230.798c10.97,0,19.854,8.89,19.854,19.851S436.988,86.447,426.024,86.447z M436.723,353.227l-78.259-87.904l74.576-82.783c1.318-1.454,1.638-3.547,0.857-5.341c-0.804-1.791-2.577-2.946-4.54-2.946h-47.18 c-1.442,0-2.802,0.629-3.759,1.72l-50.059,58.047l-49.674-58.029c-0.939-1.103-2.317-1.738-3.771-1.738h-49.334 c-1.956,0-3.729,1.149-4.521,2.929c-0.81,1.785 0.479,3.875,0.824,5.332l73.743,82.81l-77.641,87.923 c-1.297,1.465-1.605,3.552 0.813,5.325c0.813,1.785,2.586,2.92,4.528,2.92h48.9c1.472,0,2.867-0.65,3.807-1.785l51.819-62.181 l53.05,62.229c0.951,1.11,2.328,1.743,3.782,1.743h49.97c1.962,0,3.735-1.141,4.527-2.926 C438.354,356.779,438.035,354.692,436.723,353.227z"/></svg>');
|
|
41
|
+
this.registerIcon('insertFIB', '<svg width="20px" height="20px" viewBox="0 0 24 24"><path d="M17,7H22V17H17V19A1,1 0 0,0 18,20H20V22H17.5C16.95,22 16,21.55 16,21C16,21.55 15.05,22 14.5,22H12V20H14A1,1 0 0,0 15,19V5A1,1 0 0,0 14,4H12V2H14.5C15.05,2 16,2.45 16,3C16,2.45 16.95,2 17.5,2H20V4H18A1,1 0 0,0 17,5V7M2,7H13V9H4V15H13V17H2V7M20,15V9H17V15H20Z" /></svg>');
|
|
42
|
+
this.registerIcon('glossaryIcon', '<svg viewBox="0 0 24 24" width="20px" height="20px" ><path d="M3,15H1V3A2,2 0 0,1 3,1H19V3H3V15M12,23A1,1 0 0,1 11,22V19H7A2,2 0 0,1 5,17V7A2,2 0 0,1 7,5H21A2,2 0 0,1 23,7V17A2,2 0 0,1 21,19H16.9L13.2,22.71C13,22.89 12.76,23 12.5,23H12M9,9V11H19V9H9M9,13V15H17V13H9Z"></path></svg>');
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Opens the equation editor window in the TinyMCE editor.
|
|
46
|
+
* @return {void}
|
|
47
|
+
*/
|
|
48
|
+
private equationWindow() {
|
|
49
|
+
this.editor.addCommand('equation-window', (data: any) => {
|
|
50
|
+
return this.editor.windowManager.openUrl({
|
|
51
|
+
url: '/plugins/tinymce/math',
|
|
52
|
+
width: .75*this.window.innerWidth,
|
|
53
|
+
height: .9*this.window.innerHeight,
|
|
54
|
+
title: 'Equation editor',
|
|
55
|
+
buttons: [
|
|
56
|
+
{
|
|
57
|
+
type: 'cancel',
|
|
58
|
+
text: 'cancel',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
type: 'custom',
|
|
62
|
+
text: 'submit',
|
|
63
|
+
primary: true,
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
onAction: () => {
|
|
67
|
+
if (data.currentTarget) {
|
|
68
|
+
this.editor.selection.select(data.currentTarget)
|
|
69
|
+
}
|
|
70
|
+
this.editor.selection.setContent(
|
|
71
|
+
` <span class='windward-math-content'>` +
|
|
72
|
+
this.formula +
|
|
73
|
+
`</span> `
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
this.editor.windowManager.close()
|
|
77
|
+
this.setOnDoubleClickEquationContent(this.editor)
|
|
78
|
+
},
|
|
79
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
80
|
+
onMessage: (instance: any, message: any) => {
|
|
81
|
+
switch (message.mceAction) {
|
|
82
|
+
case 'equation-insert':
|
|
83
|
+
this.formula = message.content
|
|
84
|
+
break
|
|
85
|
+
case 'math-plugin-mounted':
|
|
86
|
+
this.window.postMessage({ latex: data.latex ?? '' }, '*')
|
|
87
|
+
break
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Adds custom buttons to the editor's user interface registry.
|
|
96
|
+
*/
|
|
97
|
+
private addButtons() {
|
|
98
|
+
|
|
99
|
+
this.addButtonToEditor(
|
|
100
|
+
"mathButton",
|
|
101
|
+
"insertMath",
|
|
102
|
+
() => {
|
|
103
|
+
this.editor.execCommand("equation-window", true);
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
this.addButtonToEditor(
|
|
107
|
+
"glossaryButton",
|
|
108
|
+
"glossaryIcon",
|
|
109
|
+
() => {
|
|
110
|
+
this.editor.formatter.apply("glossary");
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
this.addButtonToEditor(
|
|
114
|
+
"fibInsertButton",
|
|
115
|
+
"insertFIB",
|
|
116
|
+
() => {
|
|
117
|
+
this.editor.execCommand("fib-window", true);
|
|
118
|
+
}
|
|
119
|
+
);
|
|
120
|
+
this.addButtonToEditor(
|
|
121
|
+
"fibFormatButton",
|
|
122
|
+
"insertFIB",
|
|
123
|
+
() => {
|
|
124
|
+
this.editor.formatter.apply("fib");
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Add a button to the editor's UI registry
|
|
131
|
+
* @param {string} name Name of the button
|
|
132
|
+
* @param {string} icon Icon name of the button
|
|
133
|
+
* @param {() => void} onAction Callback function for the action to be done when the button is clicked
|
|
134
|
+
*/
|
|
135
|
+
private addButtonToEditor(name: string, icon: string, onAction: () => void) {
|
|
136
|
+
this.editor.ui.registry.addButton(name, {
|
|
137
|
+
icon: icon,
|
|
138
|
+
onAction: onAction
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Adds a menu item to the editor's UI registry.
|
|
143
|
+
*
|
|
144
|
+
* @param itemKey - The key that uniquely identifies the menu item.
|
|
145
|
+
* @param itemText - The text for the menu item.
|
|
146
|
+
* @param command - The command to be executed on item click.
|
|
147
|
+
* @param icon - The command to be executed on item click.
|
|
148
|
+
* @returns {void}
|
|
149
|
+
*/
|
|
150
|
+
private addEditorMenuItem(itemKey: string, itemText: string, command: string, icon: string): void {
|
|
151
|
+
this.editor.ui.registry.addMenuItem(itemKey, {
|
|
152
|
+
text: itemText,
|
|
153
|
+
icon: icon,
|
|
154
|
+
onAction: () => {
|
|
155
|
+
this.editor.execCommand(command, true)
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Adds menu items to the editor UI registry.
|
|
162
|
+
* @returns {void}
|
|
163
|
+
*/
|
|
164
|
+
private addMenuItems() {
|
|
165
|
+
this.addEditorMenuItem('math', 'Math', 'equation-window','insertMath');
|
|
166
|
+
this.addEditorMenuItem('FIB', 'Fill in the blank', 'fib-window','insertFIB');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Initializes the Editor and sets event listeners for 'init' event.
|
|
171
|
+
* Calls setOnClickEquationContent() and setOnClickFillInBlank() methods.
|
|
172
|
+
*
|
|
173
|
+
* @returns {void}
|
|
174
|
+
*/
|
|
175
|
+
private init() {
|
|
176
|
+
|
|
177
|
+
this.editor.on('init',()=>{
|
|
178
|
+
this.setOnDoubleClickEquationContent(this.editor)
|
|
179
|
+
this.setOnClickFillInBlank(this.editor)
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Sets the content on the editor and triggers the 'SetContent' event.
|
|
185
|
+
* calls the 'setOnClickEquationContent' and 'setOnClickFillInBlank' methods with the editor as a parameter.
|
|
186
|
+
*
|
|
187
|
+
* @returns {void} This method does not return anything.
|
|
188
|
+
*/
|
|
189
|
+
private setContent() {
|
|
190
|
+
this.editor.on('SetContent', () => {
|
|
191
|
+
this.setOnDoubleClickEquationContent(this.editor)
|
|
192
|
+
this.setOnClickFillInBlank(this.editor)
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Adds event listeners for handling input events.
|
|
198
|
+
* @return {void}
|
|
199
|
+
*/
|
|
200
|
+
setInputEvents() {
|
|
201
|
+
this.editor.on('input', () =>{
|
|
202
|
+
this.setOnDoubleClickEquationContent(this.editor)
|
|
203
|
+
this.setOnClickFillInBlank(this.editor)
|
|
204
|
+
})
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Adds an onclick listener to all equation content within the editor.
|
|
208
|
+
*
|
|
209
|
+
* @param {any} editor - The editor instance.
|
|
210
|
+
*/
|
|
211
|
+
setOnDoubleClickEquationContent(editor: any) {
|
|
212
|
+
const tinymceDoc = editor.getDoc()
|
|
213
|
+
const mqSpan = tinymceDoc.getElementsByClassName(
|
|
214
|
+
'windward-math-content'
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
// Add onclick listener to all equation content
|
|
218
|
+
for (const equationContent of mqSpan) {
|
|
219
|
+
equationContent.contentEditable = 'true'
|
|
220
|
+
if (equationContent.onclick) {
|
|
221
|
+
continue
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
equationContent.ondblclick = (event: any) => {
|
|
225
|
+
event.stopPropagation()
|
|
226
|
+
editor.execCommand('equation-window', {
|
|
227
|
+
latex: event.target.innerText,
|
|
228
|
+
currentTarget: event.target,
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Opens a fill in the blank window in the TinyMCE editor.
|
|
236
|
+
* @return {void}
|
|
237
|
+
*/
|
|
238
|
+
fillInBlankWindow() {
|
|
239
|
+
this.editor.addCommand('fib-window', (data: any) => {
|
|
240
|
+
return this.editor.windowManager.openUrl({
|
|
241
|
+
url: '/plugins/tinymce/FIB',
|
|
242
|
+
title: 'Fill in the blank',
|
|
243
|
+
buttons: [
|
|
244
|
+
{
|
|
245
|
+
type: 'cancel',
|
|
246
|
+
text: 'cancel',
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
type: 'custom',
|
|
250
|
+
text: 'submit',
|
|
251
|
+
primary: true,
|
|
252
|
+
},
|
|
253
|
+
],
|
|
254
|
+
onAction: () => {
|
|
255
|
+
if (data.currentTarget) {
|
|
256
|
+
this.editor.selection.select(data.currentTarget)
|
|
257
|
+
}
|
|
258
|
+
this.editor.selection.setContent(
|
|
259
|
+
`<span class='windward-fill-blank' data-feedback =` +
|
|
260
|
+
this.fillInBlank.feedback +
|
|
261
|
+
` >` +
|
|
262
|
+
this.fillInBlank.answer +
|
|
263
|
+
`</span>`
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
this.editor.windowManager.close()
|
|
267
|
+
this.setOnClickFillInBlank(this.editor)
|
|
268
|
+
},
|
|
269
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
270
|
+
onMessage: (instance: any, message: any) => {
|
|
271
|
+
switch (message.mceAction) {
|
|
272
|
+
case 'fib-insert':
|
|
273
|
+
this.fillInBlank = {
|
|
274
|
+
answer: message.content.answer,
|
|
275
|
+
feedback: message.content.feedback,
|
|
276
|
+
}
|
|
277
|
+
break
|
|
278
|
+
case 'fib-plugin-mounted':
|
|
279
|
+
this.window.parent.postMessage(
|
|
280
|
+
{ answer: data.answer, feedback: data.feedback },
|
|
281
|
+
'*'
|
|
282
|
+
)
|
|
283
|
+
break
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
})
|
|
287
|
+
})
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Adds an onclick listener to fill-in-blank elements within the provided editor.
|
|
292
|
+
* When a fill-in-blank element is double-clicked, it executes the 'fib-window' command with the appropriate parameters.
|
|
293
|
+
*
|
|
294
|
+
* @param {any} editor - TThe editor instance.
|
|
295
|
+
* @returns {void}
|
|
296
|
+
*/
|
|
297
|
+
setOnClickFillInBlank(editor: any) {
|
|
298
|
+
const tinymceDoc = editor.getDoc()
|
|
299
|
+
const mqSpan = tinymceDoc.getElementsByClassName('windward-fill-blank')
|
|
300
|
+
|
|
301
|
+
// Add onclick listener to all equation content
|
|
302
|
+
for (const fib of mqSpan) {
|
|
303
|
+
fib.contentEditable = 'true'
|
|
304
|
+
if (fib.onclick) {
|
|
305
|
+
continue
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
fib.ondblclick = (event: any) => {
|
|
309
|
+
event.stopPropagation()
|
|
310
|
+
editor.execCommand('fib-window', {
|
|
311
|
+
answer: event.target.innerText,
|
|
312
|
+
feedback: event.target.getAttribute('data-feedback'),
|
|
313
|
+
currentTarget: event.target,
|
|
314
|
+
})
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Registers the application, initializing necessary components and setting up the user interface.
|
|
321
|
+
*
|
|
322
|
+
* @return {void}
|
|
323
|
+
*/
|
|
324
|
+
private register() {
|
|
325
|
+
this.addIcons()
|
|
326
|
+
this.addButtons()
|
|
327
|
+
this.addMenuItems()
|
|
328
|
+
this.setContent()
|
|
329
|
+
this.setInputEvents()
|
|
330
|
+
this.equationWindow()
|
|
331
|
+
this.fillInBlankWindow()
|
|
332
|
+
this.init()
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@windward/core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Windward UI Core Plugins",
|
|
5
5
|
"main": "plugin.js",
|
|
6
6
|
"scripts": {
|
|
@@ -26,10 +26,11 @@
|
|
|
26
26
|
"eslint": "^8.11.0",
|
|
27
27
|
"he": "^1.2.0",
|
|
28
28
|
"lodash": "^4.17.21",
|
|
29
|
-
"mathlive": "^0.
|
|
29
|
+
"mathlive": "^0.98.6",
|
|
30
30
|
"mathml-to-latex": "^1.4.0",
|
|
31
31
|
"prettier": "^2.6.0",
|
|
32
32
|
"raw-loader": "^4.0.2",
|
|
33
|
+
"speechreader": "^1.1.5",
|
|
33
34
|
"tinymce": "^7.1.0"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
@@ -8,6 +8,7 @@ const text =
|
|
|
8
8
|
const mathML =
|
|
9
9
|
'<math xmlns="http://www.w3.org/1998/Math/MathML"><mfrac><mn>1</mn><mn>2</mn></mfrac><mo>+</mo><mfrac><mn>3</mn><mn>4</mn></mfrac></math>'
|
|
10
10
|
const latexContent = '\\frac{1}{2} + \\frac{3}{4}'
|
|
11
|
+
const latexContentSpeakableText = ' half plus three quarter '
|
|
11
12
|
const srEnhancedlatex =
|
|
12
13
|
'**' +
|
|
13
14
|
JSON.stringify({
|
|
@@ -15,7 +16,7 @@ const srEnhancedlatex =
|
|
|
15
16
|
sr_text: ' half plus three quarter ',
|
|
16
17
|
}) +
|
|
17
18
|
'**'
|
|
18
|
-
const mathliveHtml = `<span tabindex="0" aria-label=" half plus three quarter "> <span tabindex="-1" ><span class="
|
|
19
|
+
const mathliveHtml = `<span tabindex="0" aria-label=" half plus three quarter "> <span tabindex="-1" ><span class="ML__latex"><span class="ML__strut" style="height:1.15em"></span><span class="ML__strut--bottom" style="height:1.84em;vertical-align:-0.68em"></span><span class="ML__base"><span class="ML__mfrac"><span class="ML__nulldelimiter ML__open" style="width:0.12em"></span><span class="ML__vlist-t ML__vlist-t2"><span class="ML__vlist-r"><span class="ML__vlist" style="height:1.15em"><span class="ML__center" style="top:-2.31em"><span class="ML__pstrut" style="height:3em"></span><span style="height:0.65em;display:inline-block"><span class="ML__cmr">2</span></span></span><span style="top:-3.23em"><span class="ML__pstrut" style="height:3em"></span><span class="ML__frac-line" style="height:0.04em;display:inline-block"></span></span><span class="ML__center" style="top:-3.5em"><span class="ML__pstrut" style="height:3em"></span><span style="height:0.65em;display:inline-block"><span class="ML__cmr">1</span></span></span></span><span class="ML__vlist-s"></span></span><span class="ML__vlist-r"><span class="ML__vlist" style="height:0.69em"></span></span></span><span class="ML__nulldelimiter ML__close" style="width:0.12em"></span></span><span style="display:inline-block;width:0.23em"></span><span class="ML__cmr">+</span><span style="display:inline-block;width:0.23em"></span><span class="ML__mfrac"><span class="ML__nulldelimiter ML__open" style="width:0.12em"></span><span class="ML__vlist-t ML__vlist-t2"><span class="ML__vlist-r"><span class="ML__vlist" style="height:1.15em"><span class="ML__center" style="top:-2.31em"><span class="ML__pstrut" style="height:3em"></span><span style="height:0.65em;display:inline-block"><span class="ML__cmr">4</span></span></span><span style="top:-3.23em"><span class="ML__pstrut" style="height:3em"></span><span class="ML__frac-line" style="height:0.04em;display:inline-block"></span></span><span class="ML__center" style="top:-3.5em"><span class="ML__pstrut" style="height:3em"></span><span style="height:0.65em;display:inline-block"><span class="ML__cmr">3</span></span></span></span><span class="ML__vlist-s"></span></span><span class="ML__vlist-r"><span class="ML__vlist" style="height:0.69em"></span></span></span><span class="ML__nulldelimiter ML__close" style="width:0.12em"></span></span></span></span></span></span>`
|
|
19
20
|
describe('MahtHelper ', () => {
|
|
20
21
|
test('detects text has mathml', () => {
|
|
21
22
|
expect(MathHelper.containsMathML(text + mathML)).toBeTruthy()
|
|
@@ -144,4 +145,16 @@ describe('MahtHelper ', () => {
|
|
|
144
145
|
`</span> `
|
|
145
146
|
)
|
|
146
147
|
})
|
|
148
|
+
test('can convert latex to speakable text', () => {
|
|
149
|
+
expect(
|
|
150
|
+
MathHelper.convertMathContentToSpeakableText(
|
|
151
|
+
'$$' + latexContent + '$$'
|
|
152
|
+
)
|
|
153
|
+
).toEqual(` half plus three quarter `)
|
|
154
|
+
})
|
|
155
|
+
test('can convert SR enhanced latex to speakable text', () => {
|
|
156
|
+
expect(
|
|
157
|
+
MathHelper.convertMathContentToSpeakableText(srEnhancedlatex)
|
|
158
|
+
).toEqual(` half plus three quarter `)
|
|
159
|
+
})
|
|
147
160
|
})
|
package/tsconfig.json
CHANGED
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
import MathHelper from "../MathHelper";
|
|
2
|
-
const WindwardPlugins = function (editor: any) {
|
|
3
|
-
let formula: any
|
|
4
|
-
let fillInBlank: any
|
|
5
|
-
// ----- Events ----- //
|
|
6
|
-
editor.ui.registry.addIcon(
|
|
7
|
-
'insertMath',
|
|
8
|
-
'<svg x="0px" y="0px"width="20px" height="20px" viewBox="0 0 445.878 445.878" style="enable-background:new 0 0 445.878 445.878;">' +
|
|
9
|
-
'<path d="M426.024,86.447H209.705l-84.911,298.911c-2.568,7.967-9.854,13.482-18.22,13.771c-0.236,0-0.464,0.006-0.688,0.006 c-8.092,0-15.41-4.924-18.436-12.478l-34.714-86.782H19.851C8.884,299.876,0,290.986,0,280.022 c0-10.965,8.893-19.854,19.851-19.854H66.18c8.109,0,15.421,4.941,18.436,12.483l19.237,48.09l72.472-260.218 c2.639-8.213,10.279-13.781,18.903-13.781h230.798c10.97,0,19.854,8.89,19.854,19.851S436.988,86.447,426.024,86.447z M436.723,353.227l-78.259-87.904l74.576-82.783c1.318-1.454,1.638-3.547,0.857-5.341c-0.804-1.791-2.577-2.946-4.54-2.946h-47.18 c-1.442,0-2.802,0.629-3.759,1.72l-50.059,58.047l-49.674-58.029c-0.939-1.103-2.317-1.738-3.771-1.738h-49.334 c-1.956,0-3.729,1.149-4.521,2.929c-0.81,1.785 0.479,3.875,0.824,5.332l73.743,82.81l-77.641,87.923 c-1.297,1.465-1.605,3.552 0.813,5.325c0.813,1.785,2.586,2.92,4.528,2.92h48.9c1.472,0,2.867-0.65,3.807-1.785l51.819-62.181 l53.05,62.229c0.951,1.11,2.328,1.743,3.782,1.743h49.97c1.962,0,3.735-1.141,4.527-2.926 C438.354,356.779,438.035,354.692,436.723,353.227z"/>' +
|
|
10
|
-
'</svg>'
|
|
11
|
-
)
|
|
12
|
-
editor.addCommand('equation-window', function (data: any) {
|
|
13
|
-
return editor.windowManager.openUrl({
|
|
14
|
-
url: '/plugins/tinymce/math',
|
|
15
|
-
title: 'Equation editor',
|
|
16
|
-
buttons: [
|
|
17
|
-
{
|
|
18
|
-
type: 'cancel',
|
|
19
|
-
text: 'cancel',
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
type: 'custom',
|
|
23
|
-
text: 'submit',
|
|
24
|
-
primary: true,
|
|
25
|
-
},
|
|
26
|
-
],
|
|
27
|
-
onAction: () => {
|
|
28
|
-
if (data.currentTarget) {
|
|
29
|
-
editor.selection.select(data.currentTarget)
|
|
30
|
-
}
|
|
31
|
-
editor.selection.setContent(
|
|
32
|
-
` <span class='windward-math-content'>` +
|
|
33
|
-
formula +
|
|
34
|
-
`</span> `
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
editor.windowManager.close()
|
|
38
|
-
setOnClickEquationContent(editor)
|
|
39
|
-
},
|
|
40
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
41
|
-
onMessage: (instance: any, message: any) => {
|
|
42
|
-
switch (message.mceAction) {
|
|
43
|
-
case 'equation-insert':
|
|
44
|
-
formula = message.content
|
|
45
|
-
break
|
|
46
|
-
case 'math-plugin-mounted':
|
|
47
|
-
window.postMessage({ latex: data.latex ?? '' }, '*')
|
|
48
|
-
break
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
})
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
editor.ui.registry.addButton('mathButton', {
|
|
55
|
-
icon: 'insertMath',
|
|
56
|
-
onAction: () => {
|
|
57
|
-
editor.execCommand('equation-window', true)
|
|
58
|
-
},
|
|
59
|
-
})
|
|
60
|
-
editor.ui.registry.addMenuItem('math', {
|
|
61
|
-
text: 'Math',
|
|
62
|
-
icon: 'insertMath',
|
|
63
|
-
onAction: () => {
|
|
64
|
-
editor.execCommand('equation-window', true)
|
|
65
|
-
},
|
|
66
|
-
})
|
|
67
|
-
editor.on('init', function () {
|
|
68
|
-
setOnClickEquationContent(editor)
|
|
69
|
-
setOnClickFillInBlank(editor)
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
editor.on('SetContent', function () {
|
|
73
|
-
setOnClickEquationContent(editor)
|
|
74
|
-
setOnClickFillInBlank(editor)
|
|
75
|
-
})
|
|
76
|
-
editor.on('input', function () {
|
|
77
|
-
setOnClickEquationContent(editor)
|
|
78
|
-
setOnClickFillInBlank(editor)
|
|
79
|
-
})
|
|
80
|
-
function setOnClickEquationContent(editor: any) {
|
|
81
|
-
const tinymceDoc = editor.getDoc()
|
|
82
|
-
const mqSpan = tinymceDoc.getElementsByClassName(
|
|
83
|
-
'windward-math-content'
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
// Add onclick listener to all equation content
|
|
87
|
-
for (const equationContent of mqSpan) {
|
|
88
|
-
equationContent.contentEditable = 'true'
|
|
89
|
-
if (equationContent.onclick) {
|
|
90
|
-
continue
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
equationContent.ondblclick = (event: any) => {
|
|
94
|
-
event.stopPropagation()
|
|
95
|
-
editor.execCommand('equation-window', {
|
|
96
|
-
latex: event.target.innerText,
|
|
97
|
-
currentTarget: event.target,
|
|
98
|
-
})
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
editor.addCommand('fib-window', function (data: any) {
|
|
104
|
-
return editor.windowManager.openUrl({
|
|
105
|
-
url: '/plugins/tinymce/FIB',
|
|
106
|
-
title: 'Fill in the blank',
|
|
107
|
-
buttons: [
|
|
108
|
-
{
|
|
109
|
-
type: 'cancel',
|
|
110
|
-
text: 'cancel',
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
type: 'custom',
|
|
114
|
-
text: 'submit',
|
|
115
|
-
primary: true,
|
|
116
|
-
},
|
|
117
|
-
],
|
|
118
|
-
onAction: () => {
|
|
119
|
-
if (data.currentTarget) {
|
|
120
|
-
editor.selection.select(data.currentTarget)
|
|
121
|
-
}
|
|
122
|
-
editor.selection.setContent(
|
|
123
|
-
`<span class='windward-fill-blank' data-feedback =` +
|
|
124
|
-
fillInBlank.feedback +
|
|
125
|
-
` >` +
|
|
126
|
-
fillInBlank.answer +
|
|
127
|
-
`</span>`
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
editor.windowManager.close()
|
|
131
|
-
setOnClickFillInBlank(editor)
|
|
132
|
-
},
|
|
133
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
134
|
-
onMessage: (instance: any, message: any) => {
|
|
135
|
-
switch (message.mceAction) {
|
|
136
|
-
case 'fib-insert':
|
|
137
|
-
fillInBlank = {
|
|
138
|
-
answer: message.content.answer,
|
|
139
|
-
feedback: message.content.feedback,
|
|
140
|
-
}
|
|
141
|
-
break
|
|
142
|
-
case 'fib-plugin-mounted':
|
|
143
|
-
window.parent.postMessage(
|
|
144
|
-
{ answer: data.answer, feedback: data.feedback },
|
|
145
|
-
'*'
|
|
146
|
-
)
|
|
147
|
-
break
|
|
148
|
-
}
|
|
149
|
-
},
|
|
150
|
-
})
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
editor.ui.registry.addIcon(
|
|
154
|
-
'insertFIB',
|
|
155
|
-
'<svg width="20px" height="20px" viewBox="0 0 24 24"><path d="M17,7H22V17H17V19A1,1 0 0,0 18,20H20V22H17.5C16.95,22 16,21.55 16,21C16,21.55 15.05,22 14.5,22H12V20H14A1,1 0 0,0 15,19V5A1,1 0 0,0 14,4H12V2H14.5C15.05,2 16,2.45 16,3C16,2.45 16.95,2 17.5,2H20V4H18A1,1 0 0,0 17,5V7M2,7H13V9H4V15H13V17H2V7M20,15V9H17V15H20Z" /></svg>'
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
editor.ui.registry.addIcon(
|
|
159
|
-
'glossaryIcon',
|
|
160
|
-
'<svg viewBox="0 0 24 24" width="20px" height="20px" ><path d="M3,15H1V3A2,2 0 0,1 3,1H19V3H3V15M12,23A1,1 0 0,1 11,22V19H7A2,2 0 0,1 5,17V7A2,2 0 0,1 7,5H21A2,2 0 0,1 23,7V17A2,2 0 0,1 21,19H16.9L13.2,22.71C13,22.89 12.76,23 12.5,23H12M9,9V11H19V9H9M9,13V15H17V13H9Z"></path></svg>'
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
/* Add a button that opens a window */
|
|
164
|
-
editor.ui.registry.addButton('glossaryButton', {
|
|
165
|
-
icon: 'glossaryIcon',
|
|
166
|
-
onAction: function () {
|
|
167
|
-
/* apply format */
|
|
168
|
-
editor.formatter.apply('glossary')
|
|
169
|
-
},
|
|
170
|
-
})
|
|
171
|
-
/* Add a button that opens a window */
|
|
172
|
-
editor.ui.registry.addButton('fibButton', {
|
|
173
|
-
icon: 'insertFIB',
|
|
174
|
-
onAction: function () {
|
|
175
|
-
/* Open window */
|
|
176
|
-
editor.execCommand('fib-window', true)
|
|
177
|
-
},
|
|
178
|
-
})
|
|
179
|
-
editor.ui.registry.addMenuItem('FIB', {
|
|
180
|
-
text: 'FIll in the blank',
|
|
181
|
-
icon: 'insertFIB',
|
|
182
|
-
onAction: () => {
|
|
183
|
-
editor.execCommand('fib-window', true)
|
|
184
|
-
},
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
function setOnClickFillInBlank(editor: any) {
|
|
188
|
-
const tinymceDoc = editor.getDoc()
|
|
189
|
-
const mqSpan = tinymceDoc.getElementsByClassName('windward-fill-blank')
|
|
190
|
-
|
|
191
|
-
// Add onclick listener to all equation content
|
|
192
|
-
for (const fib of mqSpan) {
|
|
193
|
-
fib.contentEditable = 'true'
|
|
194
|
-
if (fib.onclick) {
|
|
195
|
-
continue
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
fib.ondblclick = (event: any) => {
|
|
199
|
-
event.stopPropagation()
|
|
200
|
-
editor.execCommand('fib-window', {
|
|
201
|
-
answer: event.target.innerText,
|
|
202
|
-
feedback: event.target.getAttribute('data-feedback'),
|
|
203
|
-
currentTarget: event.target,
|
|
204
|
-
})
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
export { WindwardPlugins }
|