diff --git a/core/models/submission_type.py b/core/models/submission_type.py index e33a17200ac1c88e11d99cacc6c5db927632020e..9cdb7982b33aaa9b4bad25ebc58a1b3458b6f214 100644 --- a/core/models/submission_type.py +++ b/core/models/submission_type.py @@ -37,6 +37,7 @@ class SubmissionType(models.Model): HASKELL = 'haskell' TEXT = 'plaintext' PYTHON = 'python' + MARKDOWN = 'markdown' LANGUAGE_CHOICES = ( (C, 'C syntax highlighting'), @@ -44,6 +45,7 @@ class SubmissionType(models.Model): (MIPS, 'Mips syntax highlighting'), (HASKELL, 'Haskell syntax highlighting'), (PYTHON, 'Python syntax highlighting'), + (MARKDOWN, 'Markdown syntax highlighting with asciimath rendering'), (TEXT, 'No syntax highlighting'), ) diff --git a/frontend/package.json b/frontend/package.json index 0f0fdcd0456f92b7607c3325cf25ec27ca39d88d..30f0dd1a23065f0eb283d668cb6ca3d50b99c9f7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -35,20 +35,20 @@ "@types/mocha": "^5.2.5", "@types/nightwatch": "^0.9.8", "@types/sinon": "^7.0.2", + "@typescript-eslint/eslint-plugin": "^2.3.2", + "@typescript-eslint/parser": "^2.3.2", "@vue/cli-plugin-eslint": "^3.11.0", "@vue/cli-plugin-typescript": "^3.11.0", "@vue/cli-plugin-unit-mocha": "^3.11.0", "@vue/cli-service": "^3.11.0", - "@vue/test-utils": "^1.0.0-beta.29", "@vue/eslint-config-typescript": "^4.0.0", - "@typescript-eslint/parser": "^2.3.2", - "@typescript-eslint/eslint-plugin": "^2.3.2", + "@vue/test-utils": "^1.0.0-beta.29", "chai": "^4.2.0", + "eslint": "^5.16.0", + "eslint-plugin-vue": "^5.0.0", "mocha": "^5.2.0", "mock-local-storage": "^1.1.8", "sinon": "^7.2.2", - "eslint": "^5.16.0", - "eslint-plugin-vue": "^5.0.0", "typescript": "^3.4.3", "vue-template-compiler": "^2.5.16", "webpack": "^4.41.0" diff --git a/frontend/public/index.html b/frontend/public/index.html index 8008677d0a81e743b9b0f3cd1f09a007f2693ed5..f2f04e976c9f85633d99ba0a230dc3f5217275ca 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -6,11 +6,14 @@ <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title>Grady</title> - <script id="MathJax-script" async src="https://grady.informatik.uni-goettingen.de/static/mathjax/es5/tex-mml-chtml.js"></script> <script> MathJax = { + loader: {load: ['input/asciimath', 'output/chtml', 'input/tex']}, + asciimath: { + delimiters: [['$', '$']] + }, options: { - skipHtmlTags: [ // HTML tags that won't be searched for math + skipHtmlTags: [// HTML tags that won't be searched for math 'script', 'noscript', 'style', 'textarea', 'pre', 'code', 'annotation', 'annotation-xml' ], @@ -22,6 +25,7 @@ } }; </script> + <script id="MathJax-script" async src="https://grady.informatik.uni-goettingen.de/static/mathjax/es5/startup.js"></script> </head> <body class="tex2jax_ignore"> <noscript> diff --git a/frontend/src/components/submission_notes/SubmissionCorrection.vue b/frontend/src/components/submission_notes/SubmissionCorrection.vue index ac298e839aaf6ba10b6f2e0a686e42840a8e8bb1..c02136558d8087e17a4d16092eb3a2fb82082cd2 100644 --- a/frontend/src/components/submission_notes/SubmissionCorrection.vue +++ b/frontend/src/components/submission_notes/SubmissionCorrection.vue @@ -79,7 +79,7 @@ import FeedbackLabel from '@/components/feedback_labels/FeedbackLabel.vue' import { FeedbackLabels as Labels } from '@/store/modules/feedback-labels' import LabelSelector from '@/components/feedback_labels/LabelSelector.vue' import SubmissionLine from '@/components/submission_notes/base/SubmissionLine' -import { SubmissionNotes } from '@/store/modules/submission-notes' +import { SubmissionNotes, subNotesEventBus } from '@/store/modules/submission-notes' import { Authentication } from '@/store/modules/authentication' import { actions } from '@/store/actions' import { fetchFeedback } from '@/api' @@ -148,6 +148,11 @@ export default { }, submissionWithoutAssignment: function () { this.init() + }, + submission: function (oldVal, newVal) { + if (JSON.stringify(oldVal) !== JSON.stringify(newVal)) { + subNotesEventBus.$emit('submissionChanged') + } } }, created () { diff --git a/frontend/src/components/submission_notes/base/BaseAnnotatedSubmission.vue b/frontend/src/components/submission_notes/base/BaseAnnotatedSubmission.vue index 9cb5de2117a8be455f3219e1631686de3a7ba2ae..d6c48276082dd97a8585e5293778329c92c0a224 100644 --- a/frontend/src/components/submission_notes/base/BaseAnnotatedSubmission.vue +++ b/frontend/src/components/submission_notes/base/BaseAnnotatedSubmission.vue @@ -1,7 +1,10 @@ <template> <div> <slot name="header" /> - <table class="submission-table elevation-1"> + <table + id="submission-table" + class="latex submission-table elevation-1" + > <slot name="table-content" /> </table> <slot name="labels" /> diff --git a/frontend/src/components/submission_notes/base/SubmissionLine.vue b/frontend/src/components/submission_notes/base/SubmissionLine.vue index 7cf2158927fee5fc9e951311ca757b2d5ddad692..d2170684cb8e7306765cd7c96ab144dabde08686 100644 --- a/frontend/src/components/submission_notes/base/SubmissionLine.vue +++ b/frontend/src/components/submission_notes/base/SubmissionLine.vue @@ -16,13 +16,14 @@ </td> <td class="code-cell-content pl-2"> <!-- eslint-disable-next-line --> - <span class="code-line" v-html="code"/> + <span class="code-line" :key="key" v-html="code"/> <slot /> </td> </div> </template> <script> +import { subNotesEventBus } from '../../../store/modules/submission-notes' export default { name: 'SubmissionLine', props: { @@ -43,11 +44,21 @@ export default { default: false, }, }, + data () { + return { + key: 0 + } + }, computed: { backgroundColor() { return this.hint ? 'background-color: #F44336;' : 'background-color: transparent;' } }, + created () { + subNotesEventBus.$on('resetSubmission', () => { + this.key++ + }) + }, methods: { toggleEditor () { this.$emit('toggleEditor') diff --git a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionTopToolbar.vue b/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionTopToolbar.vue index 204b45ce1272247eb2afca8a3b7c36eb1ffea2d0..b1ad9618b0b7f6f5c0b3f920018886cf65eae11d 100644 --- a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionTopToolbar.vue +++ b/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionTopToolbar.vue @@ -14,6 +14,20 @@ </v-dialog> <span class="title">Submission of {{ ofStudent }}</span> <toggle-feedback-visibility-button /> + <div v-if="isMarkdown"> + <v-btn + v-if="!mathIsRendered" + @click="renderAsciiMath" + > + Render Math + </v-btn> + <v-btn + v-else + @click="resetSubmission" + > + Reset Math + </v-btn> + </div> <v-spacer /> <v-tooltip v-if="sourceCodeAvailable" @@ -45,9 +59,12 @@ import CorrectionHelpCard from '@/components/submission_notes/CorrectionHelpCard' import { mapState } from 'vuex' import ToggleFeedbackVisibilityButton from '@/components/submission_notes/toolbars/ToggleFeedbackVisibilityButton' -import { SubmissionNotes } from '@/store/modules/submission-notes' +import { SubmissionNotes, subNotesEventBus } from '@/store/modules/submission-notes' import {fetchSubmissionSourceCode} from '@/api.ts' import {saveAs} from 'file-saver' +import store from '../../../store/store' +import { SubmissionType } from '../../../models' +import Vue from 'vue' export default { name: 'AnnotatedSubmissionTopToolbar', @@ -63,15 +80,44 @@ export default { data () { return { helpDialog: false, - copyMessage: 'Copy to clipboard' + copyMessage: 'Copy to clipboard', + mathIsRendered: false } }, computed: { sourceCodeAvailable () { return SubmissionNotes.state.submission.sourceCodeAvailable + }, + isMarkdown () { + const typePk = SubmissionNotes.state.submission.type + const type = store.state.submissionTypes[typePk] + return type.programmingLanguage === SubmissionType.ProgrammingLanguageEnum.Markdown + } + }, + created () { + subNotesEventBus.$on('submissionChanged', () => { + if (this.mathIsRendered) { + this.$nextTick(() => { + this.renderAsciiMath() + }) + } + }) + }, + mounted () { + const lang = SubmissionNotes.submissionType.programmingLanguage + if (lang === SubmissionType.ProgrammingLanguageEnum.Markdown) { + this.renderAsciiMath() } }, methods: { + renderAsciiMath () { + window.MathJax.typeset() + this.mathIsRendered = true + }, + resetSubmission () { + this.mathIsRendered = false + subNotesEventBus.$emit('resetSubmission') + }, async downloadSourceCode () { const data = await fetchSubmissionSourceCode(SubmissionNotes.state.submission.pk) saveAs(new Blob([data.sourceCode], {type: 'application/json'}), 'notebook.ipynb') diff --git a/frontend/src/components/submission_type/SubmissionType.vue b/frontend/src/components/submission_type/SubmissionType.vue index 138d1ae9a4da1e8a15a0961a7e1b4e4467e3b90c..91ad3ade006d5c5c5f96bab3dce9eaeaf4330f17 100644 --- a/frontend/src/components/submission_type/SubmissionType.vue +++ b/frontend/src/components/submission_type/SubmissionType.vue @@ -137,10 +137,13 @@ export default class SubmissionType extends Vue { } @Watch('description') - onDescriptionChanged(val: string) { - this.$nextTick().then(() => { - window.MathJax.typeset() - }) + onDescriptionChanged(newVal: string, oldVal: string) { + console.log(`desc rerender old ${oldVal} new ${newVal}`) + if (oldVal !== newVal) { + this.$nextTick().then(() => { + window.MathJax.typeset() + }) + } } mounted () { diff --git a/frontend/src/models.ts b/frontend/src/models.ts index 1982c228151576f7130b6f0912fb87e7b612a685..6b7b44eaa2277832529bc025bd9103bf269b3fa1 100644 --- a/frontend/src/models.ts +++ b/frontend/src/models.ts @@ -640,8 +640,9 @@ export namespace SubmissionType { * @enum {string} */ export enum ProgrammingLanguageEnum { - C = 'c' as any, - Java = 'java' as any + C = 'c', + Java = 'java', + Markdown = 'markdown' } } diff --git a/frontend/src/store/modules/submission-notes.ts b/frontend/src/store/modules/submission-notes.ts index 0870424bdb3c0c4f4ea42eb8122de27d92995837..78bc156bf64726cbd8b48816f00fb3211bf06f96 100644 --- a/frontend/src/store/modules/submission-notes.ts +++ b/frontend/src/store/modules/submission-notes.ts @@ -1,13 +1,15 @@ import Vue from 'vue' import * as hljs from 'highlight.js' import * as api from '@/api' -import { Feedback, FeedbackComment, SubmissionNoType, CreateUpdateFeedback } from '@/models' +import { Feedback, FeedbackComment, SubmissionNoType, CreateUpdateFeedback, SubmissionType } from '@/models' import { RootState } from '@/store/store' import { getStoreBuilder, BareActionContext } from 'vuex-typex' import { syntaxPostProcess } from '@/util/helpers' import { AxiosResponse } from 'axios' import { Assignments } from './assignments' +export const subNotesEventBus = new Vue() + export interface SubmissionNotesState { submission: SubmissionNoType ui: { @@ -70,7 +72,11 @@ const submissionGetter = mb.read(function submission(state, getters) { const language = getters.submissionType ? getters.submissionType.programmingLanguage : 'c' - const highlighted = hljs.highlight(language, state.submission.text || '', true).value + let highlighted = state.submission.text || '' + if (language !== SubmissionType.ProgrammingLanguageEnum.Markdown) { + highlighted = hljs.highlight(language, highlighted, true).value + } + // const highlighted = state.submission.text || '' const postProcessed = syntaxPostProcess(highlighted) const splitted = postProcessed.split('\n').reduce((acc: { [k: number]: string }, cur, index) => { acc[index + 1] = cur