@windward/core 0.0.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/.editorconfig +13 -0
- package/.eslintrc.js +11 -0
- package/.prettierrc +4 -0
- package/README.md +86 -0
- package/babel.config.js +1 -0
- package/components/Content/Blocks/Accordion.vue +133 -0
- package/components/Content/Blocks/ClickableIcons.vue +98 -0
- package/components/Content/Blocks/Feedback.vue +323 -0
- package/components/Content/Blocks/FeedbackTemplates/FeedbackQuestionLikert.vue +95 -0
- package/components/Content/Blocks/FeedbackTemplates/FeedbackQuestionOpenResponse.vue +45 -0
- package/components/Content/Blocks/FeedbackTemplates/FeedbackQuestionTrueFalse.vue +55 -0
- package/components/Content/Blocks/Image.vue +116 -0
- package/components/Content/Blocks/Math.vue +87 -0
- package/components/Content/Blocks/Tab.vue +89 -0
- package/components/Content/Blocks/Table.vue +68 -0
- package/components/Content/Blocks/UserUpload/DisplayUserFilesTable.vue +157 -0
- package/components/Content/Blocks/UserUpload/ManageDataTableUserFiles.vue +68 -0
- package/components/Content/Blocks/UserUpload.vue +240 -0
- package/components/Content/Blocks/Video.vue +245 -0
- package/components/Navigation/Items/CourseGlossaryToolNav.vue +39 -0
- package/components/Navigation/Items/GlossaryNav.vue +32 -0
- package/components/Navigation/Items/UserUploadNav.vue +35 -0
- package/components/Settings/AccordionSettings.vue +153 -0
- package/components/Settings/ClickableIconsSettings.vue +189 -0
- package/components/Settings/FeedbackSettings.vue +92 -0
- package/components/Settings/ImageSettings.vue +124 -0
- package/components/Settings/MathSettings.vue +81 -0
- package/components/Settings/TabSettings.vue +139 -0
- package/components/Settings/TextEditorSettings.vue +249 -0
- package/components/Settings/UserUploadSettings.vue +151 -0
- package/components/Settings/VideoSettings.vue +699 -0
- package/components/utils/ContentViewer.vue +69 -0
- package/components/utils/MathExpressionEditor.vue +294 -0
- package/components/utils/MathLiveWrapper.vue +86 -0
- package/components/utils/TinyMCEWrapper.vue +103 -0
- package/components/utils/glossary/CourseGlossary.vue +284 -0
- package/components/utils/glossary/CourseGlossaryForm.vue +106 -0
- package/components/utils/glossary/GlossaryToolTip.vue +87 -0
- package/config/tinymce.config.js +136 -0
- package/helpers/GlossaryHelper.ts +137 -0
- package/helpers/GlossaryTerm.ts +31 -0
- package/helpers/MathHelper.ts +236 -0
- package/helpers/tinymce/plugin.ts +83 -0
- package/i18n/en-US/components/content/blocks/feedback.ts +28 -0
- package/i18n/en-US/components/content/blocks/image.ts +5 -0
- package/i18n/en-US/components/content/blocks/index.ts +15 -0
- package/i18n/en-US/components/content/blocks/tab.ts +4 -0
- package/i18n/en-US/components/content/blocks/table.ts +4 -0
- package/i18n/en-US/components/content/blocks/user_upload.ts +12 -0
- package/i18n/en-US/components/content/blocks/video.ts +53 -0
- package/i18n/en-US/components/content/index.ts +5 -0
- package/i18n/en-US/components/index.ts +12 -0
- package/i18n/en-US/components/navigation/image.ts +4 -0
- package/i18n/en-US/components/navigation/index.ts +7 -0
- package/i18n/en-US/components/navigation/user_upload.ts +3 -0
- package/i18n/en-US/components/settings/clickable_icon.ts +9 -0
- package/i18n/en-US/components/settings/image.ts +1 -0
- package/i18n/en-US/components/settings/index.ts +13 -0
- package/i18n/en-US/components/settings/text_editor.ts +7 -0
- package/i18n/en-US/components/settings/user_upload.ts +11 -0
- package/i18n/en-US/components/settings/video.ts +13 -0
- package/i18n/en-US/components/utils/index.ts +5 -0
- package/i18n/en-US/components/utils/tiny_mce_wrapper.ts +7 -0
- package/i18n/en-US/index.ts +16 -0
- package/i18n/en-US/modules/index.ts +5 -0
- package/i18n/en-US/pages/glossary.ts +7 -0
- package/i18n/en-US/pages/index.ts +7 -0
- package/i18n/en-US/pages/user_upload.ts +3 -0
- package/i18n/en-US/shared/content_blocks.ts +20 -0
- package/i18n/en-US/shared/index.ts +11 -0
- package/i18n/en-US/shared/menu.ts +3 -0
- package/i18n/en-US/shared/permission.ts +15 -0
- package/i18n/en-US/shared/settings.ts +15 -0
- package/index.js +15 -0
- package/jest.config.js +15 -0
- package/models/SurveyResult.ts +8 -0
- package/models/SurveyTemplate.ts +8 -0
- package/models/UserFileAsset.ts +12 -0
- package/package.json +48 -0
- package/pages/glossary.vue +31 -0
- package/pages/userUpload.vue +204 -0
- package/plugin.js +299 -0
- package/test/Components/Content/Blocks/Accordion.spec.js +21 -0
- package/test/Components/Content/Blocks/ClickableIcons.spec.js +21 -0
- package/test/Components/Content/Blocks/Feedback.spec.js +31 -0
- package/test/Components/Content/Blocks/FeedbackTemplates/FeedbackQuestionLikert.spec.js +23 -0
- package/test/Components/Content/Blocks/FeedbackTemplates/FeedbackQuestionOpenResponse.spec.js +23 -0
- package/test/Components/Content/Blocks/FeedbackTemplates/FeedbackQuestionTrueFalse.spec.js +23 -0
- package/test/Components/Content/Blocks/Image.spec.js +34 -0
- package/test/Components/Content/Blocks/Math.spec.js +34 -0
- package/test/Components/Content/Blocks/Tab.spec.js +20 -0
- package/test/Components/Content/Blocks/Table.spec.js +21 -0
- package/test/Components/Content/Blocks/UserUpload.spec.js +25 -0
- package/test/Components/Content/Blocks/Video.spec.js +112 -0
- package/test/Components/Settings/AccordionSettings.spec.js +45 -0
- package/test/Components/Settings/ClickableIconsSettings.spec.js +20 -0
- package/test/Components/Settings/FeedbackSettings.spec.js +20 -0
- package/test/Components/Settings/ImageSettings.spec.js +20 -0
- package/test/Components/Settings/MathSettings.spec.js +20 -0
- package/test/Components/Settings/TabSettings.spec.js +45 -0
- package/test/Components/Settings/UserUploadSettings.spec.js +20 -0
- package/test/__mocks__/componentsMock.js +83 -0
- package/test/__mocks__/contentBlockMock.js +119 -0
- package/test/__mocks__/contentSettingsMock.js +54 -0
- package/test/__mocks__/fileMock.js +1 -0
- package/test/__mocks__/helpersMock.js +53 -0
- package/test/__mocks__/modelMock.js +82 -0
- package/test/__mocks__/styleMock.js +1 -0
- package/test/helpers/GlossaryHelper.spec.js +227 -0
- package/test/helpers/MathHelper.spec.js +128 -0
- package/test/mocks.js +140 -0
- package/tsconfig.json +15 -0
- package/utils/index.js +18 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import GlossaryTerm from "../helpers/GlossaryTerm";
|
|
2
|
+
import * as _ from 'lodash'
|
|
3
|
+
|
|
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
|
+
|
|
39
|
+
}
|
|
40
|
+
public static containsGlossaryTerms(content :string): boolean {
|
|
41
|
+
const regex = /<span.*?.class="glossary-word".*?>(.*?)<\/span>/g
|
|
42
|
+
|
|
43
|
+
return regex.exec(content) !== null
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public static getContentVerifiedGlossaryTerms(content :string, glossary :any) {
|
|
47
|
+
const regex = this.glossaryRegex
|
|
48
|
+
const currentGlossary = glossary
|
|
49
|
+
|
|
50
|
+
let match
|
|
51
|
+
const verifiedTerms: GlossaryTerm[] = []
|
|
52
|
+
while ((match = regex.exec(content)) !== null) {
|
|
53
|
+
// This is necessary to avoid infinite loops with zero-width matches
|
|
54
|
+
if (match.index === regex.lastIndex) {
|
|
55
|
+
regex.lastIndex++
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// The result can be accessed through the `m`-variable.
|
|
59
|
+
currentGlossary.forEach((term) => {
|
|
60
|
+
let addTerm = true
|
|
61
|
+
verifiedTerms.forEach((verifiedTerm)=>{
|
|
62
|
+
if(_.trim(_.toLower(verifiedTerm.term)) === _.trim(_.toLower(term.term))){
|
|
63
|
+
addTerm = false
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
if (_.trim(_.toLower(match[1]))=== _.trim(_.toLower(term.term)) && addTerm) {
|
|
67
|
+
verifiedTerms.push(new GlossaryTerm(term))
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return verifiedTerms
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public static getContentUnVerifiedGlossaryTerms(content :string, glossary :any) {
|
|
77
|
+
const regex = this.glossaryRegex
|
|
78
|
+
|
|
79
|
+
let match
|
|
80
|
+
const verifiedTerms = this.getContentVerifiedGlossaryTerms(content,glossary)
|
|
81
|
+
const unVerifiedTerms: GlossaryTerm[] = []
|
|
82
|
+
while ((match = regex.exec(content)) !== null) {
|
|
83
|
+
// This is necessary to avoid infinite loops with zero-width matches
|
|
84
|
+
if (match.index === regex.lastIndex) {
|
|
85
|
+
regex.lastIndex++
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let inGlossary = []
|
|
89
|
+
|
|
90
|
+
inGlossary = glossary.filter(item => {
|
|
91
|
+
if(_.trim(_.toLower(item.term ))=== _.trim(_.toLower(match[1]))){
|
|
92
|
+
return item
|
|
93
|
+
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
let inUnverifiedArray = false
|
|
98
|
+
unVerifiedTerms.forEach((unVerifiedTerm)=>{
|
|
99
|
+
if(_.trim(_.toLower(unVerifiedTerm.term)) === _.trim(_.toLower(match[1]))){
|
|
100
|
+
inUnverifiedArray = true
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
if ( inGlossary.length===0 && !inUnverifiedArray){
|
|
104
|
+
unVerifiedTerms.push(new GlossaryTerm( {term:_.trim(match[1]),definition:'n/a'}))
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return unVerifiedTerms
|
|
111
|
+
}
|
|
112
|
+
public static renderGlossaryWordsHtml(content :string, glossary :any) {
|
|
113
|
+
const regex = this.glossaryRegex
|
|
114
|
+
const currentGlossary = glossary
|
|
115
|
+
|
|
116
|
+
let match
|
|
117
|
+
while ((match = regex.exec(content)) !== null) {
|
|
118
|
+
// This is necessary to avoid infinite loops with zero-width matches
|
|
119
|
+
if (match.index === regex.lastIndex) {
|
|
120
|
+
regex.lastIndex++
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 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
|
+
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return content
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
class GlossaryTerm {
|
|
2
|
+
|
|
3
|
+
term!: string
|
|
4
|
+
|
|
5
|
+
definition!: string
|
|
6
|
+
|
|
7
|
+
alternate_forms: any = null
|
|
8
|
+
|
|
9
|
+
related_term: any =null
|
|
10
|
+
|
|
11
|
+
private required: string[] = ['term', 'definition']
|
|
12
|
+
|
|
13
|
+
constructor(params: any) {
|
|
14
|
+
this.required.forEach((field) => {
|
|
15
|
+
if (!params[field]) {
|
|
16
|
+
throw new Error('Glossary term ' + field + ' is required')
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
this.term = params.term
|
|
21
|
+
this.definition = params.definition
|
|
22
|
+
this.alternate_forms = params.alternate_forms || null
|
|
23
|
+
this.related_term = params.related_term || null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public static toString(){
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default GlossaryTerm
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import * as mathLiveObject from 'mathlive'
|
|
2
|
+
import * as _ from 'lodash'
|
|
3
|
+
const Mathml2latex = require('mathml-to-latex');
|
|
4
|
+
export default class MathHelper {
|
|
5
|
+
/**
|
|
6
|
+
* expose mathlive object
|
|
7
|
+
*/
|
|
8
|
+
public static Mathlive = mathLiveObject
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* containsMathML
|
|
12
|
+
* @param content
|
|
13
|
+
* checks is string contains mathml
|
|
14
|
+
* @return boolean
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
public static containsMathML(content: string): boolean {
|
|
18
|
+
const regex = /\<math.*?>.*?<\/math>/s
|
|
19
|
+
|
|
20
|
+
return regex.exec(content) !== null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* containsLatex
|
|
25
|
+
* @param content
|
|
26
|
+
* checks is string contains latex
|
|
27
|
+
* @return boolean
|
|
28
|
+
*
|
|
29
|
+
*/
|
|
30
|
+
public static containsLatex(content: string): boolean {
|
|
31
|
+
const regex = /\$\$.*?\$\$/g
|
|
32
|
+
|
|
33
|
+
return regex.exec(content) !== null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* containSREnhancedLatex
|
|
38
|
+
* @param content
|
|
39
|
+
* checks is string contains latex
|
|
40
|
+
* @return boolean
|
|
41
|
+
*
|
|
42
|
+
*/
|
|
43
|
+
public static containSREnhancedLatex(content: string): boolean {
|
|
44
|
+
const regex = /\*\*.*?\*\*/g
|
|
45
|
+
|
|
46
|
+
return regex.exec(content) !== null
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* convertMathMLToLatex
|
|
51
|
+
* @param content
|
|
52
|
+
* attempts to find mml in content string and replace it with latex equivalent
|
|
53
|
+
* @return string
|
|
54
|
+
*/
|
|
55
|
+
public static convertContentMathMLtoLatex(content: string): string {
|
|
56
|
+
const regex = /\<math.*?>.*?<\/math>/g
|
|
57
|
+
const matches = content.match(regex)
|
|
58
|
+
if (matches) {
|
|
59
|
+
matches.forEach((match) => {
|
|
60
|
+
content = content.replace(
|
|
61
|
+
match,
|
|
62
|
+
`$$$` + Mathml2latex.convert(match) + '$$$'
|
|
63
|
+
)
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return content
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* wrapMathContentForTinyMCE
|
|
72
|
+
* @param content
|
|
73
|
+
* wrap content in span with math class for tinymce
|
|
74
|
+
* @return string
|
|
75
|
+
*/
|
|
76
|
+
public static wrapMathContentForTinyMCE(content: string): string {
|
|
77
|
+
const regex = /(\*\*|\$\$)(.*?)\1/g
|
|
78
|
+
const matches = content.match(regex)
|
|
79
|
+
if (matches) {
|
|
80
|
+
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
|
+
)
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return content
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* check Math Content Wrapped For TMCE
|
|
106
|
+
* @param content
|
|
107
|
+
* checks all content
|
|
108
|
+
* @return string
|
|
109
|
+
*/
|
|
110
|
+
public static checkMathContentWrappedForTMCE(content: string): boolean {
|
|
111
|
+
const regex =
|
|
112
|
+
/\<span class ='windward-math-content'>(\*\*|\$\$)(.*?)\1<\/span>/g
|
|
113
|
+
|
|
114
|
+
return regex.exec(content) !== null
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* convertContentLatexToHtml
|
|
119
|
+
* @param content
|
|
120
|
+
* attempts to find latex in content string and replace it with latex equivalent
|
|
121
|
+
* @return string
|
|
122
|
+
*
|
|
123
|
+
*/
|
|
124
|
+
public static convertContentLatexToHtml(content: string): string {
|
|
125
|
+
const regex = /\$\$.*?\$\$/g
|
|
126
|
+
|
|
127
|
+
const matches = content.match(regex)
|
|
128
|
+
if (matches) {
|
|
129
|
+
matches.forEach((match) => {
|
|
130
|
+
content = content.replace(
|
|
131
|
+
match,
|
|
132
|
+
'<span tabindex="0" aria-label="' +
|
|
133
|
+
// @ts-ignore
|
|
134
|
+
mathLiveObject.convertLatexToSpeakableText(match) +
|
|
135
|
+
'"> <span tabindex="-1" >' +
|
|
136
|
+
mathLiveObject.convertLatexToMarkup(_.unescape(match)) +
|
|
137
|
+
'</span></span>'
|
|
138
|
+
)
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return content
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* convertContentSREnhancedLatexToHtml
|
|
147
|
+
* @param content
|
|
148
|
+
* attempts to find latex in content string and place custom SR text on renderer math div
|
|
149
|
+
* @return string
|
|
150
|
+
*
|
|
151
|
+
*/
|
|
152
|
+
public static convertContentSREnhancedLatexToHtml(content: string): string {
|
|
153
|
+
const regex = /\*\*.*?\*\*/g
|
|
154
|
+
|
|
155
|
+
const matches = content.match(regex)
|
|
156
|
+
if (matches) {
|
|
157
|
+
matches.forEach((match: any) => {
|
|
158
|
+
const mathObject = JSON.parse(match.replace(/\*\*/g, ''))
|
|
159
|
+
content = content.replace(
|
|
160
|
+
match,
|
|
161
|
+
'<span tabindex="0" aria-label="' +
|
|
162
|
+
mathObject.sr_text +
|
|
163
|
+
'"> <span tabindex="-1" >' +
|
|
164
|
+
mathLiveObject.convertLatexToMarkup(
|
|
165
|
+
'$$' + _.unescape(mathObject.latex) + '$$'
|
|
166
|
+
) +
|
|
167
|
+
'</span></span>'
|
|
168
|
+
)
|
|
169
|
+
})
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return content
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* convertMathContentToHtml
|
|
177
|
+
* @param content
|
|
178
|
+
* attempts to find any math format in content string and convert it to HTML
|
|
179
|
+
* @return string
|
|
180
|
+
*
|
|
181
|
+
*/
|
|
182
|
+
public static convertMathContentToHtml(content: string): string {
|
|
183
|
+
return MathHelper.convertContentSREnhancedLatexToHtml(
|
|
184
|
+
MathHelper.convertContentLatexToHtml(
|
|
185
|
+
MathHelper.convertContentMathMLtoLatex(content)
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* convertMathMLToLatex
|
|
192
|
+
* @param content
|
|
193
|
+
* converts mml to latex
|
|
194
|
+
*
|
|
195
|
+
*/
|
|
196
|
+
public static convertMathMLToLatex(content: string): string {
|
|
197
|
+
return Mathml2latex.convert(content)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* parseSREnhancedLatexString
|
|
202
|
+
* @param content
|
|
203
|
+
* parse SR enhanced latex string and return Object
|
|
204
|
+
* @return Object
|
|
205
|
+
*
|
|
206
|
+
*/
|
|
207
|
+
public static parseSREnhancedLatexString(content: string): object {
|
|
208
|
+
const regex = /\*\*.*?\*\*/g
|
|
209
|
+
|
|
210
|
+
const matches = content.match(regex)
|
|
211
|
+
if (matches) {
|
|
212
|
+
const mathobject = JSON.parse(matches[0].replace(/\*\*/g, ''))
|
|
213
|
+
return mathobject
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return {}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* parseSRLatex
|
|
221
|
+
* @param content
|
|
222
|
+
* parse latex expression from string
|
|
223
|
+
* @return any
|
|
224
|
+
*
|
|
225
|
+
*/
|
|
226
|
+
public static parseLatexString(content: string): string {
|
|
227
|
+
const regex = /\$\$.*?\$\$/g
|
|
228
|
+
|
|
229
|
+
const matches = content.match(regex)
|
|
230
|
+
if (matches) {
|
|
231
|
+
return matches[0].replace(/\$\$/g, '')
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return ''
|
|
235
|
+
}
|
|
236
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
const MathEditorPlugin = function (editor: any) {
|
|
2
|
+
let formula: any
|
|
3
|
+
// ----- Events ----- //
|
|
4
|
+
|
|
5
|
+
editor.addCommand('equation-window', function (data: any) {
|
|
6
|
+
return editor.windowManager.openUrl({
|
|
7
|
+
url: '/plugins/tinymce/math',
|
|
8
|
+
title: 'Equation editor',
|
|
9
|
+
width: 800,
|
|
10
|
+
height: 600,
|
|
11
|
+
buttons: [
|
|
12
|
+
{
|
|
13
|
+
type: 'cancel',
|
|
14
|
+
text: 'cancel',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
type: 'custom',
|
|
18
|
+
text: 'submit',
|
|
19
|
+
primary: true,
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
onAction: () => {
|
|
23
|
+
if (data.currentTarget) {
|
|
24
|
+
editor.selection.select(data.currentTarget);
|
|
25
|
+
}
|
|
26
|
+
editor.selection.setContent(
|
|
27
|
+
` <span class='windward-math-content'>` +
|
|
28
|
+
formula +
|
|
29
|
+
`</span>`
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
editor.windowManager.close()
|
|
33
|
+
setOnClickEquationContent(editor)
|
|
34
|
+
},
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
36
|
+
onMessage: (instance: any, message: any) => {
|
|
37
|
+
switch (message.mceAction) {
|
|
38
|
+
case 'equation-insert':
|
|
39
|
+
formula = message.content
|
|
40
|
+
break
|
|
41
|
+
case 'math-plugin-mounted':
|
|
42
|
+
window.parent.postMessage({ latex: data.latex }, '*')
|
|
43
|
+
break
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
editor.ui.registry.addButton('MathEditor', {
|
|
50
|
+
text: 'Math Editor',
|
|
51
|
+
icon: 'gamma',
|
|
52
|
+
onAction: () => {
|
|
53
|
+
editor.execCommand('equation-window', true)
|
|
54
|
+
},
|
|
55
|
+
})
|
|
56
|
+
editor.on('init', function () {
|
|
57
|
+
setOnClickEquationContent(editor)
|
|
58
|
+
})
|
|
59
|
+
function setOnClickEquationContent(editor: any) {
|
|
60
|
+
const tinymceDoc = editor.getDoc()
|
|
61
|
+
const mqSpan = tinymceDoc.getElementsByClassName(
|
|
62
|
+
'windward-math-content'
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
// Add onclick listener to all equation content
|
|
66
|
+
for (const equationContent of mqSpan) {
|
|
67
|
+
equationContent.contentEditable = 'true'
|
|
68
|
+
if (equationContent.onclick) {
|
|
69
|
+
continue
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
equationContent.onclick = (event: any) => {
|
|
73
|
+
event.stopPropagation()
|
|
74
|
+
editor.execCommand('equation-window', {
|
|
75
|
+
latex: event.target.innerText,
|
|
76
|
+
currentTarget: event.target,
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export { MathEditorPlugin }
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
feedback: 'Feedback',
|
|
3
|
+
course: 'Course',
|
|
4
|
+
module: 'Module',
|
|
5
|
+
preset: 'Presets',
|
|
6
|
+
likert: 'Add standart likert questions',
|
|
7
|
+
contact_mindedge: 'Contact Mindedge question',
|
|
8
|
+
open_response: 'Open Response',
|
|
9
|
+
response_prompt: 'Provide course feedback below',
|
|
10
|
+
no_response_entered: 'No response entered',
|
|
11
|
+
required: 'You must select an option to submit',
|
|
12
|
+
save: 'Send Feedback',
|
|
13
|
+
reset: 'Reset',
|
|
14
|
+
fill_out: 'Please fill out all fields',
|
|
15
|
+
yes: 'Yes',
|
|
16
|
+
no: 'No',
|
|
17
|
+
description:
|
|
18
|
+
"This feedback form is optional. However, we would love to hear your thoughts on this learning experience. We take your feedback very seriously, reading and reviewing each entry every day, and we are always trying to improve. Thank you for any comments you'd like to share.",
|
|
19
|
+
sent_feedback:
|
|
20
|
+
'Thank you for submitting feedback. Your input is very important and valuable to us. If you suggested improvements in your comments, we will consider those suggestions seriously. Course quality and usability are our highest priorities.',
|
|
21
|
+
scale: {
|
|
22
|
+
strongly_agree: 'Stronly Agree',
|
|
23
|
+
agree: 'Agree',
|
|
24
|
+
neutral: 'Neutral',
|
|
25
|
+
disagree: 'Disagree',
|
|
26
|
+
strongly_disagree: 'Strong Disagree',
|
|
27
|
+
},
|
|
28
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import user_upload from './user_upload'
|
|
2
|
+
import image from './image'
|
|
3
|
+
import video from './video'
|
|
4
|
+
import table from './table'
|
|
5
|
+
import tab from './tab'
|
|
6
|
+
import feedback from './feedback'
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
user_upload,
|
|
10
|
+
image,
|
|
11
|
+
video,
|
|
12
|
+
table,
|
|
13
|
+
tab,
|
|
14
|
+
feedback,
|
|
15
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
user_uploads: 'User Uploads',
|
|
3
|
+
dialog_view: 'View Uploads',
|
|
4
|
+
instructions: 'Instructions:',
|
|
5
|
+
instructions_none: 'None',
|
|
6
|
+
instructions_title: 'Student instructions:',
|
|
7
|
+
must_save: 'You must save this block before users can make uploads',
|
|
8
|
+
uploaded: 'Uploaded',
|
|
9
|
+
name: 'Name',
|
|
10
|
+
size: 'Size',
|
|
11
|
+
download: 'Download',
|
|
12
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
tabs: {
|
|
3
|
+
video: 'Video',
|
|
4
|
+
poster: 'Poster',
|
|
5
|
+
preroll: 'Preroll',
|
|
6
|
+
postroll: 'Postroll',
|
|
7
|
+
},
|
|
8
|
+
forms: {
|
|
9
|
+
unsaved: 'You have unsaved changes. Would you like to save them?',
|
|
10
|
+
cancel: 'Cancel',
|
|
11
|
+
confirm: 'Confirm',
|
|
12
|
+
},
|
|
13
|
+
video: {
|
|
14
|
+
title: 'Video File',
|
|
15
|
+
configure_blurb:
|
|
16
|
+
'Upload a video file (.mp4 or .webm), pick one from the file manager, or add via the public URL',
|
|
17
|
+
not_configured_title: 'Video not configured',
|
|
18
|
+
edit_prompt: 'Edit this block to get started',
|
|
19
|
+
autoplay: 'Autoplay on load',
|
|
20
|
+
controls: 'Show Controls',
|
|
21
|
+
loop: 'Loop video',
|
|
22
|
+
muted: 'Start muted',
|
|
23
|
+
disablepictureinpicture: 'Disable picture-in-picture button',
|
|
24
|
+
poster: 'Video poster',
|
|
25
|
+
playback_rates: 'Playback Rates',
|
|
26
|
+
rewind: 'Allow 10 Second Rewind',
|
|
27
|
+
playsinline: 'Disable fullscreen and force inline viewing',
|
|
28
|
+
captionsmenu: 'Show the interactive captions menu',
|
|
29
|
+
playlistmenu:
|
|
30
|
+
'Show playlist menu (Requires more than 1 video selected to show)',
|
|
31
|
+
playlistautoadvance: 'Auto-advance the playlist on media end',
|
|
32
|
+
},
|
|
33
|
+
caption: {
|
|
34
|
+
title: 'Captions File',
|
|
35
|
+
configure_blurb:
|
|
36
|
+
'Upload a captions file (.vtt or .xml), pick one from the file manager, or add via the public URL',
|
|
37
|
+
},
|
|
38
|
+
poster: {
|
|
39
|
+
title: 'Poster Image File',
|
|
40
|
+
configure_blurb:
|
|
41
|
+
'Upload a poster file (.png or .jpg), pick one from the file manager, or add via the public URL. This is the image that appears before the video is played.',
|
|
42
|
+
},
|
|
43
|
+
preroll: {
|
|
44
|
+
title: 'Preroll Video File',
|
|
45
|
+
configure_blurb:
|
|
46
|
+
'Upload a preroll video file (.mp4 or .webm), pick one from the file manager, or add via the public URL',
|
|
47
|
+
},
|
|
48
|
+
postroll: {
|
|
49
|
+
title: 'Postroll Video File',
|
|
50
|
+
configure_blurb:
|
|
51
|
+
'Upload a postroll video file (.mp4 or .webm), pick one from the file manager, or add via the public URL',
|
|
52
|
+
},
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default {}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import image from './image'
|
|
2
|
+
import user_upload from './user_upload'
|
|
3
|
+
import text_editor from './text_editor'
|
|
4
|
+
import video from './video'
|
|
5
|
+
import clickable_icon from './clickable_icon'
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
image,
|
|
9
|
+
user_upload,
|
|
10
|
+
text_editor,
|
|
11
|
+
video,
|
|
12
|
+
clickable_icon
|
|
13
|
+
}
|