@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.
- package/components/Content/Blocks/Accordion.vue +37 -0
- package/components/Content/Blocks/BlockQuote.vue +2 -2
- package/components/Content/Blocks/ClickableIcons.vue +108 -21
- package/components/Content/Blocks/Email.vue +19 -6
- package/components/Content/Blocks/Feedback.vue +12 -5
- package/components/Content/Blocks/Image.vue +47 -19
- 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 +82 -11
- package/components/Navigation/Items/AskTheExpert.vue +1 -0
- package/components/Settings/AccordionSettings.vue +55 -0
- package/components/Settings/ClickableIconsSettings.vue +89 -8
- package/components/Settings/EmailSettings.vue +6 -10
- package/components/Settings/FeedbackSettings.vue +1 -0
- package/components/Settings/ImageSettings.vue +142 -39
- package/components/Settings/MathSettings.vue +1 -1
- package/components/Settings/OpenResponseSettings.vue +1 -2
- 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 +102 -63
- package/components/utils/ContentViewer.vue +6 -1
- package/components/utils/FillInBlank/FillInBlankInput.vue +3 -0
- package/components/utils/MathExpressionEditor.vue +37 -11
- package/components/utils/MathLiveWrapper.vue +47 -25
- package/components/utils/TinyMCEWrapper.vue +214 -34
- package/components/utils/assets/tinymce/content/dark/content.scss +4 -0
- package/components/utils/assets/tinymce/{css/content.scss → content/global.scss} +38 -37
- package/components/utils/assets/tinymce/content/light/content.scss +4 -0
- package/components/utils/assets/tinymce/ui/dark/content.scss +803 -0
- package/components/utils/assets/tinymce/ui/dark/skin.scss +4727 -0
- package/components/utils/assets/tinymce/ui/global.scss +19 -0
- package/components/utils/assets/tinymce/ui/light/content.scss +822 -0
- package/components/utils/assets/tinymce/ui/light/skin.scss +4731 -0
- package/components/utils/glossary/CourseGlossary.vue +3 -1
- package/config/tinymce.config.ts +32 -20
- package/helpers/FillInBlankHelper.ts +34 -28
- package/helpers/GlossaryHelper.ts +90 -73
- package/helpers/MathHelper.ts +108 -28
- package/helpers/tinymce/WindwardPlugins.ts +335 -0
- package/i18n/en-US/components/settings/clickable_icon.ts +2 -0
- package/i18n/en-US/components/settings/image.ts +6 -1
- package/i18n/en-US/components/utils/tiny_mce_wrapper.ts +1 -0
- package/i18n/en-US/shared/settings.ts +3 -0
- package/i18n/es-ES/components/settings/clickable_icon.ts +2 -0
- package/i18n/es-ES/components/settings/image.ts +8 -1
- package/i18n/es-ES/components/utils/tiny_mce_wrapper.ts +1 -0
- package/i18n/es-ES/shared/settings.ts +3 -0
- package/i18n/sv-SE/components/settings/clickable_icon.ts +2 -0
- package/i18n/sv-SE/components/settings/image.ts +6 -1
- package/i18n/sv-SE/components/utils/tiny_mce_wrapper.ts +1 -0
- package/i18n/sv-SE/shared/settings.ts +3 -0
- package/package.json +6 -4
- package/test/Components/Settings/AccordionSettings.spec.js +16 -2
- package/test/__mocks__/contentBlockMock.js +6 -0
- package/test/__mocks__/contentSettingsMock.js +6 -0
- package/test/helpers/MathHelper.spec.js +36 -4
- package/tsconfig.json +4 -0
- 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="
|
|
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(
|
package/config/tinymce.config.ts
CHANGED
|
@@ -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:
|
|
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:
|
|
34
|
-
text: '
|
|
35
|
-
latex: '
|
|
41
|
+
cmd: false,
|
|
42
|
+
text: 'x^2',
|
|
43
|
+
latex: '^2',
|
|
36
44
|
},
|
|
37
45
|
{
|
|
38
|
-
cmd:
|
|
39
|
-
text: '
|
|
40
|
-
latex: '
|
|
46
|
+
cmd: false,
|
|
47
|
+
text: 'x^y',
|
|
48
|
+
latex: '^\\placeholder{}',
|
|
41
49
|
},
|
|
42
50
|
{
|
|
43
|
-
cmd:
|
|
44
|
-
text: '
|
|
45
|
-
latex: '
|
|
51
|
+
cmd: false,
|
|
52
|
+
text: 'x_y',
|
|
53
|
+
latex: '_\\placeholder{}',
|
|
46
54
|
},
|
|
47
55
|
{
|
|
48
56
|
text: '\\sqrt{x}',
|
|
49
|
-
latex: '\\sqrt{
|
|
50
|
-
cmd:
|
|
57
|
+
latex: '\\sqrt{\\placeholder{}}',
|
|
58
|
+
cmd: false,
|
|
51
59
|
},
|
|
52
60
|
{
|
|
53
|
-
latex: '\\sqrt[\\placeholder{
|
|
61
|
+
latex: '\\sqrt[\\placeholder{}]{\\placeholder{}}',
|
|
54
62
|
text: '\\sqrt[n]{x}',
|
|
55
|
-
cmd:
|
|
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
|
-
|
|
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,
|
|
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 (
|
|
19
|
-
|
|
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
|
-
|
|
24
|
-
|
|
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(
|
|
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
|
|
1
|
+
import GlossaryTerm from '../helpers/GlossaryTerm'
|
|
2
2
|
import * as _ from 'lodash'
|
|
3
3
|
|
|
4
4
|
export default class GlossaryHelper {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
+
|
|
33
|
-
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
|
|
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(
|
|
47
|
-
|
|
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
|
|
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 (
|
|
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(
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const
|
|
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
|
|
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
|
|
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 (
|
|
104
|
-
unVerifiedTerms.push(
|
|
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
|
|
113
|
-
const regex = new RegExp(
|
|
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
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
package/helpers/MathHelper.ts
CHANGED
|
@@ -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
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* @
|
|
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> `;
|
|
81
|
+
if (match.match(/\$\$.*?\$\$/g) !== null && temp !== '') {
|
|
82
|
+
wrapped = `<span class ='windward-math-content'>$$$${temp}$$$</span> `;
|
|
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 (
|
|
83
|
-
content =
|
|
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
|
-
|
|
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
|
/**
|