diff --git a/.gitignore b/.gitignore
index 93da4f6..fa4165b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -314,4 +314,5 @@ pyrightconfig.json
.vscode
output
-tmp
\ No newline at end of file
+tmp
+scraper/src/libretexts2zim/mathjax
\ No newline at end of file
diff --git a/scraper/openzim.toml b/scraper/openzim.toml
new file mode 100644
index 0000000..95887c8
--- /dev/null
+++ b/scraper/openzim.toml
@@ -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"]
\ No newline at end of file
diff --git a/scraper/pyproject.toml b/scraper/pyproject.toml
index 97b2ec8..0322b7d 100644
--- a/scraper/pyproject.toml
+++ b/scraper/pyproject.toml
@@ -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"]
diff --git a/scraper/src/libretexts2zim/client.py b/scraper/src/libretexts2zim/client.py
index de555d8..9857fd3 100644
--- a/scraper/src/libretexts2zim/client.py
+++ b/scraper/src/libretexts2zim/client.py
@@ -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
)
diff --git a/scraper/src/libretexts2zim/processor.py b/scraper/src/libretexts2zim/processor.py
index 2a77519..c794e24 100644
--- a/scraper/src/libretexts2zim/processor.py
+++ b/scraper/src/libretexts2zim/processor.py
@@ -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}")
+ 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(
+ "
Vite App",
+ f"{formatted_config.title_format}",
+ ),
+ mimetype="text/html",
+ is_front=True,
+ )
+ else:
+ add_item_for(
+ creator=creator,
+ path=path,
+ fpath=file,
+ is_front=False,
+ )
+
+ mathjax = (Path(__file__) / "../mathjax").resolve()
+ logger.info(f"Adding mathjax files in {mathjax}")
+ 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(
+ creator=creator,
+ path=path,
+ fpath=file,
+ is_front=False,
+ )
logger.info(" Fetching and storing home page...")
home = self.libretexts_client.get_home()
@@ -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(
- "Vite App",
- f"{formatted_config.title_format}",
- ),
- 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)
diff --git a/zimui/public/.gitignore b/zimui/public/.gitignore
index 6b584e8..ecf4dd6 100644
--- a/zimui/public/.gitignore
+++ b/zimui/public/.gitignore
@@ -1 +1,2 @@
-content
\ No newline at end of file
+content
+mathjax
\ No newline at end of file
diff --git a/zimui/src/directives/mathjax.ts b/zimui/src/directives/mathjax.ts
new file mode 100644
index 0000000..8727940
--- /dev/null
+++ b/zimui/src/directives/mathjax.ts
@@ -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
diff --git a/zimui/src/main.ts b/zimui/src/main.ts
index 5531a32..e598036 100644
--- a/zimui/src/main.ts
+++ b/zimui/src/main.ts
@@ -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')
@@ -20,6 +21,7 @@ loadVuetify()
app.use(createPinia())
app.use(vuetify)
app.use(router)
+ app.directive('mathjax', vMathJax)
app.mount('#app')
})
.catch((error) => {
diff --git a/zimui/src/stores/main.ts b/zimui/src/stores/main.ts
index 286de30..ed1ccc5 100644
--- a/zimui/src/stores/main.ts
+++ b/zimui/src/stores/main.ts
@@ -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
diff --git a/zimui/src/types/global.d.ts b/zimui/src/types/global.d.ts
new file mode 100644
index 0000000..e69de29
diff --git a/zimui/src/types/mathjax.d.ts b/zimui/src/types/mathjax.d.ts
new file mode 100644
index 0000000..758d9a1
--- /dev/null
+++ b/zimui/src/types/mathjax.d.ts
@@ -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 {}
diff --git a/zimui/src/views/HomeView.vue b/zimui/src/views/HomeView.vue
index 3e1493a..68fa300 100644
--- a/zimui/src/views/HomeView.vue
+++ b/zimui/src/views/HomeView.vue
@@ -45,10 +45,12 @@ watch(
+ {{ page?.title }}
Page not found