Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quantum-kittens
GitHub Repository: quantum-kittens/platypus
Path: blob/main/patches/@mathigon+studio+0.1.16.patch
3338 views
1
diff --git a/node_modules/@mathigon/studio/build/markdown/mathjax.js b/node_modules/@mathigon/studio/build/markdown/mathjax.js
2
index 93eae4d..e8da114 100644
3
--- a/node_modules/@mathigon/studio/build/markdown/mathjax.js
4
+++ b/node_modules/@mathigon/studio/build/markdown/mathjax.js
5
@@ -7,7 +7,7 @@
6
const path = require('path');
7
const entities = require('html-entities');
8
const mathjax = require('mathjax');
9
-const {readFile, writeFile, warning} = require('../utilities');
10
+const {CONFIG, readFile, writeFile, warning} = require('../utilities');
11
12
const cacheFile = path.join(process.env.HOME, '/.mathjax-cache');
13
const mathJaxStore = JSON.parse(readFile(cacheFile, '{}'));
14
@@ -17,9 +17,15 @@ const placeholders = {};
15
let placeholderCount = 0;
16
let promise = undefined;
17
18
+const tex2html = CONFIG.parser?.tex2html || false;
19
+
20
+function getId (code, isInline) {
21
+ return entities.decode(code) + (isInline || false) + (tex2html ? 'html' : '');
22
+}
23
+
24
25
module.exports.makeTexPlaceholder = function(code, isInline = false) {
26
- const id = entities.decode(code) + (isInline || false);
27
+ const id = getId(code, isInline);
28
if (id in mathJaxStore) return mathJaxStore[id];
29
30
const placeholder = `XEQUATIONX${placeholderCount++}XEQUATIONX`;
31
@@ -28,7 +34,7 @@ module.exports.makeTexPlaceholder = function(code, isInline = false) {
32
};
33
34
async function texToSvg(code, isInline) {
35
- const id = entities.decode(code) + (isInline || false);
36
+ const id = getId(code, isInline);
37
if (mathJaxStore[id]) return mathJaxStore[id];
38
39
if (!promise) {
40
@@ -56,10 +62,67 @@ async function texToSvg(code, isInline) {
41
return mathJaxStore[id] = output;
42
}
43
44
+async function texToHtml(code, isInline) {
45
+ const id = getId(code, isInline);
46
+ if (mathJaxStore[id]) return mathJaxStore[id];
47
+
48
+ if (!promise) {
49
+ promise = mathjax.init({
50
+ options: {
51
+ renderActions: {
52
+ // add the TEX string into the DOM node
53
+ addInputText: [200,
54
+ (doc) => {
55
+ for (const math of doc.math) MathJax.config.addInputText(math, doc);
56
+ },
57
+ (math, doc) => MathJax.config.addInputText(math, doc)
58
+ ]
59
+ }
60
+ },
61
+ addInputText(math, doc) {
62
+ const adaptor = doc.adaptor;
63
+ const text = adaptor.node('mjx-input-tex', {
64
+ 'aria-hidden': true,
65
+ 'style': 'display:none;'
66
+ }, [
67
+ adaptor.text(math.start.delim + math.math + math.end.delim)
68
+ ]);
69
+ adaptor.append(math.typesetRoot, text);
70
+ },
71
+ loader: {load: ['input/tex-full', 'output/chtml']},
72
+ // https://docs.mathjax.org/en/latest/options/output/chtml.html#the-configuration-block
73
+ chtml: {
74
+ adaptiveCSS: false,
75
+ fontURL: 'https://cdn.jsdelivr.net/npm/[email protected]/es5/output/chtml/fonts/woff-v2'
76
+ }
77
+ });
78
+ }
79
+
80
+ let output = '';
81
+
82
+ try {
83
+ const MathJax = await promise;
84
+ const adaptor = MathJax.startup.adaptor;
85
+
86
+ const html = await MathJax.tex2chtml(code, {display: !isInline});
87
+ output = adaptor.outerHTML(html);
88
+ } catch (e) {
89
+ warning(` MathJax Error: ${e.message} at "${code}"`);
90
+ }
91
+
92
+ storeChanged = true;
93
+ return mathJaxStore[id] = output;
94
+}
95
+
96
module.exports.fillTexPlaceholders = async function(doc) {
97
const matches = doc.match(/XEQUATIONX[0-9]+XEQUATIONX/g) || [];
98
for (const placeholder of matches) {
99
- const code = await texToSvg(...placeholders[placeholder]);
100
+ let code = '';
101
+ if (tex2html) {
102
+ code = await texToHtml(...placeholders[placeholder]);
103
+ } else {
104
+ code = await texToSvg(...placeholders[placeholder]);
105
+ }
106
doc = doc.replace(placeholder, code);
107
}
108
return doc;
109
diff --git a/node_modules/@mathigon/studio/build/markdown/parser.js b/node_modules/@mathigon/studio/build/markdown/parser.js
110
index 14a4bcf..e143648 100644
111
--- a/node_modules/@mathigon/studio/build/markdown/parser.js
112
+++ b/node_modules/@mathigon/studio/build/markdown/parser.js
113
@@ -111,6 +111,7 @@ async function parseStep(content, index, directory, courseId, locale = 'en') {
114
for (const $md of $$(body, '.md')) {
115
$md.classList.remove('md');
116
$md.innerHTML = marked($md.innerHTML, {renderer}).replace(/^<p>|<\/p>$/g, '');
117
+ $md.innerHTML = await fillTexPlaceholders($md.innerHTML);
118
}
119
120
// Add the [parent] attribute as class to all elements parents
121
diff --git a/node_modules/@mathigon/studio/server/accounts.ts b/node_modules/@mathigon/studio/server/accounts.ts
122
index a28f2db..d1eb2e7 100755
123
--- a/node_modules/@mathigon/studio/server/accounts.ts
124
+++ b/node_modules/@mathigon/studio/server/accounts.ts
125
@@ -266,110 +266,110 @@ function redirect(req: express.Request, res: express.Response, data: ResponseDat
126
127
export default function setupAuthEndpoints(app: MathigonStudioApp) {
128
129
- app.get('/login', (req, res) => {
130
- if (req.user) return res.redirect('/dashboard');
131
- res.render('accounts/login');
132
- });
133
+ // app.get('/login', (req, res) => {
134
+ // if (req.user) return res.redirect('/dashboard');
135
+ // res.render('accounts/login');
136
+ // });
137
138
- app.post('/login', async (req, res) => {
139
- const response = await login(req);
140
- if (response.user) req.session.auth!.user = response.user.id;
141
- redirect(req, res, response, '/dashboard', '/login');
142
- });
143
+ // app.post('/login', async (req, res) => {
144
+ // const response = await login(req);
145
+ // if (response.user) req.session.auth!.user = response.user.id;
146
+ // redirect(req, res, response, '/dashboard', '/login');
147
+ // });
148
149
app.get('/logout', (req, res) => {
150
delete req.session.auth!.user;
151
req.session.save(() => res.redirect('back'));
152
});
153
154
- app.get('/signup', (req, res) => {
155
- if (req.user) return res.redirect('/dashboard');
156
- res.render('accounts/signup', {countries: COUNTRY_LIST});
157
- });
158
-
159
- app.post('/signup', async (req, res) => {
160
- const response = await signup(req);
161
- if (response.user) req.session.auth!.user = response.user.id;
162
- redirect(req, res, response, '/dashboard', '/signup');
163
- });
164
-
165
- app.get('/confirm/:id/:token', async (req, res) => {
166
- const response = await confirmEmail(req);
167
- redirect(req, res, response, '/dashboard', '/login');
168
- });
169
-
170
- app.get('/forgot', (req, res) => {
171
- if (req.user) return res.redirect('/dashboard');
172
- res.render('accounts/forgot');
173
- });
174
-
175
- app.post('/forgot', async (req, res) => {
176
- const response = await requestPasswordResetEmail(req);
177
- redirect(req, res, response, '/login', '/forgot');
178
- });
179
-
180
- app.get('/reset/:token', async (req, res) => {
181
- const response = await checkResetToken(req);
182
- if (response.error) return redirect(req, res, response, '/forgot');
183
- res.render('accounts/reset');
184
- });
185
-
186
- app.post('/reset/:token', async (req, res) => {
187
- const response = await resetPassword(req);
188
- redirect(req, res, response, '/login', '/reset');
189
- });
190
-
191
- app.get('/profile', (req, res) => {
192
- if (!req.user) return res.redirect('/login');
193
- res.render('accounts/profile', {countries: COUNTRY_LIST});
194
- });
195
-
196
- app.post('/profile/details', async (req, res) => {
197
- const response = await updateProfile(req);
198
- redirect(req, res, response, '/profile');
199
- });
200
-
201
- app.post('/profile/password', async (req, res) => {
202
- const response = await updatePassword(req);
203
- redirect(req, res, response, '/profile');
204
- });
205
-
206
- app.get('/profile/delete', async (req, res) => {
207
- const response = await deleteAccount(req, true);
208
- redirect(req, res, response, '/profile', '/profile');
209
- });
210
-
211
- app.get('/profile/undelete', async (req, res) => {
212
- const response = await deleteAccount(req, false);
213
- redirect(req, res, response, '/profile', '/profile');
214
- });
215
-
216
- app.get('/profile/resend', async (req, res) => {
217
- const response = await resendVerificationEmail(req);
218
- redirect(req, res, response, req.user ? '/profile' : '/login');
219
- });
220
+ // app.get('/signup', (req, res) => {
221
+ // if (req.user) return res.redirect('/dashboard');
222
+ // res.render('accounts/signup', {countries: COUNTRY_LIST});
223
+ // });
224
+
225
+ // app.post('/signup', async (req, res) => {
226
+ // const response = await signup(req);
227
+ // if (response.user) req.session.auth!.user = response.user.id;
228
+ // redirect(req, res, response, '/dashboard', '/signup');
229
+ // });
230
+
231
+ // app.get('/confirm/:id/:token', async (req, res) => {
232
+ // const response = await confirmEmail(req);
233
+ // redirect(req, res, response, '/dashboard', '/login');
234
+ // });
235
+
236
+ // app.get('/forgot', (req, res) => {
237
+ // if (req.user) return res.redirect('/dashboard');
238
+ // res.render('accounts/forgot');
239
+ // });
240
+
241
+ // app.post('/forgot', async (req, res) => {
242
+ // const response = await requestPasswordResetEmail(req);
243
+ // redirect(req, res, response, '/login', '/forgot');
244
+ // });
245
+
246
+ // app.get('/reset/:token', async (req, res) => {
247
+ // const response = await checkResetToken(req);
248
+ // if (response.error) return redirect(req, res, response, '/forgot');
249
+ // res.render('accounts/reset');
250
+ // });
251
+
252
+ // app.post('/reset/:token', async (req, res) => {
253
+ // const response = await resetPassword(req);
254
+ // redirect(req, res, response, '/login', '/reset');
255
+ // });
256
+
257
+ // app.get('/profile', (req, res) => {
258
+ // if (!req.user) return res.redirect('/login');
259
+ // res.render('accounts/profile', {countries: COUNTRY_LIST});
260
+ // });
261
+
262
+ // app.post('/profile/details', async (req, res) => {
263
+ // const response = await updateProfile(req);
264
+ // redirect(req, res, response, '/profile');
265
+ // });
266
+
267
+ // app.post('/profile/password', async (req, res) => {
268
+ // const response = await updatePassword(req);
269
+ // redirect(req, res, response, '/profile');
270
+ // });
271
+
272
+ // app.get('/profile/delete', async (req, res) => {
273
+ // const response = await deleteAccount(req, true);
274
+ // redirect(req, res, response, '/profile', '/profile');
275
+ // });
276
+
277
+ // app.get('/profile/undelete', async (req, res) => {
278
+ // const response = await deleteAccount(req, false);
279
+ // redirect(req, res, response, '/profile', '/profile');
280
+ // });
281
+
282
+ // app.get('/profile/resend', async (req, res) => {
283
+ // const response = await resendVerificationEmail(req);
284
+ // redirect(req, res, response, req.user ? '/profile' : '/login');
285
+ // });
286
287
app.post('/profile/accept-policies', async (req, res) => {
288
await acceptPolicies(req);
289
res.send('ok');
290
});
291
292
- app.get('/profile/data.json', async (req, res) => {
293
- if (!req.user) return res.redirect('/login');
294
- res.json(await exportData(req.user));
295
- });
296
+ // app.get('/profile/data.json', async (req, res) => {
297
+ // if (!req.user) return res.redirect('/login');
298
+ // res.json(await exportData(req.user));
299
+ // });
300
301
app.get('/auth/:provider', async (req, res, next) => {
302
const response = await oAuthLogin(req);
303
if (!response) return next();
304
- redirect(req, res, response, '/signup');
305
+ redirect(req, res, response, '/signin');
306
});
307
308
app.get('/auth/:provider/callback', async (req, res, next) => {
309
const response = await oAuthCallback(req);
310
if (!response) return next();
311
if (response.user) req.session.auth!.user = response.user.id;
312
- redirect(req, res, response, '/dashboard', '/signup');
313
+ redirect(req, res, response, '/account', '/signin');
314
});
315
316
app.get('/cron/cleanup', async (req, res, next) => {
317
diff --git a/node_modules/@mathigon/studio/server/app.ts b/node_modules/@mathigon/studio/server/app.ts
318
index 1d91310..05d0ea5 100755
319
--- a/node_modules/@mathigon/studio/server/app.ts
320
+++ b/node_modules/@mathigon/studio/server/app.ts
321
@@ -314,6 +314,8 @@ export class MathigonStudioApp {
322
323
this.post('/course/:course/:section', async (req, res, next) => {
324
if (!CONFIG.accounts.enabled) return res.status(200).send('ok');
325
+ // Not store progress if there is no user logged
326
+ if (!req.user?.id) return res.status(200).send('ok');
327
328
const course = getCourse(req.params.course, req.locale.id);
329
const section = course?.sections.find(s => s.id === req.params.section);
330
diff --git a/node_modules/@mathigon/studio/server/models/progress.ts b/node_modules/@mathigon/studio/server/models/progress.ts
331
index 6c9581e..504d21e 100755
332
--- a/node_modules/@mathigon/studio/server/models/progress.ts
333
+++ b/node_modules/@mathigon/studio/server/models/progress.ts
334
@@ -85,7 +85,7 @@ const ProgressSchema = new Schema<ProgressDocument, ProgressModel>({
335
messages: [{content: String, kind: {type: String, default: 'hint'}}]
336
}, {timestamps: true});
337
338
-ProgressSchema.index({user: 1, course: 1}, {unique: true});
339
+ProgressSchema.index({userId: 1, courseId: 1}, {unique: true});
340
341
ProgressSchema.virtual('activeSection').get(function(this: ProgressDocument) {
342
const course = getCourse(this.courseId, 'en')!;
343
diff --git a/node_modules/@mathigon/studio/server/utilities/mongodb.ts b/node_modules/@mathigon/studio/server/utilities/mongodb.ts
344
index 227d047..22dcf81 100644
345
--- a/node_modules/@mathigon/studio/server/utilities/mongodb.ts
346
+++ b/node_modules/@mathigon/studio/server/utilities/mongodb.ts
347
@@ -37,5 +37,6 @@ export async function connectMongo() {
348
349
export function getMongoStore() {
350
const clientPromise = connectMongo(); // async
351
+ // @ts-ignore
352
return MongoStore.create({clientPromise, touchAfter: 12 * 3600});
353
}
354
diff --git a/node_modules/@mathigon/studio/server/utilities/oauth.ts b/node_modules/@mathigon/studio/server/utilities/oauth.ts
355
index 115cec3..6ce1173 100644
356
--- a/node_modules/@mathigon/studio/server/utilities/oauth.ts
357
+++ b/node_modules/@mathigon/studio/server/utilities/oauth.ts
358
@@ -132,7 +132,10 @@ async function findOrCreateUser(req: express.Request, provider: Provider, profil
359
// We use req.headers.host rather than req.hostname because we need to include
360
// the localhost port during local development. Note that the redirect URI
361
// must exactly match a value set up with the third-party oAuth provider.
362
-const host = (req: express.Request) => `${req.protocol}://${req.headers.host}`;
363
+const host = (req: express.Request) => {
364
+ const protocol = req.headers.host?.includes('localhost') ? 'http' : 'https';
365
+ return `${protocol}://${req.headers.host}`
366
+};
367
368
function login(req: express.Request, provider: Provider) {
369
const config = PROVIDERS[provider];
370
371