@windward/core 0.4.0 → 0.4.2

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.
Files changed (46) hide show
  1. package/components/Content/Blocks/BlockQuote.vue +2 -2
  2. package/components/Content/Blocks/ClickableIcons.vue +1 -1
  3. package/components/Content/Blocks/Email.vue +10 -6
  4. package/components/Content/Blocks/Feedback.vue +12 -5
  5. package/components/Content/Blocks/Image.vue +2 -2
  6. package/components/Content/Blocks/OpenResponse.vue +7 -3
  7. package/components/Content/Blocks/OpenResponseCollate.vue +1 -1
  8. package/components/Content/Blocks/ScenarioChoice.vue +2 -2
  9. package/components/Content/Blocks/UserUpload/DisplayUserFilesTable.vue +2 -0
  10. package/components/Content/Blocks/UserUpload.vue +1 -0
  11. package/components/Content/Blocks/Video.vue +2 -2
  12. package/components/Navigation/Items/AskTheExpert.vue +1 -0
  13. package/components/Settings/AccordionSettings.vue +2 -0
  14. package/components/Settings/ClickableIconsSettings.vue +5 -1
  15. package/components/Settings/EmailSettings.vue +6 -1
  16. package/components/Settings/FeedbackSettings.vue +1 -0
  17. package/components/Settings/ImageSettings.vue +7 -8
  18. package/components/Settings/MathSettings.vue +1 -1
  19. package/components/Settings/ScenarioChoiceSettings.vue +1 -0
  20. package/components/Settings/TabSettings.vue +7 -1
  21. package/components/Settings/TextEditorSettings.vue +4 -2
  22. package/components/Settings/UserUploadSettings.vue +1 -1
  23. package/components/Settings/VideoSettings.vue +2 -0
  24. package/components/utils/FillInBlank/FillInBlankInput.vue +3 -0
  25. package/components/utils/MathExpressionEditor.vue +89 -56
  26. package/components/utils/MathLiveWrapper.vue +47 -25
  27. package/components/utils/TinyMCEWrapper.vue +107 -18
  28. package/components/utils/assets/tinymce/ui/dark/skin.scss +1 -1
  29. package/components/utils/assets/tinymce/ui/light/skin.scss +1 -1
  30. package/components/utils/glossary/CourseGlossary.vue +2 -0
  31. package/config/tinymce.config.ts +19 -19
  32. package/helpers/MathHelper.ts +59 -0
  33. package/helpers/tinymce/WindwardPlugins.ts +335 -0
  34. package/i18n/en-US/components/settings/image.ts +2 -0
  35. package/i18n/en-US/components/utils/math_expression_editor.ts +4 -3
  36. package/i18n/en-US/components/utils/tiny_mce_wrapper.ts +1 -0
  37. package/i18n/es-ES/components/settings/image.ts +2 -0
  38. package/i18n/es-ES/components/utils/math_expression_editor.ts +2 -1
  39. package/i18n/es-ES/components/utils/tiny_mce_wrapper.ts +1 -0
  40. package/i18n/sv-SE/components/settings/image.ts +2 -0
  41. package/i18n/sv-SE/components/utils/math_expression_editor.ts +1 -0
  42. package/i18n/sv-SE/components/utils/tiny_mce_wrapper.ts +1 -0
  43. package/package.json +3 -2
  44. package/test/helpers/MathHelper.spec.js +14 -1
  45. package/tsconfig.json +4 -1
  46. package/helpers/tinymce/plugin.ts +0 -210
@@ -1,5 +1,9 @@
1
1
  <template>
2
- <math-field :id="id" style="font-size: 1.5rem; display: block"
2
+ <math-field
3
+ :id="id"
4
+ style="font-size: 1.5rem; display: block"
5
+ @change="onInput"
6
+ @input="onInput"
3
7
  ><slot></slot
4
8
  ></math-field>
5
9
  </template>
@@ -21,6 +25,13 @@ export default {
21
25
  }
22
26
  },
23
27
  watch: {
28
+ value(newVal) {
29
+ let current = this.$el.getValue()
30
+ newVal !== current &&
31
+ this.$el.setValue(newVal, {
32
+ silenceNotifications: !0,
33
+ })
34
+ },
24
35
  options: {
25
36
  deep: true,
26
37
  handler(newValue) {
@@ -29,30 +40,27 @@ export default {
29
40
  },
30
41
  },
31
42
  mounted() {
32
- this.$nextTick(() => {
33
- this.$el.setOptions({
34
- ...this.options,
35
- macros: {
36
- ...this.$el.getOptions('macros'),
37
- smallfrac: '{}^{#1}\\!\\!/\\!{}_{#2}',
38
- },
39
- onContentDidChange: () => {
40
- this.$emit('input', this.$el.getValue())
41
- this.$emit('change')
42
- },
43
- onFocus: () => {
44
- this.$emit('focus')
45
- },
46
- onBlur: () => {
47
- this.$emit('blur')
48
- },
49
- onUndoStateDidChange: (t, e) => {
50
- this.$emit('undo-state-did-change', e)
51
- },
52
- onReadAloudStatus: (t, e) => {
53
- this.$emit('read-aloud-status-change', e)
54
- },
55
- })
43
+ //this.$nextTick(() => this.$el.setOptions(this.options))
44
+ this.$el.mathVirtualKeyboardPolicy = 'manual'
45
+
46
+ this.$el.menuItems = this.$el.menuItems.filter((item) => {
47
+ const itemIdPrefix = item.id ? item.id.split('-')[0] : null
48
+
49
+ if (itemIdPrefix === 'color' || itemIdPrefix === 'background') {
50
+ return false
51
+ }
52
+
53
+ if (
54
+ !_.isEmpty(item.submenu) &&
55
+ typeof item.submenu !== 'undefined'
56
+ ) {
57
+ item.submenu = item.submenu.filter((subItem) => {
58
+ return subItem.id
59
+ ? !subItem.id.startsWith('mode-latex')
60
+ : true
61
+ })
62
+ }
63
+ return true
56
64
  })
57
65
  if (this.value !== '') {
58
66
  this.$el.setValue(this.value)
@@ -83,6 +91,20 @@ export default {
83
91
  setValue(value) {
84
92
  this.$el.setValue(value)
85
93
  },
94
+ onInput() {
95
+ this.$emit('input', this.$el.getValue())
96
+ this.$emit('change')
97
+ },
86
98
  },
87
99
  }
88
100
  </script>
101
+ <style>
102
+ @media not (pointer: coarse) {
103
+ math-field::part(virtual-keyboard-toggle) {
104
+ display: none;
105
+ }
106
+ math-field::part(menu-toggle) {
107
+ display: none;
108
+ }
109
+ }
110
+ </style>
@@ -26,9 +26,27 @@
26
26
  <v-btn-toggle dense multiple class="pt-1 d-flex justify-end">
27
27
  <slot name="actions-prepend" :render="render"></slot>
28
28
  <slot name="actions" :render="render">
29
+ <v-btn-toggle dense v-if="allowRead">
30
+ <v-btn v-if="render" color="primary" outlined @click="read">
31
+ <v-icon> mdi-play</v-icon>
32
+ </v-btn>
33
+ <v-btn
34
+ v-if="render"
35
+ color="primary"
36
+ outlined
37
+ @click="pause"
38
+ >
39
+ <v-icon> mdi-pause</v-icon>
40
+ </v-btn>
41
+ <v-btn v-if="render" color="primary" outlined @click="stop">
42
+ <v-icon> mdi-stop</v-icon>
43
+ </v-btn>
44
+ </v-btn-toggle>
45
+
29
46
  <v-btn
30
47
  v-if="render"
31
48
  color="primary"
49
+ elevation="0"
32
50
  outlined
33
51
  @click="onClickOutside"
34
52
  >{{
@@ -47,6 +65,7 @@
47
65
  import _ from 'lodash'
48
66
  import 'tinymce'
49
67
  import Editor from '@tinymce/tinymce-vue'
68
+ import { getTinymce } from '@tinymce/tinymce-vue/lib/cjs/main/ts/TinyMCE'
50
69
 
51
70
  /* Required TinyMCE components */
52
71
  import 'tinymce/icons/default/icons.min.js'
@@ -96,11 +115,12 @@ import 'tinymce/skins/ui/oxide-dark/content.js'
96
115
  import 'tinymce/skins/content/default/content.js'
97
116
  import 'tinymce/skins/content/dark/content.js'
98
117
 
99
- import { WindwardPlugins } from '../../helpers/tinymce/plugin'
118
+ import { WindwardPlugins } from '../../helpers/tinymce/WindwardPlugins'
100
119
  import ContentCss from '!raw-loader!sass-loader!./assets/tinymce/content/global.scss'
101
120
  import EditorCss from '!raw-loader!sass-loader!./assets/tinymce/ui/global.scss'
102
121
  import Crypto from '~/helpers/Crypto'
103
122
  import MathHelper from '../../helpers/MathHelper'
123
+ import { Synthesizer } from 'speechreader'
104
124
  export default {
105
125
  name: 'ContentEditorRichText',
106
126
  components: {
@@ -108,7 +128,6 @@ export default {
108
128
  },
109
129
  props: {
110
130
  value: { type: String, required: true, default: '' },
111
- api_key: { type: String, required: true, default: '' },
112
131
  height: { type: Number, required: false, default: null },
113
132
  menubar: {
114
133
  type: String,
@@ -119,10 +138,11 @@ export default {
119
138
  type: String,
120
139
  required: false,
121
140
  default:
122
- 'undo redo | formatselect | fontsizeselect | bold italic underline strikethrough removeformat | alignleft aligncenter alignright | table bullist numlist outdent indent | glossaryButton fibButton mathButton ',
141
+ 'undo redo | styles | bold italic underline strikethrough removeformat | alignleft aligncenter alignright | table bullist numlist outdent indent | glossaryButton fibFormatButton mathButton ',
123
142
  },
124
143
  root_block: { type: String, required: false, default: 'div' },
125
144
  label: { type: String, required: true, default: '' },
145
+ allowRead: { type: Boolean, required: false, default: false },
126
146
  },
127
147
  beforeMount() {
128
148
  if (MathHelper.containsLatex(this.value)) {
@@ -136,6 +156,8 @@ export default {
136
156
  render: false,
137
157
  text: '',
138
158
  seed: Crypto.id(),
159
+ synthesizer: null,
160
+ paused: false,
139
161
  }
140
162
  },
141
163
  watch: {
@@ -177,6 +199,7 @@ export default {
177
199
  visual: false,
178
200
  forced_root_block: this.root_block,
179
201
  menubar: this.filteredMenubar,
202
+ toolbar_mode: 'sliding',
180
203
  browser_spellcheck: true,
181
204
  contextmenu: false,
182
205
  statusbar: false,
@@ -187,8 +210,6 @@ export default {
187
210
  body_class: this.isDarkTheme
188
211
  ? 'editor--theme-dark'
189
212
  : 'editor--theme-light',
190
- block_formats:
191
- 'Paragraph=p; Heading 3=h3; Heading 4=h4; Heading 5=h5; Heading 6=h6;',
192
213
  menu: {
193
214
  insert: {
194
215
  title: 'Insert',
@@ -302,9 +323,11 @@ export default {
302
323
  ],
303
324
  setup() {
304
325
  // Here we can add plugin
305
- window.tinymce.PluginManager.add(
326
+ getTinymce().PluginManager.add(
306
327
  'WindwardToolKit',
307
- WindwardPlugins
328
+ (editor) => {
329
+ new WindwardPlugins(editor)
330
+ }
308
331
  )
309
332
  },
310
333
 
@@ -351,21 +374,59 @@ export default {
351
374
  ],
352
375
  },
353
376
  style_formats: [
354
- { title: 'Windward formats' },
355
377
  {
356
- title: this.$t(
357
- 'windward.core.components.utils.tiny_mce_wrapper.term'
358
- ),
359
- format: 'glossary',
378
+ title: 'Headings',
379
+ items: [
380
+ { title: 'Heading 2', format: 'h2' },
381
+ { title: 'Heading 3', format: 'h3' },
382
+ { title: 'Heading 4', format: 'h4' },
383
+ { title: 'Heading 5', format: 'h5' },
384
+ { title: 'Heading 6', format: 'h6' },
385
+ ],
360
386
  },
361
387
  {
362
- title: this.$t(
363
- 'windward.core.components.utils.tiny_mce_wrapper.fill_blank'
364
- ),
365
- format: 'fib',
388
+ title: 'Inline',
389
+ items: [
390
+ { title: 'Bold', format: 'bold' },
391
+ { title: 'Italic', format: 'italic' },
392
+ { title: 'Underline', format: 'underline' },
393
+ { title: 'Strikethrough', format: 'strikethrough' },
394
+ { title: 'Superscript', format: 'superscript' },
395
+ { title: 'Subscript', format: 'subscript' },
396
+ { title: 'Code', format: 'code' },
397
+ ],
398
+ },
399
+ {
400
+ title: 'Blocks',
401
+ items: [{ title: 'Paragraph', format: 'p' }],
402
+ },
403
+ {
404
+ title: 'Align',
405
+ items: [
406
+ { title: 'Left', format: 'alignleft' },
407
+ { title: 'Center', format: 'aligncenter' },
408
+ { title: 'Right', format: 'alignright' },
409
+ { title: 'Justify', format: 'alignjustify' },
410
+ ],
411
+ },
412
+ {
413
+ title: 'Windward',
414
+ items: [
415
+ {
416
+ title: this.$t(
417
+ 'windward.core.components.utils.tiny_mce_wrapper.term'
418
+ ),
419
+ format: 'glossary',
420
+ },
421
+ {
422
+ title: this.$t(
423
+ 'windward.core.components.utils.tiny_mce_wrapper.fill_blank'
424
+ ),
425
+ format: 'fib',
426
+ },
427
+ ],
366
428
  },
367
429
  ],
368
- style_formats_merge: true,
369
430
  placeholder: this.label
370
431
  ? this.label
371
432
  : this.$t(
@@ -390,7 +451,6 @@ export default {
390
451
  importcss_append: true,
391
452
  }
392
453
  },
393
-
394
454
  elementBody() {
395
455
  const ele = document.querySelector('.tinymce-included-' + this.seed)
396
456
 
@@ -401,6 +461,35 @@ export default {
401
461
  },
402
462
  },
403
463
  methods: {
464
+ read() {
465
+ if (this.paused) {
466
+ this.resume()
467
+ } else {
468
+ this.synthesizer = new Synthesizer(
469
+ MathHelper.convertMathContentToSpeakableText(this.text),
470
+ this.$i18n.locale
471
+ )
472
+ this.synthesizer.play()
473
+ }
474
+ },
475
+ pause() {
476
+ if (!this.paused) {
477
+ this.synthesizer.pause()
478
+ this.paused = true
479
+ }
480
+ },
481
+ resume() {
482
+ if (this.synthesizer) {
483
+ this.synthesizer.resume()
484
+ this.paused = false
485
+ }
486
+ },
487
+ stop() {
488
+ if (this.synthesizer) {
489
+ this.synthesizer.stop()
490
+ this.paused = false
491
+ }
492
+ },
404
493
  onEditorFocus() {
405
494
  if (!this.render) {
406
495
  this.seed = Crypto.id()
@@ -426,7 +426,7 @@ button::-moz-focus-inner {
426
426
  background-image: none;
427
427
  border-color: var(--v-primary-base);
428
428
  box-shadow: none;
429
- color: #fff;
429
+ color: var(--v-primary-base);
430
430
  }
431
431
  .tox .tox-button:active:not(:disabled) {
432
432
  background-color: #0054b4;
@@ -426,7 +426,7 @@ button::-moz-focus-inner {
426
426
  background-image: none;
427
427
  border-color: var(--v-primary-base);
428
428
  box-shadow: none;
429
- color: var(--v-background-base);
429
+ color: var(--v-primary-base);
430
430
  }
431
431
  .tox .tox-button:active:not(:disabled) {
432
432
  background-color: inherit;
@@ -101,6 +101,7 @@
101
101
  <v-btn
102
102
  color="error"
103
103
  class="outlined"
104
+ elevation="0"
104
105
  outlined
105
106
  :disabled="
106
107
  !$PermissionService.userHasAccessTo(
@@ -118,6 +119,7 @@
118
119
  <v-btn
119
120
  color="primary"
120
121
  class="outlined"
122
+ elevation="0"
121
123
  outlined
122
124
  :disabled="
123
125
  !$PermissionService.userHasAccessTo(
@@ -7,6 +7,7 @@ export default {
7
7
  groups: [
8
8
  {
9
9
  name: 'windward.core.components.utils.math_expression_editor.basic',
10
+ type: 'basic',
10
11
  buttons: [
11
12
  {
12
13
  cmd: false,
@@ -24,56 +25,51 @@ export default {
24
25
  cmd: false,
25
26
  text: 'i',
26
27
  },
27
- {
28
- cmd: false,
29
- text: '\\$',
30
- },
31
28
  {
32
29
  cmd: false,
33
30
  text: '\\%',
34
31
  },
35
32
  {
36
- cmd: true,
33
+ cmd: false,
37
34
  text: '\\pi',
38
- latex: '\\pi',
39
35
  },
40
36
  {
41
- cmd: true,
37
+ cmd: false,
42
38
  text: 'x^2',
43
39
  latex: '^2',
44
40
  },
45
41
  {
46
- cmd: true,
47
- text: '{x}^{y}',
42
+ cmd: false,
43
+ text: 'x^y',
48
44
  latex: '^\\placeholder{}',
49
45
  },
50
46
  {
51
- cmd: true,
52
- text: '{x}_{y}',
47
+ cmd: false,
48
+ text: 'x_y',
53
49
  latex: '_\\placeholder{}',
54
50
  },
55
51
  {
56
52
  text: '\\sqrt{x}',
57
53
  latex: '\\sqrt{\\placeholder{}}',
58
- cmd: true,
54
+ cmd: false,
59
55
  },
60
56
  {
61
57
  latex: '\\sqrt[\\placeholder{}]{\\placeholder{}}',
62
58
  text: '\\sqrt[n]{x}',
63
- cmd: true,
59
+ cmd: false,
64
60
  },
65
61
  {
66
- cmd: true,
62
+ cmd: false,
67
63
  text: '\\frac{x}{y}',
68
64
  latex: '\\frac',
69
65
  },
70
66
  {
71
- cmd: true,
67
+ cmd: false,
72
68
  text: '\\left|x\\right|',
73
69
  latex: '\\left|\\placeholder{}\\right|',
74
70
  },
75
71
  {
76
- cmd: true,
72
+ cmd: false,
77
73
  text: '\\left\\{x\\right\\}',
78
74
  latex: '\\left\\{\\placeholder{}\\right\\}',
79
75
  },
@@ -85,11 +81,11 @@ export default {
85
81
  cmd: false,
86
82
  text: '(x,y)',
87
83
  },
88
-
89
84
  ],
90
85
  },
91
86
  {
92
87
  name: 'windward.core.components.utils.math_expression_editor.operators',
88
+ type: 'operators',
93
89
  buttons: [
94
90
  {
95
91
  cmd: false,
@@ -160,6 +156,7 @@ export default {
160
156
  },
161
157
  {
162
158
  name: 'windward.core.components.utils.math_expression_editor.trigonometry',
159
+ type: 'trigonometry',
163
160
  buttons: [
164
161
  {
165
162
  cmd: false,
@@ -201,6 +198,7 @@ export default {
201
198
  },
202
199
  {
203
200
  name: 'windward.core.components.utils.math_expression_editor.greek',
201
+ type: 'greek',
204
202
  buttons: [
205
203
  {
206
204
  cmd: false,
@@ -274,6 +272,7 @@ export default {
274
272
  },
275
273
  {
276
274
  name: 'windward.core.components.utils.math_expression_editor.probability',
275
+ type: 'probability',
277
276
  buttons: [
278
277
  {
279
278
  cmd: false,
@@ -300,14 +299,15 @@ export default {
300
299
  },
301
300
  {
302
301
  name: 'windward.core.components.utils.math_expression_editor.eq_systems',
302
+ type: 'eq_systems',
303
303
  buttons: [
304
304
  {
305
305
  cmd: false,
306
- text: '\\begin{cases} x + y \\\\ x + y \\end{cases}',
306
+ text: '\\begin{cases} x+y \\\\ x+y \\end{cases}',
307
307
  },
308
308
  {
309
309
  cmd: false,
310
- text: '\\begin{cases} x + y \\\\ xy \\end{cases}',
310
+ text: '\\begin{cases}x+y\\\\ x-y\\end{cases}',
311
311
  },
312
312
  ],
313
313
  },
@@ -163,6 +163,28 @@ export default class MathHelper {
163
163
  return content
164
164
  }
165
165
 
166
+ /**
167
+ * Converts LaTeX content to speakable text.
168
+ *
169
+ * @param {string} content - The LaTeX content to be converted.
170
+ * @return {string}
171
+ */
172
+ private static convertContentLatexToSpeakableText(content : string): string {
173
+ const regex = /\$\$.*?\$\$/g
174
+
175
+ const matches = content.match(regex)
176
+ if (matches) {
177
+ matches.forEach((match) => {
178
+ content = content.replace(
179
+ match,
180
+ mathLiveObject.convertLatexToSpeakableText(match)
181
+ )
182
+ })
183
+ }
184
+
185
+ return content
186
+ }
187
+
166
188
  /**
167
189
  * convertContentSREnhancedLatexToHtml
168
190
  * @param content
@@ -193,6 +215,29 @@ export default class MathHelper {
193
215
  return content
194
216
  }
195
217
 
218
+ /**
219
+ * Converts enhanced LaTeX content to speakable text.
220
+ *
221
+ * @param {string} content - The enhanced LaTeX content to convert.
222
+ * @return {string}
223
+ */
224
+ private static convertContentSREnhancedLatexToSpeakableText(content: string): string {
225
+ const regex = /\*\*.*?\*\*/g
226
+
227
+ const matches = content.match(regex)
228
+ if (matches) {
229
+ matches.forEach((match: any) => {
230
+ const mathObject = JSON.parse(match.replace(/\*\*/g, ''))
231
+ content = content.replace(
232
+ match,
233
+ mathObject.sr_text
234
+ )
235
+ })
236
+ }
237
+
238
+ return content
239
+ }
240
+
196
241
  /**
197
242
  * convertMathContentToHtml
198
243
  * @param content
@@ -208,6 +253,20 @@ export default class MathHelper {
208
253
  ).replace(/contenteditable="true"/g, '')
209
254
  }
210
255
 
256
+ /**
257
+ * Converts math content to speakable text.
258
+ *
259
+ * @param {string} content - The math content to be converted.
260
+ * @return {string} .
261
+ */
262
+ public static convertMathContentToSpeakableText(content: string): string {
263
+ return MathHelper.convertContentSREnhancedLatexToSpeakableText(
264
+ MathHelper.convertContentLatexToSpeakableText(
265
+ MathHelper.convertContentMathMLtoLatex(content)
266
+ )
267
+ ).replace(/contenteditable="true"/g, '')
268
+ }
269
+
211
270
  /**
212
271
  * convertMathMLToLatex
213
272
  * @param content