Skip to content

Commit

Permalink
Add MathJax for proper rendering of MahtJax content
Browse files Browse the repository at this point in the history
  • Loading branch information
benoit74 committed Oct 15, 2024
1 parent ff4240b commit 0a9b617
Show file tree
Hide file tree
Showing 12 changed files with 168 additions and 32 deletions.
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
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 @@ def run(self) -> Path:
).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 @@ def run(self) -> Path:
# 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
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
100 changes: 100 additions & 0 deletions zimui/src/directives/mathjax.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
Directive to handle DOM manipulation to add/remove MathJax everytime the page change

This is a bit hackhish but 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.

This directive MUST be used only once in the whole Vue.JS application.

MathJax settings are an adaptation of libretexts.org settings, for MathJax 3 (including
extensions now removed or not yet supported or included by default).
*/
import type { DirectiveBinding } from 'vue'

let front: string | undefined = undefined

const frontFromTitle = function (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
}

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

const addMathJax = function (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'
script.async = false
document.head.appendChild(script)
}

const vMathJax = {
mounted(el: HTMLElement, binding: DirectiveBinding) {
front = frontFromTitle(binding.value.title)
removeMathJax() // "just-in-case"
addMathJax(front)
},
updated(el: HTMLElement, binding: DirectiveBinding) {
// Reload MathJax only if title has changed (allow to debounce update which might be
// called multiple times)
const new_front = frontFromTitle(binding.value.title)
if (new_front == front) {
return
}
front = new_front
removeMathJax()
addMathJax(front)
},
unmounted() {
removeMathJax()
}
}

export default vMathJax
2 changes: 2 additions & 0 deletions zimui/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import router from './router'
import loadVuetify from './plugins/vuetify'

import ResizeObserver from 'resize-observer-polyfill'
import vMathJax from './directives/mathjax'

if (typeof window.ResizeObserver === 'undefined') {
console.debug('Polyfilling ResizeObserver')
Expand All @@ -20,6 +21,7 @@ loadVuetify()
app.use(createPinia())
app.use(vuetify)
app.use(router)
app.directive('mathjax', vMathJax)
app.mount('#app')
})
.catch((error) => {
Expand Down
1 change: 0 additions & 1 deletion zimui/src/stores/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ export const useMainStore = defineStore('main', {
this.isLoading = true
this.errorMessage = ''
this.errorDetails = ''

return axios.get('./content/shared.json').then(
(response) => {
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 {}
2 changes: 2 additions & 0 deletions zimui/src/views/HomeView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,12 @@ 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"
v-html="main.pageContent.htmlBody"
v-mathjax="page"
></section>
<div v-else>Page not found</div>
</article>
Expand Down

0 comments on commit 0a9b617

Please sign in to comment.