@windward/core 0.3.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.
Files changed (63) hide show
  1. package/components/Content/Blocks/Accordion.vue +37 -0
  2. package/components/Content/Blocks/BlockQuote.vue +2 -2
  3. package/components/Content/Blocks/ClickableIcons.vue +108 -21
  4. package/components/Content/Blocks/Email.vue +19 -6
  5. package/components/Content/Blocks/Feedback.vue +12 -5
  6. package/components/Content/Blocks/Image.vue +47 -19
  7. package/components/Content/Blocks/OpenResponse.vue +7 -3
  8. package/components/Content/Blocks/OpenResponseCollate.vue +1 -1
  9. package/components/Content/Blocks/ScenarioChoice.vue +2 -2
  10. package/components/Content/Blocks/UserUpload/DisplayUserFilesTable.vue +2 -0
  11. package/components/Content/Blocks/UserUpload.vue +1 -0
  12. package/components/Content/Blocks/Video.vue +82 -11
  13. package/components/Navigation/Items/AskTheExpert.vue +1 -0
  14. package/components/Settings/AccordionSettings.vue +55 -0
  15. package/components/Settings/ClickableIconsSettings.vue +89 -8
  16. package/components/Settings/EmailSettings.vue +6 -10
  17. package/components/Settings/FeedbackSettings.vue +1 -0
  18. package/components/Settings/ImageSettings.vue +142 -39
  19. package/components/Settings/MathSettings.vue +1 -1
  20. package/components/Settings/OpenResponseSettings.vue +1 -2
  21. package/components/Settings/ScenarioChoiceSettings.vue +1 -0
  22. package/components/Settings/TabSettings.vue +7 -1
  23. package/components/Settings/TextEditorSettings.vue +3 -2
  24. package/components/Settings/UserUploadSettings.vue +1 -1
  25. package/components/Settings/VideoSettings.vue +102 -63
  26. package/components/utils/ContentViewer.vue +6 -1
  27. package/components/utils/FillInBlank/FillInBlankInput.vue +3 -0
  28. package/components/utils/MathExpressionEditor.vue +37 -11
  29. package/components/utils/MathLiveWrapper.vue +47 -25
  30. package/components/utils/TinyMCEWrapper.vue +214 -34
  31. package/components/utils/assets/tinymce/content/dark/content.scss +4 -0
  32. package/components/utils/assets/tinymce/{css/content.scss → content/global.scss} +38 -37
  33. package/components/utils/assets/tinymce/content/light/content.scss +4 -0
  34. package/components/utils/assets/tinymce/ui/dark/content.scss +803 -0
  35. package/components/utils/assets/tinymce/ui/dark/skin.scss +4727 -0
  36. package/components/utils/assets/tinymce/ui/global.scss +19 -0
  37. package/components/utils/assets/tinymce/ui/light/content.scss +822 -0
  38. package/components/utils/assets/tinymce/ui/light/skin.scss +4731 -0
  39. package/components/utils/glossary/CourseGlossary.vue +3 -1
  40. package/config/tinymce.config.ts +32 -20
  41. package/helpers/FillInBlankHelper.ts +34 -28
  42. package/helpers/GlossaryHelper.ts +90 -73
  43. package/helpers/MathHelper.ts +108 -28
  44. package/helpers/tinymce/WindwardPlugins.ts +335 -0
  45. package/i18n/en-US/components/settings/clickable_icon.ts +2 -0
  46. package/i18n/en-US/components/settings/image.ts +6 -1
  47. package/i18n/en-US/components/utils/tiny_mce_wrapper.ts +1 -0
  48. package/i18n/en-US/shared/settings.ts +3 -0
  49. package/i18n/es-ES/components/settings/clickable_icon.ts +2 -0
  50. package/i18n/es-ES/components/settings/image.ts +8 -1
  51. package/i18n/es-ES/components/utils/tiny_mce_wrapper.ts +1 -0
  52. package/i18n/es-ES/shared/settings.ts +3 -0
  53. package/i18n/sv-SE/components/settings/clickable_icon.ts +2 -0
  54. package/i18n/sv-SE/components/settings/image.ts +6 -1
  55. package/i18n/sv-SE/components/utils/tiny_mce_wrapper.ts +1 -0
  56. package/i18n/sv-SE/shared/settings.ts +3 -0
  57. package/package.json +6 -4
  58. package/test/Components/Settings/AccordionSettings.spec.js +16 -2
  59. package/test/__mocks__/contentBlockMock.js +6 -0
  60. package/test/__mocks__/contentSettingsMock.js +6 -0
  61. package/test/helpers/MathHelper.spec.js +36 -4
  62. package/tsconfig.json +4 -0
  63. package/helpers/tinymce/plugin.ts +0 -208
@@ -14,7 +14,7 @@
14
14
  <DialogBox
15
15
  v-model="dialog"
16
16
  color="primary"
17
- max-width="600px"
17
+ max-width="600"
18
18
  :trigger="
19
19
  $PermissionService.userHasAccessTo(
20
20
  'windward.global.course,windward.organization.course.contentBlock',
@@ -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,
@@ -25,50 +26,56 @@ export default {
25
26
  text: 'i',
26
27
  },
27
28
  {
28
- cmd: true,
29
+ cmd: false,
30
+ text: '\\$',
31
+ },
32
+ {
33
+ cmd: false,
34
+ text: '\\%',
35
+ },
36
+ {
37
+ cmd: false,
29
38
  text: '\\pi',
30
- latex: '\\pi',
31
39
  },
32
40
  {
33
- cmd: true,
34
- text: '{x}^{2}',
35
- latex: 'x^2',
41
+ cmd: false,
42
+ text: 'x^2',
43
+ latex: '^2',
36
44
  },
37
45
  {
38
- cmd: true,
39
- text: '{x}^{y}',
40
- latex: '\\placeholder{x}^\\placeholder{y}',
46
+ cmd: false,
47
+ text: 'x^y',
48
+ latex: '^\\placeholder{}',
41
49
  },
42
50
  {
43
- cmd: true,
44
- text: '{x}_{y}',
45
- latex: '\\placeholder{x}_\\placeholder{y}',
51
+ cmd: false,
52
+ text: 'x_y',
53
+ latex: '_\\placeholder{}',
46
54
  },
47
55
  {
48
56
  text: '\\sqrt{x}',
49
- latex: '\\sqrt{x}',
50
- cmd: true,
57
+ latex: '\\sqrt{\\placeholder{}}',
58
+ cmd: false,
51
59
  },
52
60
  {
53
- latex: '\\sqrt[\\placeholder{n}]{\\placeholder{x}}',
61
+ latex: '\\sqrt[\\placeholder{}]{\\placeholder{}}',
54
62
  text: '\\sqrt[n]{x}',
55
- cmd: true,
63
+ cmd: false,
56
64
  },
57
65
  {
58
66
  cmd: false,
59
67
  text: '\\frac{x}{y}',
60
- latex:
61
- '\\frac\n' +
62
- ' {\\placeholder[numerator]{?}}\n' +
63
- ' {\\placeholder[denominator]{?}}',
68
+ latex: '\\frac',
64
69
  },
65
70
  {
66
71
  cmd: false,
67
72
  text: '\\left|x\\right|',
73
+ latex: '\\left|\\placeholder{}\\right|',
68
74
  },
69
75
  {
70
76
  cmd: false,
71
- text: '\\left\\{\\right\\}',
77
+ text: '\\left\\{x\\right\\}',
78
+ latex: '\\left\\{\\placeholder{}\\right\\}',
72
79
  },
73
80
  {
74
81
  cmd: false,
@@ -82,6 +89,7 @@ export default {
82
89
  },
83
90
  {
84
91
  name: 'windward.core.components.utils.math_expression_editor.operators',
92
+ type: 'operators',
85
93
  buttons: [
86
94
  {
87
95
  cmd: false,
@@ -152,6 +160,7 @@ export default {
152
160
  },
153
161
  {
154
162
  name: 'windward.core.components.utils.math_expression_editor.trigonometry',
163
+ type: 'trigonometry',
155
164
  buttons: [
156
165
  {
157
166
  cmd: false,
@@ -193,6 +202,7 @@ export default {
193
202
  },
194
203
  {
195
204
  name: 'windward.core.components.utils.math_expression_editor.greek',
205
+ type: 'greek',
196
206
  buttons: [
197
207
  {
198
208
  cmd: false,
@@ -266,6 +276,7 @@ export default {
266
276
  },
267
277
  {
268
278
  name: 'windward.core.components.utils.math_expression_editor.probability',
279
+ type: 'probability',
269
280
  buttons: [
270
281
  {
271
282
  cmd: false,
@@ -292,6 +303,7 @@ export default {
292
303
  },
293
304
  {
294
305
  name: 'windward.core.components.utils.math_expression_editor.eq_systems',
306
+ type: 'eq_systems',
295
307
  buttons: [
296
308
  {
297
309
  cmd: false,
@@ -1,53 +1,59 @@
1
-
2
1
  export default class FillInBlankHelper {
3
-
4
- private static FillBlankRegex = /<span.*?.class="windward-fill-blank".*?>(.*?)<\/span>/gi;
2
+ private static FillBlankRegex =
3
+ /<span.*?.class="windward-fill-blank".*?>(.*?)<\/span>/gi
5
4
 
6
5
  public static containsFillInBlank(content: string): boolean {
7
- const regex = new RegExp(this.FillBlankRegex);
6
+ const regex = new RegExp(this.FillBlankRegex)
8
7
 
9
- return regex.exec(content) !== null;
8
+ return regex.exec(content) !== null
10
9
  }
11
10
 
12
11
  public static makeToolTip(htmlString: string): string {
13
- const parser = new DOMParser();
14
- const doc = parser.parseFromString(htmlString, "text/html");
12
+ const parser: any = new DOMParser()
13
+ const doc: any = parser.parseFromString(htmlString, 'text/html')
15
14
  // doc.body.firstChild can be null
16
- let answer = doc.body.firstChild ? doc.body.firstChild.textContent : "";
17
- let feedback = "";
18
- if (doc.body.getElementsByClassName("windward-fill-blank")[0]["dataset"]["feedback"] !== undefined) {
19
- feedback = doc.body.getElementsByClassName("windward-fill-blank")[0]["dataset"]["feedback"];
15
+ let answer = doc.body.firstChild ? doc.body.firstChild.textContent : ''
16
+ let feedback = ''
17
+ if (
18
+ doc.body.getElementsByClassName('windward-fill-blank')[0][
19
+ 'dataset'
20
+ ]['feedback'] !== undefined
21
+ ) {
22
+ feedback = doc.body.getElementsByClassName(
23
+ 'windward-fill-blank'
24
+ )[0]['dataset']['feedback']
20
25
  }
21
26
 
22
27
  return (
23
- "<plugin-core-fill-in-blank-input answer=\"" + answer + "\" description=\"" + feedback + "\">" +
24
- "</template>" + "</plugin-core-fill-in-blank-input>");
25
-
28
+ '<plugin-core-fill-in-blank-input answer="' +
29
+ answer +
30
+ '" description="' +
31
+ feedback +
32
+ '">' +
33
+ '</template>' +
34
+ '</plugin-core-fill-in-blank-input>'
35
+ )
26
36
  }
27
37
 
28
- public escapeHtml(html) {
29
- var text = document.createTextNode(html);
30
- var p = document.createElement("p");
31
- p.appendChild(text);
32
- return p.innerHTML;
38
+ public escapeHtml(html: any) {
39
+ var text = document.createTextNode(html)
40
+ var p = document.createElement('p')
41
+ p.appendChild(text)
42
+ return p.innerHTML
33
43
  }
34
44
 
35
45
  public static renderFillInTHeBlankHtml(content: string, glossary: any) {
36
- const regex = new RegExp(this.FillBlankRegex);
37
-
46
+ const regex = new RegExp(this.FillBlankRegex)
38
47
 
39
- let match;
48
+ let match
40
49
  while ((match = regex.exec(content)) !== null) {
41
50
  // This is necessary to avoid infinite loops with zero-width matches
42
51
  if (match.index === regex.lastIndex) {
43
- regex.lastIndex++;
52
+ regex.lastIndex++
44
53
  }
45
- content = content.replace(
46
- match[0],
47
- this.makeToolTip(match[0])
48
- );
54
+ content = content.replace(match[0], this.makeToolTip(match[0]))
49
55
  }
50
56
 
51
- return content;
57
+ return content
52
58
  }
53
59
  }
@@ -1,53 +1,49 @@
1
- import GlossaryTerm from "../helpers/GlossaryTerm";
1
+ import GlossaryTerm from '../helpers/GlossaryTerm'
2
2
  import * as _ from 'lodash'
3
3
 
4
4
  export default class GlossaryHelper {
5
- private static glossaryRegex = /<span.*?.class="glossary-word".*?>(.*?)<\/span>/g
6
- public static makeToolTip(term :GlossaryTerm,text :String): string {
7
- let word =
8
- _.isString(term.term)?
9
- '<template v-slot:term>' +
10
- text +
11
- '</template>' :''
12
- let definition =
13
- _.isString(term.definition)?
14
- '<template v-slot:definition>' +
15
- term.definition +
16
- '</template>' :''
17
- let alternate_forms =
18
- _.isString(term.alternate_forms)?
19
- '<template v-slot:alternate_forms>' +
20
- term.alternate_forms +
21
- '</template>' :''
22
- let related_terms =
23
- _.isString(term.related_term)?
24
- '<template v-slot:related_terms>' +
25
- term.related_term +
26
- '</template>' :''
27
- return (
28
- '<plugin-core-glossary-tool-tip>' +
29
- word
30
- +
31
- definition
32
- +
33
- alternate_forms
34
- +
35
- related_terms
36
- +
37
- '</plugin-core-glossary-tool-tip>')
38
-
5
+ private static glossaryRegex =
6
+ /<span.*?.class="glossary-word".*?>(.*?)<\/span>/g
7
+ public static makeToolTip(term: GlossaryTerm, text: String): string {
8
+ let word = _.isString(term.term)
9
+ ? '<template v-slot:term>' + text + '</template>'
10
+ : ''
11
+ let definition = _.isString(term.definition)
12
+ ? '<template v-slot:definition>' + term.definition + '</template>'
13
+ : ''
14
+ let alternate_forms = _.isString(term.alternate_forms)
15
+ ? '<template v-slot:alternate_forms>' +
16
+ term.alternate_forms +
17
+ '</template>'
18
+ : ''
19
+ let related_terms = _.isString(term.related_term)
20
+ ? '<template v-slot:related_terms>' +
21
+ term.related_term +
22
+ '</template>'
23
+ : ''
24
+ return (
25
+ '<plugin-core-glossary-tool-tip>' +
26
+ word +
27
+ definition +
28
+ alternate_forms +
29
+ related_terms +
30
+ '</plugin-core-glossary-tool-tip>'
31
+ )
39
32
  }
40
- public static containsGlossaryTerms(content :string): boolean {
33
+ public static containsGlossaryTerms(content: string): boolean {
41
34
  const regex = new RegExp(this.glossaryRegex)
42
35
 
43
36
  return regex.exec(content) !== null
44
37
  }
45
38
 
46
- public static getContentVerifiedGlossaryTerms(content :string, glossary :any) {
47
- const regex = new RegExp( this.glossaryRegex)
39
+ public static getContentVerifiedGlossaryTerms(
40
+ content: string,
41
+ glossary: any
42
+ ) {
43
+ const regex = new RegExp(this.glossaryRegex)
48
44
  const currentGlossary = glossary
49
45
 
50
- let match
46
+ let match: any
51
47
  const verifiedTerms: GlossaryTerm[] = []
52
48
  while ((match = regex.exec(content)) !== null) {
53
49
  // This is necessary to avoid infinite loops with zero-width matches
@@ -56,28 +52,40 @@ export default class GlossaryHelper {
56
52
  }
57
53
 
58
54
  // The result can be accessed through the `m`-variable.
59
- currentGlossary.forEach((term) => {
55
+ currentGlossary.forEach((term: any) => {
60
56
  let addTerm = true
61
- verifiedTerms.forEach((verifiedTerm)=>{
62
- if(_.trim(_.toLower(verifiedTerm.term)) === _.trim(_.toLower(term.term))){
57
+ verifiedTerms.forEach((verifiedTerm) => {
58
+ if (
59
+ _.trim(_.toLower(verifiedTerm.term)) ===
60
+ _.trim(_.toLower(term.term))
61
+ ) {
63
62
  addTerm = false
64
63
  }
65
- })
66
- if (_.trim(_.toLower(match[1]))=== _.trim(_.toLower(term.term)) && addTerm) {
64
+ })
65
+ if (
66
+ _.trim(_.toLower(match[1])) ===
67
+ _.trim(_.toLower(term.term)) &&
68
+ addTerm
69
+ ) {
67
70
  verifiedTerms.push(new GlossaryTerm(term))
68
71
  }
69
72
  })
70
-
71
73
  }
72
74
 
73
75
  return verifiedTerms
74
76
  }
75
77
 
76
- public static getContentUnVerifiedGlossaryTerms(content :string, glossary :any) {
77
- const regex = new RegExp( this.glossaryRegex)
78
-
79
- let match
80
- const verifiedTerms = this.getContentVerifiedGlossaryTerms(content,glossary)
78
+ public static getContentUnVerifiedGlossaryTerms(
79
+ content: string,
80
+ glossary: any
81
+ ) {
82
+ const regex: any = new RegExp(this.glossaryRegex)
83
+
84
+ let match: any
85
+ const verifiedTerms = this.getContentVerifiedGlossaryTerms(
86
+ content,
87
+ glossary
88
+ )
81
89
  const unVerifiedTerms: GlossaryTerm[] = []
82
90
  while ((match = regex.exec(content)) !== null) {
83
91
  // This is necessary to avoid infinite loops with zero-width matches
@@ -87,33 +95,41 @@ export default class GlossaryHelper {
87
95
 
88
96
  let inGlossary = []
89
97
 
90
- inGlossary = glossary.filter(item => {
91
- if(_.trim(_.toLower(item.term ))=== _.trim(_.toLower(match[1]))){
98
+ inGlossary = glossary.filter((item: any) => {
99
+ if (
100
+ _.trim(_.toLower(item.term)) === _.trim(_.toLower(match[1]))
101
+ ) {
92
102
  return item
93
-
94
103
  }
95
- });
104
+ })
96
105
 
97
106
  let inUnverifiedArray = false
98
- unVerifiedTerms.forEach((unVerifiedTerm)=>{
99
- if(_.trim(_.toLower(unVerifiedTerm.term)) === _.trim(_.toLower(match[1]))){
107
+ unVerifiedTerms.forEach((unVerifiedTerm: any) => {
108
+ if (
109
+ _.trim(_.toLower(unVerifiedTerm.term)) ===
110
+ _.trim(_.toLower(match[1]))
111
+ ) {
100
112
  inUnverifiedArray = true
101
113
  }
102
114
  })
103
- if ( inGlossary.length===0 && !inUnverifiedArray){
104
- unVerifiedTerms.push(new GlossaryTerm( {term:_.trim(match[1]),definition:'n/a'}))
115
+ if (inGlossary.length === 0 && !inUnverifiedArray) {
116
+ unVerifiedTerms.push(
117
+ new GlossaryTerm({
118
+ term: _.trim(match[1]),
119
+ definition: 'n/a',
120
+ })
121
+ )
105
122
  }
106
-
107
-
108
123
  }
109
124
 
110
125
  return unVerifiedTerms
111
126
  }
112
- public static renderGlossaryWordsHtml(content :string, glossary :any) {
113
- const regex = new RegExp( this.glossaryRegex)
114
- const currentGlossary = glossary
127
+ public static renderGlossaryWordsHtml(content: string, glossary: any) {
128
+ const regex: any = new RegExp(this.glossaryRegex)
129
+ const currentGlossary: any = glossary
130
+
131
+ let match: any = []
115
132
 
116
- let match
117
133
  while ((match = regex.exec(content)) !== null) {
118
134
  // This is necessary to avoid infinite loops with zero-width matches
119
135
  if (match.index === regex.lastIndex) {
@@ -121,15 +137,16 @@ export default class GlossaryHelper {
121
137
  }
122
138
 
123
139
  // The result can be accessed through the `m`-variable.
124
- currentGlossary.forEach((term) => {
125
- if (_.trim(_.toLower(match[1]))=== _.trim(_.toLower(term.term))) {
126
- content = content.replace(
127
- match[0],
128
- this.makeToolTip(term,match[1])
129
- )
130
- }
131
- })
132
-
140
+ currentGlossary.forEach((term: any) => {
141
+ if (
142
+ _.trim(_.toLower(match[1])) === _.trim(_.toLower(term.term))
143
+ ) {
144
+ content = content.replace(
145
+ match[0],
146
+ this.makeToolTip(term, match[1])
147
+ )
148
+ }
149
+ })
133
150
  }
134
151
 
135
152
  return content
@@ -1,6 +1,6 @@
1
1
  import * as mathLiveObject from 'mathlive'
2
2
  import * as _ from 'lodash'
3
- import { MathMLToLaTeX } from 'mathml-to-latex';
3
+ import { MathMLToLaTeX } from 'mathml-to-latex'
4
4
  export default class MathHelper {
5
5
  /**
6
6
  * expose mathlive object
@@ -67,38 +67,59 @@ export default class MathHelper {
67
67
  return content
68
68
  }
69
69
 
70
+
70
71
  /**
71
- * wrapMathContentForTinyMCE
72
- * @param content
73
- * wrap content in span with math class for tinymce
74
- * @return string
72
+ * Wraps math content.
73
+ *
74
+ * @param {string} match - The matched string to be wrapped.
75
+ * @param {string} content - The content to be replaced.
76
+ * @param {string} temp - The temporary string.
77
+ * @return {string} - The replaced content with wrapped math content.
78
+ */
79
+ private static wrapMathContentMatch(match : string, content: string, temp: string): string {
80
+ let wrapped = `<span class ='windward-math-content'>${match}</span> &nbsp;`;
81
+ if (match.match(/\$\$.*?\$\$/g) !== null && temp !== '') {
82
+ wrapped = `<span class ='windward-math-content'>$$$${temp}$$$</span> &nbsp;`;
83
+ }
84
+ return content.replaceAll(match, wrapped);
85
+ }
86
+
87
+ /**
88
+ * Checks if the given content already has a specific template wrapped around it.
89
+ *
90
+ * @param {string} content - The original content to be checked.
91
+ * @param {string} match - The template string to be compared against.
92
+ * @param {string} temp - The temp string to be compared against for final template.
93
+ * @return {boolean} - True if the content already contains the specified template, false otherwise.
94
+ */
95
+ private static contentAlreadyWrapped(content: string, match: string, temp: string): boolean {
96
+ const wrappedTemplates = [
97
+ `<span class=\"windward-math-content\" contenteditable=\"true\">$$${temp}$$</span>`,
98
+ `<span class='windward-math-content'>$$${temp}$$</span>`,
99
+ `<span class ='windward-math-content'>$$${temp}$$</span>`,
100
+ `<span class=\"windward-math-content\">$$${temp}$$</span>`];
101
+ return _.some(wrappedTemplates, template => _.includes(content, template));
102
+ }
103
+
104
+ /**
105
+ * Wraps math content for TinyMCE.
106
+ *
107
+ * @param {string} content - The content to be wrapped.
108
+ * @returns {string} - The wrapped content.
75
109
  */
76
110
  public static wrapMathContentForTinyMCE(content: string): string {
77
- const regex = /(\*\*|\$\$)(.*?)\1/g
78
- const matches = content.match(regex)
111
+ const regex = /(\*\*|\$\$)(.*?)\1/g;
112
+ const matches = content.match(regex);
113
+
79
114
  if (matches) {
80
115
  matches.forEach((match) => {
81
- const temp = match.replace(/\$\$/g, '')
82
- if (match.match(/\$\$.*?\$\$/g) !== null) {
83
- content = content.replace(
84
- match,
85
- "<span class ='windward-math-content'>$$$" +
86
- temp +
87
- '$$$</span>'
88
- )
89
- }
90
- if (match.match(/\*\*(.*?)\*\*/g) !== null) {
91
- content = content.replace(
92
- match,
93
- "<span class ='windward-math-content'>" +
94
- match +
95
- '</span>'
96
- )
116
+ const temp = match.replace(/\$\$/g, '');
117
+ if (!this.contentAlreadyWrapped(content, match, temp)) {
118
+ content = this.wrapMathContentMatch(match, content, temp);
97
119
  }
98
- })
120
+ });
99
121
  }
100
-
101
- return content
122
+ return content;
102
123
  }
103
124
 
104
125
  /**
@@ -109,7 +130,7 @@ export default class MathHelper {
109
130
  */
110
131
  public static checkMathContentWrappedForTMCE(content: string): boolean {
111
132
  const regex =
112
- /\<span class ='windward-math-content'>(\*\*|\$\$)(.*?)\1<\/span>/g
133
+ /<span\s+class.*?=.*?(windward-math-content).*?>(.*?)<\/span>/g
113
134
 
114
135
  return regex.exec(content) !== null
115
136
  }
@@ -142,6 +163,28 @@ export default class MathHelper {
142
163
  return content
143
164
  }
144
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
+
145
188
  /**
146
189
  * convertContentSREnhancedLatexToHtml
147
190
  * @param content
@@ -172,6 +215,29 @@ export default class MathHelper {
172
215
  return content
173
216
  }
174
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
+
175
241
  /**
176
242
  * convertMathContentToHtml
177
243
  * @param content
@@ -184,7 +250,21 @@ export default class MathHelper {
184
250
  MathHelper.convertContentLatexToHtml(
185
251
  MathHelper.convertContentMathMLtoLatex(content)
186
252
  )
187
- ).replace(/contenteditable="true"/g, '');
253
+ ).replace(/contenteditable="true"/g, '')
254
+ }
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, '')
188
268
  }
189
269
 
190
270
  /**