Path: blob/main/patches/@mathigon+studio+0.1.16.patch
3338 views
diff --git a/node_modules/@mathigon/studio/build/markdown/mathjax.js b/node_modules/@mathigon/studio/build/markdown/mathjax.js1index 93eae4d..e8da114 1006442--- a/node_modules/@mathigon/studio/build/markdown/mathjax.js3+++ b/node_modules/@mathigon/studio/build/markdown/mathjax.js4@@ -7,7 +7,7 @@5const path = require('path');6const entities = require('html-entities');7const mathjax = require('mathjax');8-const {readFile, writeFile, warning} = require('../utilities');9+const {CONFIG, readFile, writeFile, warning} = require('../utilities');1011const cacheFile = path.join(process.env.HOME, '/.mathjax-cache');12const mathJaxStore = JSON.parse(readFile(cacheFile, '{}'));13@@ -17,9 +17,15 @@ const placeholders = {};14let placeholderCount = 0;15let promise = undefined;1617+const tex2html = CONFIG.parser?.tex2html || false;18+19+function getId (code, isInline) {20+ return entities.decode(code) + (isInline || false) + (tex2html ? 'html' : '');21+}22+2324module.exports.makeTexPlaceholder = function(code, isInline = false) {25- const id = entities.decode(code) + (isInline || false);26+ const id = getId(code, isInline);27if (id in mathJaxStore) return mathJaxStore[id];2829const placeholder = `XEQUATIONX${placeholderCount++}XEQUATIONX`;30@@ -28,7 +34,7 @@ module.exports.makeTexPlaceholder = function(code, isInline = false) {31};3233async function texToSvg(code, isInline) {34- const id = entities.decode(code) + (isInline || false);35+ const id = getId(code, isInline);36if (mathJaxStore[id]) return mathJaxStore[id];3738if (!promise) {39@@ -56,10 +62,67 @@ async function texToSvg(code, isInline) {40return mathJaxStore[id] = output;41}4243+async function texToHtml(code, isInline) {44+ const id = getId(code, isInline);45+ if (mathJaxStore[id]) return mathJaxStore[id];46+47+ if (!promise) {48+ promise = mathjax.init({49+ options: {50+ renderActions: {51+ // add the TEX string into the DOM node52+ addInputText: [200,53+ (doc) => {54+ for (const math of doc.math) MathJax.config.addInputText(math, doc);55+ },56+ (math, doc) => MathJax.config.addInputText(math, doc)57+ ]58+ }59+ },60+ addInputText(math, doc) {61+ const adaptor = doc.adaptor;62+ const text = adaptor.node('mjx-input-tex', {63+ 'aria-hidden': true,64+ 'style': 'display:none;'65+ }, [66+ adaptor.text(math.start.delim + math.math + math.end.delim)67+ ]);68+ adaptor.append(math.typesetRoot, text);69+ },70+ loader: {load: ['input/tex-full', 'output/chtml']},71+ // https://docs.mathjax.org/en/latest/options/output/chtml.html#the-configuration-block72+ chtml: {73+ adaptiveCSS: false,74+ fontURL: 'https://cdn.jsdelivr.net/npm/[email protected]/es5/output/chtml/fonts/woff-v2'75+ }76+ });77+ }78+79+ let output = '';80+81+ try {82+ const MathJax = await promise;83+ const adaptor = MathJax.startup.adaptor;84+85+ const html = await MathJax.tex2chtml(code, {display: !isInline});86+ output = adaptor.outerHTML(html);87+ } catch (e) {88+ warning(` MathJax Error: ${e.message} at "${code}"`);89+ }90+91+ storeChanged = true;92+ return mathJaxStore[id] = output;93+}94+95module.exports.fillTexPlaceholders = async function(doc) {96const matches = doc.match(/XEQUATIONX[0-9]+XEQUATIONX/g) || [];97for (const placeholder of matches) {98- const code = await texToSvg(...placeholders[placeholder]);99+ let code = '';100+ if (tex2html) {101+ code = await texToHtml(...placeholders[placeholder]);102+ } else {103+ code = await texToSvg(...placeholders[placeholder]);104+ }105doc = doc.replace(placeholder, code);106}107return doc;108diff --git a/node_modules/@mathigon/studio/build/markdown/parser.js b/node_modules/@mathigon/studio/build/markdown/parser.js109index 14a4bcf..e143648 100644110--- a/node_modules/@mathigon/studio/build/markdown/parser.js111+++ b/node_modules/@mathigon/studio/build/markdown/parser.js112@@ -111,6 +111,7 @@ async function parseStep(content, index, directory, courseId, locale = 'en') {113for (const $md of $$(body, '.md')) {114$md.classList.remove('md');115$md.innerHTML = marked($md.innerHTML, {renderer}).replace(/^<p>|<\/p>$/g, '');116+ $md.innerHTML = await fillTexPlaceholders($md.innerHTML);117}118119// Add the [parent] attribute as class to all elements parents120diff --git a/node_modules/@mathigon/studio/server/accounts.ts b/node_modules/@mathigon/studio/server/accounts.ts121index a28f2db..d1eb2e7 100755122--- a/node_modules/@mathigon/studio/server/accounts.ts123+++ b/node_modules/@mathigon/studio/server/accounts.ts124@@ -266,110 +266,110 @@ function redirect(req: express.Request, res: express.Response, data: ResponseDat125126export default function setupAuthEndpoints(app: MathigonStudioApp) {127128- app.get('/login', (req, res) => {129- if (req.user) return res.redirect('/dashboard');130- res.render('accounts/login');131- });132+ // app.get('/login', (req, res) => {133+ // if (req.user) return res.redirect('/dashboard');134+ // res.render('accounts/login');135+ // });136137- app.post('/login', async (req, res) => {138- const response = await login(req);139- if (response.user) req.session.auth!.user = response.user.id;140- redirect(req, res, response, '/dashboard', '/login');141- });142+ // app.post('/login', async (req, res) => {143+ // const response = await login(req);144+ // if (response.user) req.session.auth!.user = response.user.id;145+ // redirect(req, res, response, '/dashboard', '/login');146+ // });147148app.get('/logout', (req, res) => {149delete req.session.auth!.user;150req.session.save(() => res.redirect('back'));151});152153- app.get('/signup', (req, res) => {154- if (req.user) return res.redirect('/dashboard');155- res.render('accounts/signup', {countries: COUNTRY_LIST});156- });157-158- app.post('/signup', async (req, res) => {159- const response = await signup(req);160- if (response.user) req.session.auth!.user = response.user.id;161- redirect(req, res, response, '/dashboard', '/signup');162- });163-164- app.get('/confirm/:id/:token', async (req, res) => {165- const response = await confirmEmail(req);166- redirect(req, res, response, '/dashboard', '/login');167- });168-169- app.get('/forgot', (req, res) => {170- if (req.user) return res.redirect('/dashboard');171- res.render('accounts/forgot');172- });173-174- app.post('/forgot', async (req, res) => {175- const response = await requestPasswordResetEmail(req);176- redirect(req, res, response, '/login', '/forgot');177- });178-179- app.get('/reset/:token', async (req, res) => {180- const response = await checkResetToken(req);181- if (response.error) return redirect(req, res, response, '/forgot');182- res.render('accounts/reset');183- });184-185- app.post('/reset/:token', async (req, res) => {186- const response = await resetPassword(req);187- redirect(req, res, response, '/login', '/reset');188- });189-190- app.get('/profile', (req, res) => {191- if (!req.user) return res.redirect('/login');192- res.render('accounts/profile', {countries: COUNTRY_LIST});193- });194-195- app.post('/profile/details', async (req, res) => {196- const response = await updateProfile(req);197- redirect(req, res, response, '/profile');198- });199-200- app.post('/profile/password', async (req, res) => {201- const response = await updatePassword(req);202- redirect(req, res, response, '/profile');203- });204-205- app.get('/profile/delete', async (req, res) => {206- const response = await deleteAccount(req, true);207- redirect(req, res, response, '/profile', '/profile');208- });209-210- app.get('/profile/undelete', async (req, res) => {211- const response = await deleteAccount(req, false);212- redirect(req, res, response, '/profile', '/profile');213- });214-215- app.get('/profile/resend', async (req, res) => {216- const response = await resendVerificationEmail(req);217- redirect(req, res, response, req.user ? '/profile' : '/login');218- });219+ // app.get('/signup', (req, res) => {220+ // if (req.user) return res.redirect('/dashboard');221+ // res.render('accounts/signup', {countries: COUNTRY_LIST});222+ // });223+224+ // app.post('/signup', async (req, res) => {225+ // const response = await signup(req);226+ // if (response.user) req.session.auth!.user = response.user.id;227+ // redirect(req, res, response, '/dashboard', '/signup');228+ // });229+230+ // app.get('/confirm/:id/:token', async (req, res) => {231+ // const response = await confirmEmail(req);232+ // redirect(req, res, response, '/dashboard', '/login');233+ // });234+235+ // app.get('/forgot', (req, res) => {236+ // if (req.user) return res.redirect('/dashboard');237+ // res.render('accounts/forgot');238+ // });239+240+ // app.post('/forgot', async (req, res) => {241+ // const response = await requestPasswordResetEmail(req);242+ // redirect(req, res, response, '/login', '/forgot');243+ // });244+245+ // app.get('/reset/:token', async (req, res) => {246+ // const response = await checkResetToken(req);247+ // if (response.error) return redirect(req, res, response, '/forgot');248+ // res.render('accounts/reset');249+ // });250+251+ // app.post('/reset/:token', async (req, res) => {252+ // const response = await resetPassword(req);253+ // redirect(req, res, response, '/login', '/reset');254+ // });255+256+ // app.get('/profile', (req, res) => {257+ // if (!req.user) return res.redirect('/login');258+ // res.render('accounts/profile', {countries: COUNTRY_LIST});259+ // });260+261+ // app.post('/profile/details', async (req, res) => {262+ // const response = await updateProfile(req);263+ // redirect(req, res, response, '/profile');264+ // });265+266+ // app.post('/profile/password', async (req, res) => {267+ // const response = await updatePassword(req);268+ // redirect(req, res, response, '/profile');269+ // });270+271+ // app.get('/profile/delete', async (req, res) => {272+ // const response = await deleteAccount(req, true);273+ // redirect(req, res, response, '/profile', '/profile');274+ // });275+276+ // app.get('/profile/undelete', async (req, res) => {277+ // const response = await deleteAccount(req, false);278+ // redirect(req, res, response, '/profile', '/profile');279+ // });280+281+ // app.get('/profile/resend', async (req, res) => {282+ // const response = await resendVerificationEmail(req);283+ // redirect(req, res, response, req.user ? '/profile' : '/login');284+ // });285286app.post('/profile/accept-policies', async (req, res) => {287await acceptPolicies(req);288res.send('ok');289});290291- app.get('/profile/data.json', async (req, res) => {292- if (!req.user) return res.redirect('/login');293- res.json(await exportData(req.user));294- });295+ // app.get('/profile/data.json', async (req, res) => {296+ // if (!req.user) return res.redirect('/login');297+ // res.json(await exportData(req.user));298+ // });299300app.get('/auth/:provider', async (req, res, next) => {301const response = await oAuthLogin(req);302if (!response) return next();303- redirect(req, res, response, '/signup');304+ redirect(req, res, response, '/signin');305});306307app.get('/auth/:provider/callback', async (req, res, next) => {308const response = await oAuthCallback(req);309if (!response) return next();310if (response.user) req.session.auth!.user = response.user.id;311- redirect(req, res, response, '/dashboard', '/signup');312+ redirect(req, res, response, '/account', '/signin');313});314315app.get('/cron/cleanup', async (req, res, next) => {316diff --git a/node_modules/@mathigon/studio/server/app.ts b/node_modules/@mathigon/studio/server/app.ts317index 1d91310..05d0ea5 100755318--- a/node_modules/@mathigon/studio/server/app.ts319+++ b/node_modules/@mathigon/studio/server/app.ts320@@ -314,6 +314,8 @@ export class MathigonStudioApp {321322this.post('/course/:course/:section', async (req, res, next) => {323if (!CONFIG.accounts.enabled) return res.status(200).send('ok');324+ // Not store progress if there is no user logged325+ if (!req.user?.id) return res.status(200).send('ok');326327const course = getCourse(req.params.course, req.locale.id);328const section = course?.sections.find(s => s.id === req.params.section);329diff --git a/node_modules/@mathigon/studio/server/models/progress.ts b/node_modules/@mathigon/studio/server/models/progress.ts330index 6c9581e..504d21e 100755331--- a/node_modules/@mathigon/studio/server/models/progress.ts332+++ b/node_modules/@mathigon/studio/server/models/progress.ts333@@ -85,7 +85,7 @@ const ProgressSchema = new Schema<ProgressDocument, ProgressModel>({334messages: [{content: String, kind: {type: String, default: 'hint'}}]335}, {timestamps: true});336337-ProgressSchema.index({user: 1, course: 1}, {unique: true});338+ProgressSchema.index({userId: 1, courseId: 1}, {unique: true});339340ProgressSchema.virtual('activeSection').get(function(this: ProgressDocument) {341const course = getCourse(this.courseId, 'en')!;342diff --git a/node_modules/@mathigon/studio/server/utilities/mongodb.ts b/node_modules/@mathigon/studio/server/utilities/mongodb.ts343index 227d047..22dcf81 100644344--- a/node_modules/@mathigon/studio/server/utilities/mongodb.ts345+++ b/node_modules/@mathigon/studio/server/utilities/mongodb.ts346@@ -37,5 +37,6 @@ export async function connectMongo() {347348export function getMongoStore() {349const clientPromise = connectMongo(); // async350+ // @ts-ignore351return MongoStore.create({clientPromise, touchAfter: 12 * 3600});352}353diff --git a/node_modules/@mathigon/studio/server/utilities/oauth.ts b/node_modules/@mathigon/studio/server/utilities/oauth.ts354index 115cec3..6ce1173 100644355--- a/node_modules/@mathigon/studio/server/utilities/oauth.ts356+++ b/node_modules/@mathigon/studio/server/utilities/oauth.ts357@@ -132,7 +132,10 @@ async function findOrCreateUser(req: express.Request, provider: Provider, profil358// We use req.headers.host rather than req.hostname because we need to include359// the localhost port during local development. Note that the redirect URI360// must exactly match a value set up with the third-party oAuth provider.361-const host = (req: express.Request) => `${req.protocol}://${req.headers.host}`;362+const host = (req: express.Request) => {363+ const protocol = req.headers.host?.includes('localhost') ? 'http' : 'https';364+ return `${protocol}://${req.headers.host}`365+};366367function login(req: express.Request, provider: Provider) {368const config = PROVIDERS[provider];369370371