Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mathjax to properly render mathjax content #31

Merged
merged 2 commits into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -314,4 +314,5 @@ pyrightconfig.json

.vscode
output
tmp
tmp
scraper/src/libretexts2zim/mathjax
5 changes: 2 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,10 @@ docker run --rm -it -v "$PWD/output":/output local-libretexts2zim libretexts2zim
Extract interesting ZIM content and move it to `public` folder.

```
rm -rf zimui/public/content
rm -rf zimui/public/content zimui/public/mathjax
docker run -it --rm -v $(pwd)/output:/data ghcr.io/openzim/zim-tools:latest zimdump dump --dir=/data/tests_en_libretexts-geo /data/tests_en_libretexts-geo.zim
sudo chown -R $(id -u -n):$(id -g -n) output/tests_en_libretexts-geo
mkdir -p zimui/public/content
mv output/tests_en_libretexts-geo/content/* zimui/public/content
mv output/tests_en_libretexts-geo/content output/tests_en_libretexts-geo/mathjax zimui/public
rm -rf output/tests_en_libretexts-geo
```

Expand Down
9 changes: 9 additions & 0 deletions scraper/openzim.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[files.assets.config]
target_dir="src/libretexts2zim"

[files.assets.actions."mathjax"]
action="extract_items"
source="https://github.com/mathjax/MathJax/archive/refs/tags/3.2.2.zip"
zip_paths=["MathJax-3.2.2"]
target_paths=["mathjax"]
remove = ["mathjax/.github","mathjax/.gitignore","mathjax/.travis.yml","mathjax/bower.json","mathjax/composer.json","mathjax/CONTRIBUTING.md","mathjax/package.json"]
2 changes: 2 additions & 0 deletions scraper/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ allow-direct-references = true
kind = "scraper"
additional-keywords = ["libretexts"]

[tool.hatch.build.hooks.openzim-build]

[project.optional-dependencies]
scripts = ["invoke==2.2.0"]
lint = ["black==24.8.0", "ruff==0.6.4"]
Expand Down
3 changes: 1 addition & 2 deletions scraper/src/libretexts2zim/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,8 +295,7 @@ def _process_tree_data(page_node: Any, parent: LibraryPage) -> None:
return tree_obj

def get_page_content(self, page: LibraryPage) -> LibraryPageContent:
"""Returns the content of a given page"""

"""Returns the 'raw' content of a given page"""
tree = self._get_api_json(
f"/pages/{page.id}/contents", timeout=HTTP_TIMEOUT_NORMAL_SECONDS
)
Expand Down
66 changes: 39 additions & 27 deletions scraper/src/libretexts2zim/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,45 @@
).model_dump_json(by_alias=True),
)

logger.info(" Storing the ZIM UI")
logger.info(f"Adding Vue.JS UI files in {self.zimui_dist}")

Check warning on line 262 in scraper/src/libretexts2zim/processor.py

View check run for this annotation

Codecov / codecov/patch

scraper/src/libretexts2zim/processor.py#L262

Added line #L262 was not covered by tests
for file in self.zimui_dist.rglob("*"):
if file.is_dir():
continue
path = str(Path(file).relative_to(self.zimui_dist))
logger.debug(f"Adding {path} to ZIM")

Check warning on line 267 in scraper/src/libretexts2zim/processor.py

View check run for this annotation

Codecov / codecov/patch

scraper/src/libretexts2zim/processor.py#L265-L267

Added lines #L265 - L267 were not covered by tests
if path == "index.html": # Change index.html title and add to ZIM
index_html_path = self.zimui_dist / path
add_item_for(

Check warning on line 270 in scraper/src/libretexts2zim/processor.py

View check run for this annotation

Codecov / codecov/patch

scraper/src/libretexts2zim/processor.py#L269-L270

Added lines #L269 - L270 were not covered by tests
creator=creator,
path=path,
content=index_html_path.read_text(encoding="utf-8").replace(
"<title>Vite App</title>",
f"<title>{formatted_config.title_format}</title>",
),
mimetype="text/html",
is_front=True,
)
else:
add_item_for(

Check warning on line 281 in scraper/src/libretexts2zim/processor.py

View check run for this annotation

Codecov / codecov/patch

scraper/src/libretexts2zim/processor.py#L281

Added line #L281 was not covered by tests
creator=creator,
path=path,
fpath=file,
is_front=False,
)

mathjax = (Path(__file__) / "../mathjax").resolve()
logger.info(f"Adding MathJax files in {mathjax}")

Check warning on line 289 in scraper/src/libretexts2zim/processor.py

View check run for this annotation

Codecov / codecov/patch

scraper/src/libretexts2zim/processor.py#L288-L289

Added lines #L288 - L289 were not covered by tests
for file in mathjax.rglob("*"):
if not file.is_file():
continue
path = str(Path(file).relative_to(mathjax.parent))
logger.debug(f"Adding {path} to ZIM")
add_item_for(

Check warning on line 295 in scraper/src/libretexts2zim/processor.py

View check run for this annotation

Codecov / codecov/patch

scraper/src/libretexts2zim/processor.py#L292-L295

Added lines #L292 - L295 were not covered by tests
creator=creator,
path=path,
fpath=file,
is_front=False,
)

logger.info(" Fetching and storing home page...")
home = self.libretexts_client.get_home()
Expand Down Expand Up @@ -308,32 +346,6 @@
# missing
logger.debug(f"Ignoring {asset_path} due to {exc}")

logger.info(f"Adding Vue.JS UI files in {self.zimui_dist}")
for file in self.zimui_dist.rglob("*"):
if file.is_dir():
continue
path = str(Path(file).relative_to(self.zimui_dist))
logger.debug(f"Adding {path} to ZIM")
if path == "index.html": # Change index.html title and add to ZIM
index_html_path = self.zimui_dist / path
add_item_for(
creator=creator,
path=path,
content=index_html_path.read_text(encoding="utf-8").replace(
"<title>Vite App</title>",
f"<title>{formatted_config.title_format}</title>",
),
mimetype="text/html",
is_front=True,
)
else:
add_item_for(
creator=creator,
path=path,
fpath=file,
is_front=False,
)

logger.info("Fetching pages tree")
pages_tree = self.libretexts_client.get_page_tree()
selected_pages = self.content_filter.filter(pages_tree)
Expand Down
13 changes: 7 additions & 6 deletions scraper/tests-integration/test_client.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import io
import re
from pathlib import Path

import pytest
Expand Down Expand Up @@ -138,17 +139,17 @@ def test_get_home_page_content(client: LibreTextsClient, page_tree: LibraryTree)

def test_get_home_screen_css_url(home: LibreTextsHome):
"""Ensures proper screen CSS url is retrieved"""
assert (
home.screen_css_url
== "https://a.mtstatic.com/@cache/layout/anonymous.css?_=715eca8811db7abb8e6f0555936e020d_Z2VvLmxpYnJldGV4dHMub3Jn:site_4038"
assert re.match(
r"https:\/\/a\.mtstatic\.com\/@cache\/layout\/anonymous\.css\?_=.*:site_4038",
home.screen_css_url,
)


def test_get_home_print_css_url(home: LibreTextsHome):
"""Ensures proper print CSS url is retrieved"""
assert (
home.print_css_url
== "https://a.mtstatic.com/@cache/layout/print.css?_=99d83fb44eaebe60981933ec554d138d:site_4038"
assert re.match(
r"https:\/\/a\.mtstatic\.com\/@cache\/layout\/print\.css\?_=.*:site_4038",
home.print_css_url,
)


Expand Down
3 changes: 2 additions & 1 deletion zimui/public/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
content
content
mathjax
14 changes: 14 additions & 0 deletions zimui/src/services/__tests__/mathjax.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { describe, it, expect } from 'vitest'
import mathjaxService from '../mathjax'

describe('MathJaxService', () => {
it('computes front 1.2. properly', () => {
expect(mathjaxService.frontFromTitle('1.2: The Scientific Method')).toBe('1.2.')
})
it('computes front 1.3. properly', () => {
expect(mathjaxService.frontFromTitle('1.3: The Foo Method')).toBe('1.3.')
})
it('computes front 1. properly', () => {
expect(mathjaxService.frontFromTitle('1: The Title Method')).toBe('1.')
})
})
79 changes: 79 additions & 0 deletions zimui/src/services/mathjax.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
Service to handle DOM manipulation to add/remove MathJax everytime needed.

This is a bit hackhish to remove and and back MathJax, but it is the only reliable
solution found so far, and probably the most robust one.

The dynamic behavior of removing / adding back MathJax is wanted/necessary because we
need to dynamically set the PageIndex macro to dynamically display proper figures
/ equations / ... numbering.

MathJax settings are an adaptation of libretexts.org settings, for MathJax 3 (including
extensions now removed or not yet supported or included by default).
*/

class MathJaxService {
front: string | undefined = undefined

frontFromTitle(title: string): string {
// Computes front value from page title.
// E.g. if page title is `1.2: The Scientific Method` then front is `1.2.`
let front: string = ''
if (title.includes(':')) {
front = title.split(':')[0]
if (front.includes('.')) {
const parts: string[] = front.split('.')
front = parts.map((int) => (int.includes('0') ? parseInt(int, 10) : int)).join('.')
}
front += '.'
}
return front
}

removeMathJax() {
const script = document.getElementById('mathjax-script')
if (script) script.remove()
if (window.MathJax) delete window.MathJax
}

addMathJax(front: string) {
window.MathJax = {
section: front,
tex: {
tags: 'all',
macros: {
PageIndex: ['{' + front + '#1}'.toString(), 1]
},
autoload: {
color: [],
colorv2: ['color']
},
packages: { '[+]': ['noerrors', 'mhchem', 'tagFormat', 'color', 'cancel'] }
},
loader: {
load: ['[tex]/noerrors', '[tex]/mhchem', '[tex]/tagFormat', '[tex]/colorv2', '[tex]/cancel']
},
svg: {
scale: 0.85
},
options: {
menuOptions: {
settings: {
zoom: 'Double-Click',
zscale: '150%'
}
}
}
}
const script = document.createElement('script', {
id: 'mathjax-script'
} as ElementCreationOptions)
script.src = './mathjax/es5/tex-svg.js'
document.head.appendChild(script)
}
}

const mathjaxService = new MathJaxService()
Object.freeze(mathjaxService)

export default mathjaxService
3 changes: 3 additions & 0 deletions zimui/src/stores/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { defineStore } from 'pinia'
import axios, { AxiosError } from 'axios'
import type { PageContent, Shared, SharedPage } from '@/types/shared'
import mathjaxService from '@/services/mathjax'

export type RootState = {
shared: Shared | null
Expand Down Expand Up @@ -56,6 +57,8 @@ export const useMainStore = defineStore('main', {
(response) => {
this.isLoading = false
this.pageContent = response.data as PageContent
mathjaxService.removeMathJax()
mathjaxService.addMathJax(mathjaxService.frontFromTitle(page.title))
},
(error) => {
this.isLoading = false
Expand Down
Empty file added zimui/src/types/global.d.ts
Empty file.
9 changes: 9 additions & 0 deletions zimui/src/types/mathjax.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// declare window.MathJax so that we can manipulate it without TS errors
declare global {
interface Window {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
MathJax: any
}
}

export {}
1 change: 1 addition & 0 deletions zimui/src/views/HomeView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ watch(
<!-- Reproduce DOM structure of libretexts.org for proper CSS functioning -->
<main class="elm-skin-container">
<article id="elm-main-content" class="elm-content-container">
<h1>{{ page?.title }}</h1>
<section
class="mt-content-container"
v-if="main.pageContent"
Expand Down