Working…
This commit is contained in:
+18
-13
@@ -49,13 +49,13 @@ example (x y : ℚ) (h : x = y) : x + 1 = y + 1 := by
|
||||
|
||||
Definition 2.1.1 des Skripts:
|
||||
|
||||
> *Definition (Gruppe).* Eine Gruppe ist eine nicht-leere Menge `G`
|
||||
> mit einer Abbildung `m : G ⨯ G → G`, sodass folgende Eigenschaften
|
||||
> gelten. _Assoziativität:_ … _Neutrales Element:_ Es gibt genau ein
|
||||
> Element `e` aus `G`, sodass für alle `a` aus `G` gilt:
|
||||
> `m(e,a) = m(a,e) = a`. _Inverse Elemente:_ Für alle `a` aus `G`
|
||||
> gibt es genau ein Element `b` aus `G`, sodass `m(a,b) = m(b,a) = e`
|
||||
> ist.
|
||||
> *Definition (Gruppe).* Eine Gruppe ist eine nicht-leere Menge $`G`
|
||||
mit einer Abbildung $`m : G \times G \to G`, sodass folgende
|
||||
Eigenschaften gelten. _Assoziativität:_ … _Neutrales Element:_ Es
|
||||
gibt genau ein Element $`e` aus $`G`, sodass für alle $`a` aus $`G`
|
||||
gilt: $`m(e,a) = m(a,e) = a`. _Inverse Elemente:_ Für alle $`a`
|
||||
aus $`G` gibt es genau ein Element $`b` aus $`G`, sodass
|
||||
$`m(a,b) = m(b,a) = e` ist.
|
||||
|
||||
In Mathlib ist eine Gruppe eine Typklasse `Group G`. Die Verknüpfung
|
||||
schreibt sich `a * b`, das neutrale Element `1`, das Inverse `a⁻¹`;
|
||||
@@ -68,9 +68,10 @@ Widerspruch — die Eindeutigkeit folgt aus den übrigen Axiomen. Genau
|
||||
das beweisen wir jetzt, mit dem Standardargument aus der linearen
|
||||
Algebra:
|
||||
|
||||
> Es sei `e` ein weiteres Element, das von links neutral wirkt. Weil
|
||||
> `1` neutral ist, gilt `e·1 = e`. Weil `e` neutral wirkt, gilt
|
||||
> `e·1 = 1`. Zusammen folgt `e = e·1 = 1`.
|
||||
> Es sei $`e` ein weiteres Element, das von links neutral wirkt.
|
||||
Weil $`1` neutral ist, gilt $`e \cdot 1 = e`. Weil $`e` neutral
|
||||
wirkt, gilt $`e \cdot 1 = 1`. Zusammen folgt
|
||||
$`e = e \cdot 1 = 1`.
|
||||
|
||||
Der Lean-Beweis benutzt die Taktik `calc`, die eine Kette von
|
||||
Gleichungen Schritt für Schritt abarbeitet — das Lean-Gegenstück zur
|
||||
@@ -87,9 +88,13 @@ theorem neutral_eindeutig {G : Type*} [Group G] (e : G)
|
||||
|
||||
# Übungsaufgabe
|
||||
|
||||
Zeigen Sie ebenso die Eindeutigkeit des Inversen: Wenn `a·b = 1` ist,
|
||||
dann ist `b` bereits _das_ Inverse `a⁻¹`. Das Standardargument:
|
||||
`b = 1·b = (a⁻¹·a)·b = a⁻¹·(a·b) = a⁻¹·1 = a⁻¹`. Öffnen Sie
|
||||
Zeigen Sie ebenso die Eindeutigkeit des Inversen: Wenn
|
||||
$`a \cdot b = 1` ist, dann ist $`b` bereits _das_ Inverse $`a^{-1}`.
|
||||
Das Standardargument:
|
||||
|
||||
$$`b = 1 \cdot b = (a^{-1} \cdot a) \cdot b = a^{-1} \cdot (a \cdot b) = a^{-1} \cdot 1 = a^{-1}`
|
||||
|
||||
Öffnen Sie
|
||||
`AlgebraInLean/Basics.lean` und ersetzen Sie das `sorry` durch einen
|
||||
Beweis — `calc` und die Lemmata `one_mul`, `inv_mul_cancel`,
|
||||
`mul_assoc`, `mul_one` genügen.
|
||||
|
||||
+49
-43
@@ -29,26 +29,27 @@ ersten Mal selbst etwas in Lean _definieren_: eine Gruppenwirkung.
|
||||
|
||||
# Das zentrale Schlüssellemma
|
||||
|
||||
> *Lemma 18.1.1 (Zentrales Schlüssellemma).* Es sei `m ∈ ℕ` und es sei `p`
|
||||
> eine Primzahl. Weiter sei `G` eine Gruppe der Ordnung `p^m`, die auf
|
||||
> einer endlichen Menge `M` operiert. Weiter sei
|
||||
> `M₀ = { m ∈ M : ∀ g ∈ G: g·m = m }` die Menge der Fixpunkte. Dann
|
||||
> ist `|M| ≡ |M₀| (mod p)`.
|
||||
> *Lemma 18.1.1 (Zentrales Schlüssellemma).* Es sei
|
||||
$`m \in \mathbb{N}` und es sei $`p` eine Primzahl. Weiter sei $`G`
|
||||
eine Gruppe der Ordnung $`p^m`, die auf einer endlichen Menge $`M`
|
||||
operiert. Weiter sei
|
||||
$`M_0 = \{ m \in M : g \cdot m = m \ \forall g \in G \}` die Menge
|
||||
der Fixpunkte. Dann ist $`|M| \equiv |M_0| \pmod{p}`.
|
||||
|
||||
Wie sagt Mathlib das alles?
|
||||
|
||||
* „eine Gruppe der Ordnung `p^m`“: Dafür hat Mathlib ein Prädikat
|
||||
`IsPGroup p G` („die Ordnung jedes Elements ist eine p-Potenz“ — für
|
||||
endliche Gruppen ist das äquivalent, nach dem Satz von Cauchy
|
||||
unten!). Das Lemma `IsPGroup.of_card` übersetzt unsere
|
||||
Voraussetzung `Nat.card G = p ^ m` dorthin.
|
||||
* „eine Gruppe der Ordnung $`p^m`“: Dafür hat Mathlib ein Prädikat
|
||||
`IsPGroup p G` („die Ordnung jedes Elements ist eine
|
||||
$`p`-Potenz“ — für endliche Gruppen ist das äquivalent, nach dem
|
||||
Satz von Cauchy unten!). Das Lemma `IsPGroup.of_card` übersetzt
|
||||
unsere Voraussetzung `Nat.card G = p ^ m` dorthin.
|
||||
|
||||
* „die auf einer endlichen Menge `M` operiert“: Eine Wirkung von `G`
|
||||
auf `M` ist eine Typklasse, `[MulAction G M]`; die Endlichkeit von
|
||||
`M` ist die Typklasse `[Finite M]`.
|
||||
|
||||
* Die Fixpunktmenge heißt `MulAction.fixedPoints G M`, und die
|
||||
Kongruenz `|M| ≡ |M₀| (mod p)` schreibt sich
|
||||
Kongruenz $`|M| \equiv |M_0| \pmod{p}` schreibt sich
|
||||
`Nat.card M ≡ Nat.card (fixedPoints G M) [MOD p]`.
|
||||
|
||||
Der Beweis im Skript zerlegt `M` in Bahnen und zitiert die
|
||||
@@ -75,8 +76,9 @@ gleich auf.)
|
||||
|
||||
# Der Satz von Cauchy
|
||||
|
||||
> *Satz 18.1.2 (Satz von Cauchy).* Wenn die Ordnung einer endlichen Gruppe
|
||||
> durch `p` teilbar ist, dann existiert ein Element von Ordnung `p`.
|
||||
> *Satz 18.1.2 (Satz von Cauchy).* Wenn die Ordnung einer endlichen
|
||||
Gruppe durch $`p` teilbar ist, dann existiert ein Element von
|
||||
Ordnung $`p`.
|
||||
|
||||
Wir folgen dem Beweis des Skripts Satz für Satz. Der Beweis
|
||||
konstruiert eine raffinierte Hilfsmenge mit einer raffinierten
|
||||
@@ -86,22 +88,23 @@ Arbeit: Wir bauen erst die Menge und die Wirkung.
|
||||
## Die Menge M
|
||||
|
||||
„Betrachte die Menge
|
||||
`M = { (a₁, …, a_p) ∈ G ⨯ ⋯ ⨯ G : a₁·a₂ ⋯ a_p = e }`.“
|
||||
$`M = \bigl\{ (a_1, \dots, a_p) \in G \times \dots \times G : a_1 \cdot a_2 \cdots a_p = e \bigr\}`.“
|
||||
|
||||
Mathlib kennt diese Menge. Ein p-Tupel von Gruppenelementen ist ein
|
||||
`List.Vector G p` — eine Liste der Länge `p` —, und `M` ist die Menge
|
||||
`Equiv.Perm.vectorsProdEqOne G p` aller Vektoren, deren Einträge sich
|
||||
zu 1 multiplizieren.
|
||||
Mathlib kennt diese Menge. Ein $`p`-Tupel von Gruppenelementen ist
|
||||
ein `List.Vector G p` — eine Liste der Länge $`p` —, und $`M` ist die
|
||||
Menge `Equiv.Perm.vectorsProdEqOne G p` aller Vektoren, deren
|
||||
Einträge sich zu 1 multiplizieren.
|
||||
|
||||
„Gegeben ein Tupel `(a₁, …, a_p) ∈ M`, dann stellen wir erst einmal
|
||||
fest, dass der letzte Eintrag des Tupels durch die ersten Einträge
|
||||
eindeutig bestimmt ist, `a_p = (a₁ ⋯ a_{p-1})⁻¹`. Wir erhalten die
|
||||
folgende Gleichung: `|M| = |G^{p-1}| = |G|^{p-1}`.“
|
||||
„Gegeben ein Tupel $`(a_1, \dots, a_p) \in M`, dann stellen wir erst
|
||||
einmal fest, dass der letzte Eintrag des Tupels durch die ersten
|
||||
Einträge eindeutig bestimmt ist,
|
||||
$`a_p = (a_1 \cdots a_{p-1})^{-1}`. Wir erhalten die folgende
|
||||
Gleichung: $`|M| = |G^{p-1}| = |G|^{p-1}`.“
|
||||
|
||||
Auch das steht schon in Mathlib: Die Bijektion
|
||||
`(a₁, …, a_{p-1}) ↦ (a₁, …, a_{p-1}, (a₁ ⋯ a_{p-1})⁻¹)` heißt
|
||||
`VectorsProdEqOne.vectorEquiv`, und die resultierende Zählformel ist
|
||||
`VectorsProdEqOne.card`:
|
||||
$`(a_1, \dots, a_{p-1}) \mapsto (a_1, \dots, a_{p-1}, (a_1 \cdots a_{p-1})^{-1})`
|
||||
heißt `VectorsProdEqOne.vectorEquiv`, und die resultierende
|
||||
Zählformel ist `VectorsProdEqOne.card`:
|
||||
|
||||
```
|
||||
VectorsProdEqOne.card (G : Type) [Group G] (n : ℕ) [Fintype G] :
|
||||
@@ -112,22 +115,25 @@ VectorsProdEqOne.card (G : Type) [Group G] (n : ℕ) [Fintype G] :
|
||||
|
||||
„Als Nächstes brauchen wir eine schicke Gruppenwirkung, denn wir
|
||||
wollen das zentrale Schlüssellemma anwenden. Dazu lassen wir die
|
||||
zyklische Gruppe `ℤ/(p)` auf `M` durch zyklisches Vertauschen wirken.“
|
||||
zyklische Gruppe $`\mathbb{Z}/(p)` auf $`M` durch zyklisches
|
||||
Vertauschen wirken.“
|
||||
|
||||
Der zyklische Shift eines Vektors `v ∈ vectorsProdEqOne G p` um `k`
|
||||
Stellen ist `VectorsProdEqOne.rotate v k`. Die Fußnote des Skripts —
|
||||
der Shift bildet `M` auf sich ab, „weil in jeder Gruppe aus `a·b = e`
|
||||
auch `b·a = e` gilt“ — ist das Mathlib-Lemma
|
||||
Der zyklische Shift eines Vektors `v ∈ vectorsProdEqOne G p` um
|
||||
$`k` Stellen ist `VectorsProdEqOne.rotate v k`. Die Fußnote des
|
||||
Skripts — der Shift bildet $`M` auf sich ab, „weil in jeder Gruppe
|
||||
aus $`a \cdot b = e` auch $`b \cdot a = e` gilt“ — ist das
|
||||
Mathlib-Lemma
|
||||
`List.prod_rotate_eq_one_of_prod_eq_one`, das schon in der Definition
|
||||
von `rotate` steckt.
|
||||
|
||||
Damit `ℤ/(p)` _als Gruppe_ wirkt, müssen wir die Wirkungsaxiome
|
||||
nachprüfen: Der Shift um 0 tut nichts, und der Shift um `j + k` ist
|
||||
der Shift um `k` gefolgt vom Shift um `j`. Mathlib stellt
|
||||
`rotate_zero`, `rotate_rotate` und `rotate_length` bereit (der Shift
|
||||
um die volle Länge `p` tut nichts); daraus leiten wir zuerst ab, dass
|
||||
der Shift nur von der Verschiebung _modulo p_ abhängt — genau deshalb
|
||||
wirkt `ℤ/(p)` und nicht bloß `ℕ`.
|
||||
Damit $`\mathbb{Z}/(p)` _als Gruppe_ wirkt, müssen wir die
|
||||
Wirkungsaxiome nachprüfen: Der Shift um 0 tut nichts, und der Shift
|
||||
um $`j + k` ist der Shift um $`k` gefolgt vom Shift um $`j`. Mathlib
|
||||
stellt `rotate_zero`, `rotate_rotate` und `rotate_length` bereit (der
|
||||
Shift um die volle Länge $`p` tut nichts); daraus leiten wir zuerst
|
||||
ab, dass der Shift nur von der Verschiebung _modulo p_ abhängt —
|
||||
genau deshalb wirkt $`\mathbb{Z}/(p)` und nicht bloß
|
||||
$`\mathbb{N}`.
|
||||
|
||||
```lean
|
||||
theorem rotate_mul {G : Type*} [Group G] {p : ℕ}
|
||||
@@ -151,7 +157,7 @@ theorem rotate_mod {G : Type*} [Group G] {p : ℕ}
|
||||
|
||||
Nun können wir die Wirkung definieren. Zwei technische Anmerkungen.
|
||||
Mathlibs `MulAction` erwartet eine multiplikativ geschriebene Gruppe;
|
||||
`ℤ/(p)` = `ZMod p` ist aber additiv geschrieben. Der Wrapper
|
||||
$`\mathbb{Z}/(p)` = `ZMod p` ist aber additiv geschrieben. Der Wrapper
|
||||
`Multiplicative` wechselt die Notation (dank
|
||||
`open Multiplicative` oben schreiben sich seine Übergänge kurz
|
||||
`toAdd` und `ofAdd`). Die Voraussetzung `[NeZero p]` schließt
|
||||
@@ -176,13 +182,13 @@ instance rotateAction {G : Type*} [Group G] {p : ℕ}
|
||||
## Die Fixpunkte
|
||||
|
||||
„Die Fixpunktmenge dieser Wirkung ist
|
||||
`M₀ = { (a, …, a) ∈ G^p : a^p = e }`.“
|
||||
$`M_0 = \{ (a, \dots, a) \in G^p : a^p = e \}`.“
|
||||
|
||||
Mit anderen Worten: Ein Tupel ist genau dann ein Fixpunkt, wenn es
|
||||
konstant ist, seine Liste also für ein `a` die Liste
|
||||
`List.replicate p a` — das ist `(a, …, a)` — ist. (Die Bedingung
|
||||
`a^p = e` gilt dann automatisch, weil sich die Einträge eines Tupels
|
||||
in `M` zu `e` multiplizieren.) Der Schlüsselschritt ist das Lemma
|
||||
konstant ist, seine Liste also für ein $`a` die Liste
|
||||
`List.replicate p a` — das ist $`(a, \dots, a)` — ist. (Die
|
||||
Bedingung $`a^p = e` gilt dann automatisch, weil sich die Einträge
|
||||
eines Tupels in $`M` zu $`e` multiplizieren.) Der Schlüsselschritt ist das Lemma
|
||||
`List.rotate_one_eq_self_iff_eq_replicate`: Eine Liste, die der
|
||||
zyklische Shift um _eine_ Stelle nicht ändert, ist konstant.
|
||||
|
||||
|
||||
+30
-26
@@ -27,34 +27,37 @@ und Körpererweiterungen als Vektorräume.
|
||||
|
||||
# Das Mathlib-Wörterbuch
|
||||
|
||||
* Eine Körpererweiterung `L/K` ist in Mathlib eine
|
||||
`Algebra K L`-Instanz zwischen zwei Körpern — `L` wird damit
|
||||
insbesondere ein K-Vektorraum, genau wie im Skript.
|
||||
* Eine Körpererweiterung $`L/K` ist in Mathlib eine
|
||||
`Algebra K L`-Instanz zwischen zwei Körpern — $`L` wird damit
|
||||
insbesondere ein $`K`-Vektorraum, genau wie im Skript.
|
||||
|
||||
* Eine Kette `K ⊆ L ⊆ M` besteht aus drei Algebra-Instanzen und der
|
||||
Verträglichkeitsbedingung `IsScalarTower K L M` („erst nach `L`,
|
||||
dann nach `M` einbetten ist dasselbe wie direkt nach `M`“).
|
||||
* Eine Kette $`K \subseteq L \subseteq M` besteht aus drei
|
||||
Algebra-Instanzen und der Verträglichkeitsbedingung
|
||||
`IsScalarTower K L M` („erst nach $`L`, dann nach $`M` einbetten
|
||||
ist dasselbe wie direkt nach $`M`“).
|
||||
|
||||
* Der Grad `[L:K]` heißt `Module.finrank K L`. Achtung, hier weicht
|
||||
die Konvention vom Skript ab: `finrank` hat Werte in `ℕ`, und
|
||||
unendlichdimensionale Erweiterungen bekommen den Wert 0 — wo das
|
||||
Skript in seiner Fußnote mit `∞` rechnet, rechnet Mathlib mit 0.
|
||||
„`[L:K]` ist endlich“ ist die Typklasse `FiniteDimensional K L`.
|
||||
* Der Grad $`[L:K]` heißt `Module.finrank K L`. Achtung, hier weicht
|
||||
die Konvention vom Skript ab: `finrank` hat Werte in $`\mathbb{N}`,
|
||||
und unendlichdimensionale Erweiterungen bekommen den Wert 0 — wo
|
||||
das Skript in seiner Fußnote mit $`\infty` rechnet, rechnet Mathlib
|
||||
mit 0. „$`[L:K]` ist endlich“ ist die Typklasse
|
||||
`FiniteDimensional K L`.
|
||||
|
||||
# Die Gradformel
|
||||
|
||||
Satz 3.6.1 des Skripts, mit Beweis:
|
||||
|
||||
> *Satz 3.6.1 (Gradformel).* Es sei `K ⊆ L ⊆ M` eine Kette von
|
||||
> Körpererweiterungen. Dann gilt die Gleichung
|
||||
> `[M:K] = [M:L]·[L:K]`.
|
||||
>
|
||||
> *Satz 3.6.1 (Gradformel).* Es sei $`K \subseteq L \subseteq M` eine
|
||||
Kette von Körpererweiterungen. Dann gilt die Gleichung
|
||||
$`[M:K] = [M:L] \cdot [L:K]`.
|
||||
|
||||
> _Beweis._ Wir kümmern uns zuerst um die unendlichen Fälle. … Es
|
||||
> seien jetzt also `a := [L:K]` und `b := [M:L]` beide endlich. Wähle
|
||||
> Basen `ℓ₁, …, ℓ_a` von `L` als K-Vektorraum und `m₁, …, m_b` von `M`
|
||||
> als L-Vektorraum. Ich behaupte, dass die `a·b` Produkte `(ℓᵢ·mⱼ)`
|
||||
> eine Basis von `M` als K-Vektorraum bilden; damit ist dann sofort
|
||||
> `[M:K] = a·b` gezeigt.
|
||||
seien jetzt also $`a := [L:K]` und $`b := [M:L]` beide endlich.
|
||||
Wähle Basen $`\ell_1, \dots, \ell_a` von $`L` als
|
||||
$`K`-Vektorraum und $`m_1, \dots, m_b` von $`M` als
|
||||
$`L`-Vektorraum. Ich behaupte, dass die $`a \cdot b` Produkte
|
||||
$`(\ell_i \cdot m_j)` eine Basis von $`M` als $`K`-Vektorraum
|
||||
bilden; damit ist dann sofort $`[M:K] = a \cdot b` gezeigt.
|
||||
|
||||
Die unendlichen Fälle entfallen in unserer Fassung, weil wir
|
||||
Endlichkeit voraussetzen; in Mathlibs 0-Konvention gilt die Formel
|
||||
@@ -63,8 +66,9 @@ sogar uneingeschränkt (`Module.finrank_mul_finrank`).
|
||||
Die Behauptung — mit den beiden Beweisschritten „Die Produkte bilden
|
||||
ein Erzeugendensystem“ und „Die Produkte sind linear unabhängig“ —
|
||||
ist in Mathlib die Konstruktion `Basis.smulTower`: Aus einer Basis
|
||||
`ℓ` von `L/K` und einer Basis `m` von `M/L` baut sie die Basis
|
||||
`(ℓᵢ • mⱼ)` von `M/K` (das Lemma `Basis.smulTower_apply` bestätigt,
|
||||
$`\ell` von $`L/K` und einer Basis $`m` von $`M/L` baut sie die
|
||||
Basis $`(\ell_i \cdot m_j)` von $`M/K` (das Lemma
|
||||
`Basis.smulTower_apply` bestätigt,
|
||||
dass die Basiselemente wirklich die Produkte sind). Der Rest ist
|
||||
Abzählen der Indexmenge.
|
||||
|
||||
@@ -101,12 +105,12 @@ Skript.
|
||||
|
||||
Das erste Korollar des Skripts zur Gradformel:
|
||||
|
||||
> Es sei `K ⊆ L ⊆ M` eine Kette von Körpererweiterungen. Wenn
|
||||
> `[M:K]` endlich ist, dann ist `[L:K]` endlich, und sogar ein Teiler
|
||||
> von `[M:K]`.
|
||||
> Es sei $`K \subseteq L \subseteq M` eine Kette von
|
||||
Körpererweiterungen. Wenn $`[M:K]` endlich ist, dann ist
|
||||
$`[L:K]` endlich, und sogar ein Teiler von $`[M:K]`.
|
||||
|
||||
Der erste Teil ist `Module.Finite.left` (und `Module.Finite.right`
|
||||
liefert die Endlichkeit von `[M:L]`) — beide müssen mit `have := …`
|
||||
liefert die Endlichkeit von $`[M:L]`) — beide müssen mit `have := …`
|
||||
in den Kontext geholt werden, damit die Instanzsuche sie sieht. Der
|
||||
Teiler-Teil ist die Übung: Öffnen Sie `AlgebraInLean/Degrees.lean`
|
||||
und ersetzen Sie das `sorry` durch einen Beweis mit `gradformel`.
|
||||
|
||||
+23
-19
@@ -22,13 +22,13 @@ file := "frobenius"
|
||||
|
||||
Dieses Kapitel übersetzt den Satz über den Frobenius-Endomorphismus
|
||||
aus Kapitel 14 des Skripts Satz für Satz nach Lean — den „Traum jedes
|
||||
Studienanfängers“: `(a+b)^p = a^p + b^p`.
|
||||
Studienanfängers“: $`(a+b)^p = a^p + b^p`.
|
||||
|
||||
# Das Mathlib-Wörterbuch
|
||||
|
||||
* „`R` hat Charakteristik `p`“ ist die Typklasse `CharP R p`. Das
|
||||
entscheidende Lemma ist `CharP.cast_eq_zero`: Das Bild von `p` in
|
||||
`R` ist Null.
|
||||
* „$`R` hat Charakteristik $`p`“ ist die Typklasse `CharP R p`.
|
||||
Das entscheidende Lemma ist `CharP.cast_eq_zero`: Das Bild von
|
||||
$`p` in $`R` ist Null.
|
||||
|
||||
* Binomialkoeffizienten heißen `Nat.choose`; die binomische Formel
|
||||
ist `add_pow`, eine Summe über `Finset.range`. Endliche Summen und
|
||||
@@ -38,19 +38,22 @@ Studienanfängers“: `(a+b)^p = a^p + b^p`.
|
||||
|
||||
Satz und Definition 14.2.2 des Skripts:
|
||||
|
||||
> *Satz und Definition 14.2.2 (Frobenius-Endomorphismus).* Es sei `p` eine
|
||||
> Primzahl und es sei `R` ein kommutativer Ring mit Eins der
|
||||
> Charakteristik `p`. Dann ist die Abbildung `F : R → R`, `a ↦ a^p`
|
||||
> ein Ringmorphismus.
|
||||
>
|
||||
> _Beweis._ Die Verträglichkeit mit der Multiplikation ist klar, weil
|
||||
> `R` kommutativ ist: `(a·b)^p = a^p·b^p`. Ebenso ist `F(1) = 1`.
|
||||
> Interessant ist nur die Verträglichkeit mit der Addition. … Die
|
||||
> binomische Formel gilt in jedem kommutativen Ring …. Für alle
|
||||
> Indizes `0 < k < p` ist der Binomialkoeffizient ein Vielfaches von
|
||||
> `p` …. Weil `R` die Charakteristik `p` hat, ist `p = 0` in `R`,
|
||||
> und alle diese Summanden verschwinden. Übrig bleiben nur die
|
||||
> Summanden für `k = 0` und `k = p`.
|
||||
> *Satz und Definition 14.2.2 (Frobenius-Endomorphismus).* Es sei
|
||||
$`p` eine Primzahl und es sei $`R` ein kommutativer Ring mit Eins
|
||||
der Charakteristik $`p`. Dann ist die Abbildung
|
||||
$`F : R \to R`, $`a \mapsto a^p` ein Ringmorphismus.
|
||||
|
||||
> _Beweis._ Die Verträglichkeit mit der Multiplikation ist klar,
|
||||
weil $`R` kommutativ ist: $`(a \cdot b)^p = a^p \cdot b^p`.
|
||||
Ebenso ist $`F(1) = 1`. Interessant ist nur die Verträglichkeit
|
||||
mit der Addition. … Die binomische Formel gilt in jedem
|
||||
kommutativen Ring, also ist
|
||||
$`(a+b)^p = \sum_{k=0}^{p} \binom{p}{k} \cdot a^k \cdot b^{p-k}`.
|
||||
Für alle Indizes $`0 < k < p` ist der Binomialkoeffizient
|
||||
$`\binom{p}{k}` ein Vielfaches von $`p` …. Weil $`R` die
|
||||
Charakteristik $`p` hat, ist $`p = 0` in $`R`, und alle diese
|
||||
Summanden verschwinden. Übrig bleiben nur die Summanden für
|
||||
$`k = 0` und $`k = p`.
|
||||
|
||||
```lean
|
||||
theorem frobenius_mul {R : Type*} [CommRing R] (p : ℕ)
|
||||
@@ -115,8 +118,9 @@ Elementen. In Mathlib heißt er `GaloisField p n`.
|
||||
|
||||
# Übungsaufgabe
|
||||
|
||||
Der Frobenius des Körpers `𝔽_p` ist die Identität: Für jedes
|
||||
`a ∈ 𝔽_p` ist `a^p = a`. Das ist ein alter Bekannter — der kleine
|
||||
Der Frobenius des Körpers $`\mathbb{F}_p` ist die Identität: Für
|
||||
jedes $`a \in \mathbb{F}_p` ist $`a^p = a`. Das ist ein alter
|
||||
Bekannter — der kleine
|
||||
Satz von Fermat aus unserem dritten Kapitel! In Mathlib heißt er
|
||||
`ZMod.pow_card`. Öffnen Sie `AlgebraInLean/Frobenius.lean` und
|
||||
ersetzen Sie das `sorry` durch einen Beweis.
|
||||
|
||||
+22
-15
@@ -28,11 +28,11 @@ ist und es auch hier bleibt.
|
||||
|
||||
# Das Mathlib-Wörterbuch
|
||||
|
||||
* Die Galoisgruppe einer Körpererweiterung `L/K` ist der Typ
|
||||
* Die Galoisgruppe einer Körpererweiterung $`L/K` ist der Typ
|
||||
`L ≃ₐ[K] L` der K-Algebren-Automorphismen; Mathlib stellt dafür
|
||||
sogar die Notation `Gal(L/K)` bereit.
|
||||
|
||||
* „`L/K` ist galoissch“ ist die Typklasse `IsGalois K L` — definiert
|
||||
* „$`L/K` ist galoissch“ ist die Typklasse `IsGalois K L` — definiert
|
||||
als „separabel und normal“, genau wie im Skript.
|
||||
|
||||
* Ein Zwischenkörper ist ein Term `Z : IntermediateField K L`; der
|
||||
@@ -43,10 +43,12 @@ ist und es auch hier bleibt.
|
||||
|
||||
Satz und Definition 16.1.1 des Skripts:
|
||||
|
||||
> *Satz und Definition 16.1.1 (Invariante Elemente, Fixkörper).* Sei `L` ein
|
||||
> Körper und `G` eine Menge von Automorphismen `L → L`. Dann ist die
|
||||
> Menge `Fix G = { a ∈ L : σ(a) = a für alle σ ∈ G }` ein Unterkörper
|
||||
> von `L`. … Der Beweis des folgenden Satzes ist eine Hausaufgabe.
|
||||
> *Satz und Definition 16.1.1 (Invariante Elemente, Fixkörper).* Sei
|
||||
$`L` ein Körper und $`G` eine Menge von Automorphismen
|
||||
$`L \to L`. Dann ist die Menge
|
||||
$`\operatorname{Fix} G = \{ a \in L : \sigma(a) = a \ \forall \sigma \in G \}`
|
||||
ein Unterkörper von $`L`. … Der Beweis des folgenden Satzes ist
|
||||
eine Hausaufgabe.
|
||||
|
||||
Wir definieren die Menge in Lean und beweisen exemplarisch die
|
||||
Abgeschlossenheit unter der Multiplikation — der Rest der Hausaufgabe
|
||||
@@ -71,23 +73,28 @@ theorem mul_mem_fix {L : Type*} [Field L]
|
||||
Auch die großen Sätze dieses Teils der Vorlesung stehen in Mathlib;
|
||||
wir zitieren sie mit ihren Namen.
|
||||
|
||||
> *Satz von Emil Artin (Satz 16.1.2).* Es sei `G` eine endliche Untergruppe der
|
||||
> Automorphismengruppe eines Körpers `L` und es sei `K := Fix G`.
|
||||
> Dann ist `L/K` eine Galoiserweiterung mit Galoisgruppe
|
||||
> `Gal(L/K) = G`. Insbesondere ist `[L:K] = |G|`.
|
||||
> *Satz von Emil Artin (Satz 16.1.2).* Es sei $`G` eine endliche
|
||||
Untergruppe der Automorphismengruppe eines Körpers $`L` und es sei
|
||||
$`K := \operatorname{Fix} G`. Dann ist $`L/K` eine
|
||||
Galoiserweiterung mit Galoisgruppe
|
||||
$`\operatorname{Gal}(L/K) = G`. Insbesondere ist
|
||||
$`[L:K] = |G|`.
|
||||
|
||||
Die Gradaussage ist `FixedPoints.finrank_eq_card`; das gebündelte
|
||||
Fixkörper-Objekt heißt dort `FixedPoints.subfield G L`.
|
||||
|
||||
> *Hauptsatz der Galoistheorie (Satz 16.3.2).* Es sei `L/K` eine Galoiserweiterung
|
||||
> mit Galoisgruppe `G`. Dann sind die Abbildungen `Z ↦ Gal(L/Z)` und
|
||||
> `H ↦ Fix H` zueinander inverse, inklusionsumkehrende Bijektionen
|
||||
> zwischen Zwischenkörpern und Untergruppen. …
|
||||
> *Hauptsatz der Galoistheorie (Satz 16.3.2).* Es sei $`L/K` eine
|
||||
Galoiserweiterung mit Galoisgruppe $`G`. Dann sind die Abbildungen
|
||||
$`Z \mapsto \operatorname{Gal}(L/Z)` und
|
||||
$`H \mapsto \operatorname{Fix} H` zueinander inverse,
|
||||
inklusionsumkehrende Bijektionen zwischen Zwischenkörpern und
|
||||
Untergruppen. …
|
||||
|
||||
In Mathlib ist das der ordnungsumkehrende Isomorphismus
|
||||
`IsGalois.intermediateFieldEquivSubgroup`; das „ᵒᵈ“ (_order dual_)
|
||||
in seinem Typ ist genau das „inklusionsumkehrend“ des Skripts. Die
|
||||
Gradaussage `|Gal(L/K)| = [L:K]` für Galoiserweiterungen heißt
|
||||
Gradaussage $`|\operatorname{Gal}(L/K)| = [L:K]` für
|
||||
Galoiserweiterungen heißt
|
||||
`IsGalois.card_aut_eq_finrank`:
|
||||
|
||||
```lean
|
||||
|
||||
+16
-14
@@ -31,7 +31,7 @@ Lean.
|
||||
* Ein Ideal in einem kommutativen Ring `R` ist ein Term
|
||||
`I : Ideal R`. Die beiden Bedingungen aus der Ideal-Definition des
|
||||
Skripts heißen `I.add_mem` (für alle `a, b ∈ I` ist `a+b ∈ I`) und
|
||||
`I.mul_mem_left` (für alle `r ∈ R` und `a ∈ I` ist `r·a ∈ I`);
|
||||
`I.mul_mem_left` (für alle $`r \in R` und $`a \in I` ist $`r \cdot a \in I`);
|
||||
außerdem ist `I.zero_mem : 0 ∈ I`.
|
||||
|
||||
* Das von einer Menge `s` erzeugte Ideal ist `Ideal.span s`; ein
|
||||
@@ -46,15 +46,16 @@ Lean.
|
||||
|
||||
Satz 9.3.7 des Skripts, mit Beweis:
|
||||
|
||||
> Es sei `I ⊂ ℤ` ein Ideal und `I ≠ {0}`. Dann gibt es ein
|
||||
> `x ∈ I∖{0}`. Beachte, dass dann auch `−x = (−1)·x` in `I` ist.
|
||||
> Also enthält `I` positive Elemente. Sei `a ∈ I` jetzt das kleinste
|
||||
> positive Element. Wir werden zeigen, dass `I = (a)` ist. Die
|
||||
> Inklusion `(a) ⊆ I` ist klar. Sei `b ∈ I` irgendein positives
|
||||
> Element, dann teilen wir mit Rest: `b = q·a + r` mit `0 ≤ r < a`.
|
||||
> Die Zahl `r` ist jetzt aber in `I`, denn `b` und `q·a` sind in `I`.
|
||||
> Weiter muss wegen der Minimalität von `a` also `r = 0` sein und
|
||||
> somit `b ∈ (a)`.
|
||||
> Es sei $`I \subset \mathbb{Z}` ein Ideal und $`I \neq \{0\}`.
|
||||
Dann gibt es ein $`x \in I \setminus \{0\}`. Beachte, dass dann
|
||||
auch $`-x = (-1) \cdot x` in $`I` ist. Also enthält $`I` positive
|
||||
Elemente. Sei $`a \in I` jetzt das kleinste positive Element. Wir
|
||||
werden zeigen, dass $`I = (a)` ist. Die Inklusion
|
||||
$`(a) \subseteq I` ist klar. Sei $`b \in I` irgendein positives
|
||||
Element, dann teilen wir mit Rest: $`b = q \cdot a + r` mit
|
||||
$`0 \leq r < a`. Die Zahl $`r` ist jetzt aber in $`I`, denn $`b`
|
||||
und $`q \cdot a` sind in $`I`. Weiter muss wegen der Minimalität
|
||||
von $`a` also $`r = 0` sein und somit $`b \in (a)`.
|
||||
|
||||
Zwei Anmerkungen zur Übersetzung. Das „kleinste positive Element“
|
||||
liefert das Wohlordnungsprinzip, in Mathlib
|
||||
@@ -133,10 +134,11 @@ aus diesem Kapitel steht in Mathlib: `Polynomial.isNoetherianRing`.
|
||||
|
||||
Das Beispiel „Triviale Ideale“ des Skripts:
|
||||
|
||||
> Wenn `R` ein Körper und `I ⊂ R` ein Ideal ist und `a ∈ I∖{0}`,
|
||||
> dann ist auch jedes andere Körperelement in `I`. Sei nämlich
|
||||
> irgendein Element `r ∈ R` gegeben. Nach Definition ist
|
||||
> `r = (r·a⁻¹)·a ∈ I`. Also ist `I = R`.
|
||||
> Wenn $`R` ein Körper und $`I \subset R` ein Ideal ist und
|
||||
$`a \in I \setminus \{0\}`, dann ist auch jedes andere
|
||||
Körperelement in $`I`. Sei nämlich irgendein Element $`r \in R`
|
||||
gegeben. Nach Definition ist
|
||||
$`r = (r \cdot a^{-1}) \cdot a \in I`. Also ist $`I = R`.
|
||||
|
||||
Übersetzen Sie dieses Argument: Öffnen Sie
|
||||
`AlgebraInLean/Ideals.lean` und ersetzen Sie das `sorry` durch einen
|
||||
|
||||
+28
-24
@@ -21,16 +21,20 @@ file := "little-fermat"
|
||||
Unser erster Beweis übersetzt Satz 17.5.1 des Skripts. Hier das
|
||||
deutsche Original, Aussage und Beweis:
|
||||
|
||||
> *Satz 17.5.1 (Kleiner Satz von Fermat).* Es sei `p ∈ ℕ` eine Primzahl und es
|
||||
> sei `a ∈ ℤ` irgendeine Zahl. Dann ist `a^p ≡ a (mod p)`.
|
||||
>
|
||||
> _Beweis._ Falls `a` ein Vielfaches von `p` ist, ist die Sache klar.
|
||||
> Ansonsten liefert die Restklasse von `a` ein nicht-verschwindendes
|
||||
> Element `ā ∈ ℤ/(p) = 𝔽_p`, also ein Element der multiplikativen
|
||||
> Gruppe `𝔽_p^*`, welche `p−1` Elemente hat. Nach dem Satz von
|
||||
> Lagrange ist die Ordnung von `ā`, also die Größe der von `ā`
|
||||
> erzeugten Untergruppe, ein Teiler von `|𝔽_p^*| = p−1`. Es gilt also
|
||||
> `ā^(p−1) = 1 ∈ 𝔽_p^*`, oder äquivalent `a^p ≡ a (mod p)`. ∎
|
||||
> *Satz 17.5.1 (Kleiner Satz von Fermat).* Es sei $`p \in \mathbb{N}`
|
||||
eine Primzahl und es sei $`a \in \mathbb{Z}` irgendeine Zahl. Dann
|
||||
ist $`a^p \equiv a \pmod{p}`.
|
||||
|
||||
> _Beweis._ Falls $`a` ein Vielfaches von $`p` ist, ist die Sache
|
||||
klar. Ansonsten liefert die Restklasse von $`a` ein
|
||||
nicht-verschwindendes Element
|
||||
$`\overline{a} \in \mathbb{Z}/(p) = \mathbb{F}_p`, also ein Element
|
||||
der multiplikativen Gruppe $`\mathbb{F}_p^*`, welche $`p-1` Elemente
|
||||
hat. Nach dem Satz von Lagrange ist die Ordnung von
|
||||
$`\overline{a}`, also die Größe der von $`\overline{a}` erzeugten
|
||||
Untergruppe, ein Teiler von $`|\mathbb{F}_p^*| = p-1`. Es gilt also
|
||||
$`\overline{a}^{p-1} = 1 \in \mathbb{F}_p^*`, oder äquivalent
|
||||
$`a^p \equiv a \pmod{p}`. ∎
|
||||
|
||||
Fünf Sätze. Unser Ziel ist ein Lean-Beweis mit genau dieser Struktur —
|
||||
jeder deutsche Satz kehrt als Kommentar über dem Lean-Code wieder, der
|
||||
@@ -41,24 +45,24 @@ ihn umsetzt.
|
||||
Bevor wir den Satz überhaupt formulieren können, müssen wir wissen, wie
|
||||
Mathlib über die beteiligten Objekte spricht.
|
||||
|
||||
* Der Restklassenring `ℤ/(p)` heißt `ZMod p`. Die Restklasse einer
|
||||
ganzen Zahl `a : ℤ` schreibt sich `(a : ZMod p)` — Lean fügt den
|
||||
kanonischen Ringmorphismus `ℤ → ℤ/(p)` automatisch ein (eine
|
||||
_Koerzion_).
|
||||
* Der Restklassenring $`\mathbb{Z}/(p)` heißt `ZMod p`. Die
|
||||
Restklasse einer ganzen Zahl `a : ℤ` schreibt sich `(a : ZMod p)` —
|
||||
Lean fügt den kanonischen Ringmorphismus
|
||||
$`\mathbb{Z} \to \mathbb{Z}/(p)` automatisch ein (eine _Koerzion_).
|
||||
|
||||
* Die Kongruenz `a ≡ b (mod p)` für ganze Zahlen schreibt sich
|
||||
* Die Kongruenz $`a \equiv b \pmod{p}` für ganze Zahlen schreibt sich
|
||||
`a ≡ b [ZMOD p]`.
|
||||
|
||||
* Die multiplikative Gruppe `𝔽_p^*` ist die Gruppe der _Einheiten_ des
|
||||
Rings `ZMod p`, geschrieben `(ZMod p)ˣ`. Eine Einheit
|
||||
`u : (ZMod p)ˣ` kennt ihr Inverses; das zugrundeliegende Ringelement
|
||||
schreibt sich wieder `(u : ZMod p)`.
|
||||
* Die multiplikative Gruppe $`\mathbb{F}_p^*` ist die Gruppe der
|
||||
_Einheiten_ des Rings `ZMod p`, geschrieben `(ZMod p)ˣ`. Eine
|
||||
Einheit `u : (ZMod p)ˣ` kennt ihr Inverses; das zugrundeliegende
|
||||
Ringelement schreibt sich wieder `(u : ZMod p)`.
|
||||
|
||||
* Das Skript sagt „es sei `p` eine Primzahl“. In Lean führen wir die
|
||||
* Das Skript sagt „es sei $`p` eine Primzahl“. In Lean führen wir die
|
||||
Primalität von `p` als Hypothese `hp : p.Prime` mit. Manche
|
||||
Tatsachen — etwa dass `ℤ/(p)` ein Körper ist — findet Leans
|
||||
Automatisierung nur, wenn die Hypothese als _Instanz_ registriert
|
||||
ist; genau das leistet die erste Beweiszeile
|
||||
Tatsachen — etwa dass $`\mathbb{Z}/(p)` ein Körper ist — findet
|
||||
Leans Automatisierung nur, wenn die Hypothese als _Instanz_
|
||||
registriert ist; genau das leistet die erste Beweiszeile
|
||||
`have : Fact p.Prime := ⟨hp⟩`.
|
||||
|
||||
# Der Beweis, Satz für Satz
|
||||
@@ -135,7 +139,7 @@ example (p : ℕ) [Fact p.Prime] (a : ZMod p) : a ^ p = a :=
|
||||
|
||||
Das ist die Bemerkung nach dem Satz im Skript: In Anwendungen
|
||||
verwendet man häufig die äquivalente Formulierung
|
||||
`a^(p−1) ≡ 1 (mod p)` für nicht durch `p` teilbare `a`. Leiten Sie
|
||||
$`a^{p-1} \equiv 1 \pmod{p}` für nicht durch $`p` teilbare $`a`. Leiten Sie
|
||||
sie aus `little_fermat` ab — oder geben Sie einen direkten Beweis nach
|
||||
den Ideen oben. Öffnen Sie `AlgebraInLean/LittleFermat.lean` im
|
||||
Übungs-Repository und ersetzen Sie das `sorry` durch einen Beweis.
|
||||
|
||||
+23
-17
@@ -43,30 +43,34 @@ Polynom-API kennen.
|
||||
|
||||
Satz 7.2.1 des Skripts:
|
||||
|
||||
> *Satz 7.2.1 (Eisenstein-Kriterium).* Es sei `R` ein faktorieller Ring und
|
||||
> es sei `f = a₀ + a₁·x + … + aₙ·xⁿ ∈ R[x]` ein Polynom vom Grad
|
||||
> `n > 0`. Weiter sei `ggT(a₀, …, aₙ) = 1`. Wenn es ein Primelement
|
||||
> `p ∈ R` gibt mit `p|a₀`, `p|a₁`, …, `p|a_{n−1}` und `p² ∤ a₀`, dann
|
||||
> ist `f` irreduzibel in `R[x]`.
|
||||
> *Satz 7.2.1 (Eisenstein-Kriterium).* Es sei $`R` ein faktorieller
|
||||
Ring und es sei
|
||||
$`f = a_0 + a_1 \cdot x + \dots + a_n \cdot x^n \in R[x]` ein
|
||||
Polynom vom Grad $`n > 0`. Weiter sei
|
||||
$`\operatorname{ggT}(a_0, \dots, a_n) = 1`. Wenn es ein
|
||||
Primelement $`p \in R` gibt mit $`p \mid a_0`, $`p \mid a_1`, …,
|
||||
$`p \mid a_{n-1}` und $`p^2 \nmid a_0`, dann ist $`f` irreduzibel
|
||||
in $`R[x]`.
|
||||
|
||||
In Mathlib heißt der Satz
|
||||
`Polynomial.irreducible_of_eisenstein_criterion`. Er ist dort über
|
||||
ein Primideal `P` statt eines Primelements `p` formuliert — für uns
|
||||
ist `P` das Hauptideal `(p)` aus dem letzten Kapitel, und „`p` teilt
|
||||
`aₖ`“ wird zu `f.coeff k ∈ Ideal.span {p}`. Statt „`p ∤ aₙ`“ (was im
|
||||
Skript aus `ggT = 1` folgt) verlangt Mathlib direkt
|
||||
`f.leadingCoeff ∉ P`.
|
||||
ein Primideal `P` statt eines Primelements $`p` formuliert — für uns
|
||||
ist `P` das Hauptideal $`(p)` aus dem letzten Kapitel, und „$`p`
|
||||
teilt $`a_k`“ wird zu `f.coeff k ∈ Ideal.span {p}`. Statt
|
||||
„$`p \nmid a_n`“ (was im Skript aus $`\operatorname{ggT} = 1` folgt)
|
||||
verlangt Mathlib direkt `f.leadingCoeff ∉ P`.
|
||||
|
||||
# Erstes Beispiel: xⁿ − r
|
||||
|
||||
> Das Polynom `xⁿ − r ∈ ℤ[x]` ist irreduzibel, wenn `r` durch eine
|
||||
> Primzahl `p`, aber nicht durch `p²` teilbar ist.
|
||||
> Das Polynom $`x^n - r \in \mathbb{Z}[x]` ist irreduzibel, wenn
|
||||
$`r` durch eine Primzahl $`p`, aber nicht durch $`p^2` teilbar
|
||||
ist.
|
||||
|
||||
Das Skript lässt die Kontrolle der Eisenstein-Bedingungen als
|
||||
Kopfrechnung weg — in Lean führen wir sie aus und lernen dabei die
|
||||
Koeffizienten-API kennen: `coeff_sub`, `coeff_X_pow` und `coeff_C`
|
||||
berechnen die Koeffizienten von `xⁿ − r`, nämlich `aₙ = 1`, `a₀ = −r`
|
||||
und `aₖ = 0` sonst.
|
||||
berechnen die Koeffizienten von $`x^n - r`, nämlich $`a_n = 1`,
|
||||
$`a_0 = -r` und $`a_k = 0` sonst.
|
||||
|
||||
```lean
|
||||
open Polynomial
|
||||
@@ -128,11 +132,13 @@ Körpererweiterungen wieder auf.
|
||||
|
||||
Genau dieses zweite Beispiel ist die Übung:
|
||||
|
||||
> Das Polynom `xⁿ − p ∈ ℤ[x]` ist für jede Primzahl `p` irreduzibel.
|
||||
> Das Polynom $`x^n - p \in \mathbb{Z}[x]` ist für jede Primzahl
|
||||
$`p` irreduzibel.
|
||||
|
||||
Wenden Sie `xn_sub_r_irreduzibel` mit `r = p` an. Zu zeigen bleibt
|
||||
`p² ∤ p`: Aus `p = p²·c` folgt durch Kürzen `1 = p·c` — dann wäre `p`
|
||||
eine Einheit, im Widerspruch zur Primalität. Nützlich sind
|
||||
$`p^2 \nmid p`: Aus $`p = p^2 \cdot c` folgt durch Kürzen
|
||||
$`1 = p \cdot c` — dann wäre $`p` eine Einheit, im Widerspruch zur
|
||||
Primalität. Nützlich sind
|
||||
`mul_left_cancel₀`, `isUnit_of_dvd_one` und `Prime.not_isUnit`.
|
||||
Öffnen Sie `AlgebraInLean/Polynomials.lean` und ersetzen Sie das
|
||||
`sorry` durch einen Beweis.
|
||||
|
||||
+23
-19
@@ -24,8 +24,8 @@ file := "quotients"
|
||||
Dieses Kapitel übersetzt zwei Beweise rund um normale Untergruppen
|
||||
Satz für Satz nach Lean: das Argument aus Erklärvideo 1-1 des
|
||||
Skripts, dass Kerne von Gruppenmorphismen abgeschlossen unter
|
||||
Konjugation sind, und die Beobachtung aus Kapitel 17.3, dass `U·N`
|
||||
für normales `N` wieder eine Untergruppe ist.
|
||||
Konjugation sind, und die Beobachtung aus Kapitel 17.3, dass
|
||||
$`U \cdot N` für normales $`N` wieder eine Untergruppe ist.
|
||||
|
||||
# Das Mathlib-Wörterbuch
|
||||
|
||||
@@ -41,18 +41,19 @@ für normales `N` wieder eine Untergruppe ist.
|
||||
|
||||
* „`N` ist Normalteiler“ ist das Prädikat `N.Normal`; sein Feld
|
||||
`conj_mem` ist wörtlich die Bedingung des Skripts: für alle
|
||||
`n ∈ N` und alle `g` ist `g·n·g⁻¹ ∈ N`.
|
||||
$`n \in N` und alle $`g` ist $`g \cdot n \cdot g^{-1} \in N`.
|
||||
|
||||
# Kerne sind abgeschlossen unter Konjugation
|
||||
|
||||
Aus Erklärvideo 1-1 des Skripts:
|
||||
|
||||
> Die Antwort lautet: Nein! Nicht jede Untergruppe kann Kern eines
|
||||
> Gruppenmorphismus sein. Wenn es nämlich einen Gruppenmorphismus
|
||||
> `φ : G → Q` mit `H = ker(φ)` gibt, dann gilt für alle `h ∈ H` und
|
||||
> alle `g ∈ G` die Gleichung `φ(g·h·g⁻¹) = φ(g)·φ(h)·φ(g)⁻¹ = e`,
|
||||
> wobei `e` das neutrale Element von `Q` bezeichnet. Also ist stets
|
||||
> `g·h·g⁻¹ ∈ H`.
|
||||
Gruppenmorphismus sein. Wenn es nämlich einen Gruppenmorphismus
|
||||
$`\varphi : G \to Q` mit $`H = \ker(\varphi)` gibt, dann gilt für
|
||||
alle $`h \in H` und alle $`g \in G` die Gleichung
|
||||
$`\varphi(g \cdot h \cdot g^{-1}) = \varphi(g) \cdot \varphi(h) \cdot \varphi(g)^{-1} = e`,
|
||||
wobei $`e` das neutrale Element von $`Q` bezeichnet. Also ist
|
||||
stets $`g \cdot h \cdot g^{-1} \in H`.
|
||||
|
||||
```lean
|
||||
theorem kern_konjugation {G Q : Type*} [Group G] [Group Q]
|
||||
@@ -98,17 +99,20 @@ example {G H : Type*} [Group G] [Group H] (N : Subgroup G)
|
||||
|
||||
Beobachtung 17.3.5 des Skripts:
|
||||
|
||||
> Es sei `G` eine Gruppe und es sei `N ⊂ G` eine normale
|
||||
> Untergruppe. Weiter sei `U ⊂ G` irgendeine Untergruppe. Dann ist
|
||||
> `U·N = {u·n : u ∈ U, n ∈ N}` wieder eine Untergruppe. Zum Beweis
|
||||
> müssen wir lediglich zeigen, dass `U·N` abgeschlossen unter der
|
||||
> Gruppenoperation ist. … Wir wissen, dass `N` normal ist. Also
|
||||
> ist `ñ₁ := u₂⁻¹·n₁·u₂ ∈ N` und es gilt `n₁·u₂ = u₂·ñ₁`. Demnach
|
||||
> ist `u₁n₁u₂n₂ = u₁u₂·ñ₁n₂ ∈ U·N`. Fertig ist der Beweis.
|
||||
> Es sei $`G` eine Gruppe und es sei $`N \subset G` eine normale
|
||||
Untergruppe. Weiter sei $`U \subset G` irgendeine Untergruppe.
|
||||
Dann ist $`U \cdot N = \{u \cdot n : u \in U,\ n \in N\}` wieder
|
||||
eine Untergruppe. Zum Beweis müssen wir lediglich zeigen, dass
|
||||
$`U \cdot N` abgeschlossen unter der Gruppenoperation ist. … Wir
|
||||
wissen, dass $`N` normal ist. Also ist
|
||||
$`\widetilde{n_1} := u_2^{-1} \cdot n_1 \cdot u_2 \in N` und es
|
||||
gilt $`n_1 \cdot u_2 = u_2 \cdot \widetilde{n_1}`. Demnach ist
|
||||
$`u_1 n_1 u_2 n_2 = u_1 u_2 \cdot \widetilde{n_1} n_2 \in U \cdot N`.
|
||||
Fertig ist der Beweis.
|
||||
|
||||
Neu ist hier die Taktik `group`: Sie verrechnet Ausdrücke, die
|
||||
allein mit den Gruppenaxiomen ineinander umgeformt werden können.
|
||||
(Das `ñ₁` des Skripts heißt im Code `n₁'`.)
|
||||
(Das $`\widetilde{n_1}` des Skripts heißt im Code `n₁'`.)
|
||||
|
||||
```lean
|
||||
theorem mul_mem_mul {G : Type*} [Group G] (U N : Subgroup G)
|
||||
@@ -136,9 +140,9 @@ theorem mul_mem_mul {G : Type*} [Group G] (U N : Subgroup G)
|
||||
|
||||
Das Skript sagt, es sei „lediglich“ die Abgeschlossenheit unter der
|
||||
Verknüpfung zu zeigen — prüfen Sie den verschwiegenen Teil selbst
|
||||
nach: `U·N` ist auch abgeschlossen unter Inversen. Tipp:
|
||||
`(u·n)⁻¹ = u⁻¹·(u·n⁻¹·u⁻¹)`, und der zweite Faktor liegt nach
|
||||
`conj_mem` in `N`. Öffnen Sie `AlgebraInLean/Quotients.lean` und
|
||||
nach: $`U \cdot N` ist auch abgeschlossen unter Inversen. Tipp:
|
||||
$`(u \cdot n)^{-1} = u^{-1} \cdot (u \cdot n^{-1} \cdot u^{-1})`,
|
||||
und der zweite Faktor liegt nach `conj_mem` in $`N`. Öffnen Sie `AlgebraInLean/Quotients.lean` und
|
||||
ersetzen Sie das `sorry` durch einen Beweis.
|
||||
|
||||
```lean
|
||||
|
||||
+13
-10
@@ -24,8 +24,9 @@ schließt den Kreis zum Anfang des Kurses.
|
||||
|
||||
# Das Mathlib-Wörterbuch
|
||||
|
||||
* Das Legendre-Symbol `(a/p)` heißt `legendreSym p a : ℤ`; es setzt
|
||||
eine `Fact (Nat.Prime p)`-Instanz voraus.
|
||||
* Das Legendre-Symbol $`\left(\tfrac{a}{p}\right)` heißt
|
||||
`legendreSym p a : ℤ`; es setzt eine `Fact (Nat.Prime p)`-Instanz
|
||||
voraus.
|
||||
|
||||
* „`a` ist ein quadratischer Rest modulo `p`“ ist einfach
|
||||
`IsSquare (a : ZMod p)`.
|
||||
@@ -39,14 +40,16 @@ schließt den Kreis zum Anfang des Kurses.
|
||||
|
||||
Satz 24.2.1 des Skripts:
|
||||
|
||||
> *Satz 24.2.1 (Quadratisches Reziprozitätsgesetz).* Es seien `p` und `q`
|
||||
> zwei unterschiedliche, ungerade Primzahlen. Dann gilt die
|
||||
> Gleichung `(p/q)·(q/p) = (−1)^((p−1)/2 · (q−1)/2)`.
|
||||
> *Satz 24.2.1 (Quadratisches Reziprozitätsgesetz).* Es seien $`p`
|
||||
und $`q` zwei unterschiedliche, ungerade Primzahlen. Dann gilt
|
||||
die Gleichung
|
||||
$`\left(\tfrac{p}{q}\right) \cdot \left(\tfrac{q}{p}\right) = (-1)^{\frac{p-1}{2} \cdot \frac{q-1}{2}}`.
|
||||
|
||||
In Mathlib: `legendreSym.quadratic_reciprocity` — dort mit den
|
||||
Exponenten `p / 2` geschrieben, was für ungerade `p` dasselbe ist wie
|
||||
`(p−1)/2`. Dazu die beiden Ergänzungssätze: Der erste —
|
||||
`(−1/p) = 1` genau für `p ≡ 1 (mod 4)` — ist in der
|
||||
Exponenten `p / 2` geschrieben, was für ungerade $`p` dasselbe ist
|
||||
wie $`(p-1)/2`. Dazu die beiden Ergänzungssätze: Der erste —
|
||||
$`\left(\tfrac{-1}{p}\right) = 1` genau für
|
||||
$`p \equiv 1 \pmod{4}` — ist in der
|
||||
Quadrat-Formulierung `ZMod.exists_sq_eq_neg_one_iff`:
|
||||
|
||||
```lean
|
||||
@@ -60,8 +63,8 @@ example (p : ℕ) [Fact p.Prime] (hp : p % 4 ≠ 3) :
|
||||
Aus dem Skript:
|
||||
|
||||
> Ist 7 ein quadratischer Rest modulo 17?
|
||||
> `(7/17) = (17/7)·(−1)^{8·3} = (3/7) = (7/3)·(−1)^{3·1} = −(1/3)
|
||||
> = −1`. Also ist die Antwort: „Nein!“
|
||||
$$`\left(\tfrac{7}{17}\right) = \left(\tfrac{17}{7}\right) \cdot (-1)^{8 \cdot 3} = \left(\tfrac{3}{7}\right) = \left(\tfrac{7}{3}\right) \cdot (-1)^{3 \cdot 1} = -\left(\tfrac{1}{3}\right) = -1.`
|
||||
Also ist die Antwort: „Nein!“
|
||||
|
||||
So rechnet man von Hand — und in der Klausur. In Lean können wir
|
||||
dieselbe Frage der Taktik `decide` übergeben, die das Legendre-Symbol
|
||||
|
||||
@@ -7,6 +7,10 @@ open Verso.Genre.Manual
|
||||
-- The static assets (theme, fonts, KaTeX) follow the setup of
|
||||
-- Peter Pfaffelhuber's course notes, https://github.com/pfaffelh/leancourse.
|
||||
|
||||
-- Kein eigenes KaTeX mehr: Das aktuelle Verso liefert seinen eigenen
|
||||
-- Mathe-Renderer mit (-verso-data/katex/…); eine zweite Kopie würde
|
||||
-- jede Formel doppelt rendern.
|
||||
|
||||
open Verso.Output.Html in
|
||||
def staticCss := {{
|
||||
<link rel="stylesheet" href="/static/colors.css" />
|
||||
@@ -14,45 +18,13 @@ def staticCss := {{
|
||||
<link rel="stylesheet" href="/static/print.css" />
|
||||
<link rel="stylesheet" href="/static/fonts/source-serif/source-serif-text.css" />
|
||||
<link rel="stylesheet" href="/static/fonts/source-code-pro/source-code-pro.css" />
|
||||
<link rel="stylesheet" href="/static/katex/katex.min.css" />
|
||||
}}
|
||||
|
||||
open Verso.Output.Html in
|
||||
def staticJs := {{
|
||||
<script src="/static/katex/katex.min.js"></script>
|
||||
<script src="/static/math.js"></script>
|
||||
<script src="/static/print.js"></script>
|
||||
}}
|
||||
|
||||
def KaTeXLicense : LicenseInfo where
|
||||
identifier := "MIT"
|
||||
dependency := "KaTeX"
|
||||
howUsed := "KaTeX is used to render mathematical notation."
|
||||
link := "https://katex.org/"
|
||||
text := #[(some "The MIT License", text)]
|
||||
where
|
||||
text := r#"
|
||||
Copyright (c) 2013-2020 Khan Academy and other contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
"#
|
||||
|
||||
def main :=
|
||||
manualMain (%doc Book) (config := config)
|
||||
where
|
||||
|
||||
@@ -6,5 +6,4 @@ set -e
|
||||
|
||||
lake build
|
||||
lake exe algebrainlean --output _out
|
||||
mkdir -p public
|
||||
rsync -a --delete _out/html-multi/ public/
|
||||
rsync -arvz --delete _out/html-multi/ kebekus@cplx.vm.uni-freiburg.de:/var/www/storage/algebra-in-lean
|
||||
|
||||
+3
-1
@@ -1 +1,3 @@
|
||||
The directory `katex` contains KaTeX v0.16.11 (MIT license)
|
||||
Theme, Schriften und Druck-Stile nach dem Vorbild von
|
||||
https://github.com/pfaffelh/leancourse.
|
||||
Die Fonts (Source Serif, Source Code Pro) stehen unter der SIL OFL.
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
<h1><a href="https://katex.org/">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://katex.org/img/katex-logo.svg">
|
||||
<img alt="KaTeX" width=130 src="https://katex.org/img/katex-logo-black.svg">
|
||||
</picture>
|
||||
</a></h1>
|
||||
|
||||
[](https://www.npmjs.com/package/katex)
|
||||
[](https://github.com/semantic-release/semantic-release)
|
||||
[](https://github.com/KaTeX/KaTeX/actions?query=workflow%3ACI)
|
||||
[](https://codecov.io/gh/KaTeX/KaTeX)
|
||||
[](https://github.com/KaTeX/KaTeX/discussions)
|
||||
[](https://www.jsdelivr.com/package/npm/katex)
|
||||

|
||||
[](https://gitpod.io/#https://github.com/KaTeX/KaTeX)
|
||||
[](https://opencollective.com/katex)
|
||||
|
||||
KaTeX is a fast, easy-to-use JavaScript library for TeX math rendering on the web.
|
||||
|
||||
* **Fast:** KaTeX renders its math synchronously and doesn't need to reflow the page. See how it compares to a competitor in [this speed test](https://www.intmath.com/cg5/katex-mathjax-comparison.php).
|
||||
* **Print quality:** KaTeX's layout is based on Donald Knuth's TeX, the gold standard for math typesetting.
|
||||
* **Self contained:** KaTeX has no dependencies and can easily be bundled with your website resources.
|
||||
* **Server side rendering:** KaTeX produces the same output regardless of browser or environment, so you can pre-render expressions using Node.js and send them as plain HTML.
|
||||
|
||||
KaTeX is compatible with all major browsers, including Chrome, Safari, Firefox, Opera, Edge, and IE 11.
|
||||
|
||||
KaTeX supports much (but not all) of LaTeX and many LaTeX packages. See the [list of supported functions](https://katex.org/docs/supported.html).
|
||||
|
||||
Try out KaTeX [on the demo page](https://katex.org/#demo)!
|
||||
|
||||
## Getting started
|
||||
|
||||
### Starter template
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<!-- KaTeX requires the use of the HTML5 doctype. Without it, KaTeX may not render properly -->
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
|
||||
|
||||
<!-- The loading of KaTeX is deferred to speed up page rendering -->
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
|
||||
|
||||
<!-- To automatically render math in text elements, include the auto-render extension: -->
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"
|
||||
onload="renderMathInElement(document.body);"></script>
|
||||
</head>
|
||||
...
|
||||
</html>
|
||||
```
|
||||
|
||||
You can also [download KaTeX](https://github.com/KaTeX/KaTeX/releases) and host it yourself.
|
||||
|
||||
For details on how to configure auto-render extension, refer to [the documentation](https://katex.org/docs/autorender.html).
|
||||
|
||||
### API
|
||||
|
||||
Call `katex.render` to render a TeX expression directly into a DOM element.
|
||||
For example:
|
||||
|
||||
```js
|
||||
katex.render("c = \\pm\\sqrt{a^2 + b^2}", element, {
|
||||
throwOnError: false
|
||||
});
|
||||
```
|
||||
|
||||
Call `katex.renderToString` to generate an HTML string of the rendered math,
|
||||
e.g., for server-side rendering. For example:
|
||||
|
||||
```js
|
||||
var html = katex.renderToString("c = \\pm\\sqrt{a^2 + b^2}", {
|
||||
throwOnError: false
|
||||
});
|
||||
// '<span class="katex">...</span>'
|
||||
```
|
||||
|
||||
Make sure to include the CSS and font files in both cases.
|
||||
If you are doing all rendering on the server, there is no need to include the
|
||||
JavaScript on the client.
|
||||
|
||||
The examples above use the `throwOnError: false` option, which renders invalid
|
||||
inputs as the TeX source code in red (by default), with the error message as
|
||||
hover text. For other available options, see the
|
||||
[API documentation](https://katex.org/docs/api.html),
|
||||
[options documentation](https://katex.org/docs/options.html), and
|
||||
[handling errors documentation](https://katex.org/docs/error.html).
|
||||
|
||||
## Demo and Documentation
|
||||
|
||||
Learn more about using KaTeX [on the website](https://katex.org)!
|
||||
|
||||
## Contributors
|
||||
|
||||
### Code Contributors
|
||||
|
||||
This project exists thanks to all the people who contribute code. If you'd like to help, see [our guide to contributing code](CONTRIBUTING.md).
|
||||
<a href="https://github.com/KaTeX/KaTeX/graphs/contributors"><img src="https://contributors-svg.opencollective.com/katex/contributors.svg?width=890&button=false" alt="Code contributors" /></a>
|
||||
|
||||
### Financial Contributors
|
||||
|
||||
Become a financial contributor and help us sustain our community.
|
||||
|
||||
#### Individuals
|
||||
|
||||
<a href="https://opencollective.com/katex"><img src="https://opencollective.com/katex/individuals.svg?width=890" alt="Contribute on Open Collective"></a>
|
||||
|
||||
#### Organizations
|
||||
|
||||
Support this project with your organization. Your logo will show up here with a link to your website.
|
||||
|
||||
<a href="https://opencollective.com/katex/organization/0/website"><img src="https://opencollective.com/katex/organization/0/avatar.svg" alt="Organization 1"></a>
|
||||
<a href="https://opencollective.com/katex/organization/1/website"><img src="https://opencollective.com/katex/organization/1/avatar.svg" alt="Organization 2"></a>
|
||||
<a href="https://opencollective.com/katex/organization/2/website"><img src="https://opencollective.com/katex/organization/2/avatar.svg" alt="Organization 3"></a>
|
||||
<a href="https://opencollective.com/katex/organization/3/website"><img src="https://opencollective.com/katex/organization/3/avatar.svg" alt="Organization 4"></a>
|
||||
<a href="https://opencollective.com/katex/organization/4/website"><img src="https://opencollective.com/katex/organization/4/avatar.svg" alt="Organization 5"></a>
|
||||
<a href="https://opencollective.com/katex/organization/5/website"><img src="https://opencollective.com/katex/organization/5/avatar.svg" alt="Organization 6"></a>
|
||||
<a href="https://opencollective.com/katex/organization/6/website"><img src="https://opencollective.com/katex/organization/6/avatar.svg" alt="Organization 7"></a>
|
||||
<a href="https://opencollective.com/katex/organization/7/website"><img src="https://opencollective.com/katex/organization/7/avatar.svg" alt="Organization 8"></a>
|
||||
<a href="https://opencollective.com/katex/organization/8/website"><img src="https://opencollective.com/katex/organization/8/avatar.svg" alt="Organization 9"></a>
|
||||
<a href="https://opencollective.com/katex/organization/9/website"><img src="https://opencollective.com/katex/organization/9/avatar.svg" alt="Organization 10"></a>
|
||||
|
||||
## License
|
||||
|
||||
KaTeX is licensed under the [MIT License](https://opensource.org/licenses/MIT).
|
||||
@@ -1,341 +0,0 @@
|
||||
(function webpackUniversalModuleDefinition(root, factory) {
|
||||
if(typeof exports === 'object' && typeof module === 'object')
|
||||
module.exports = factory(require("katex"));
|
||||
else if(typeof define === 'function' && define.amd)
|
||||
define(["katex"], factory);
|
||||
else if(typeof exports === 'object')
|
||||
exports["renderMathInElement"] = factory(require("katex"));
|
||||
else
|
||||
root["renderMathInElement"] = factory(root["katex"]);
|
||||
})((typeof self !== 'undefined' ? self : this), function(__WEBPACK_EXTERNAL_MODULE__771__) {
|
||||
return /******/ (function() { // webpackBootstrap
|
||||
/******/ "use strict";
|
||||
/******/ var __webpack_modules__ = ({
|
||||
|
||||
/***/ 771:
|
||||
/***/ (function(module) {
|
||||
|
||||
module.exports = __WEBPACK_EXTERNAL_MODULE__771__;
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
||||
/************************************************************************/
|
||||
/******/ // The module cache
|
||||
/******/ var __webpack_module_cache__ = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/ // Check if module is in cache
|
||||
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
||||
/******/ if (cachedModule !== undefined) {
|
||||
/******/ return cachedModule.exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = __webpack_module_cache__[moduleId] = {
|
||||
/******/ // no module.id needed
|
||||
/******/ // no module.loaded needed
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/************************************************************************/
|
||||
/******/ /* webpack/runtime/compat get default export */
|
||||
/******/ !function() {
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function() { return module['default']; } :
|
||||
/******/ function() { return module; };
|
||||
/******/ __webpack_require__.d(getter, { a: getter });
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/ }();
|
||||
/******/
|
||||
/******/ /* webpack/runtime/define property getters */
|
||||
/******/ !function() {
|
||||
/******/ // define getter functions for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, definition) {
|
||||
/******/ for(var key in definition) {
|
||||
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
|
||||
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
|
||||
/******/ }
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/ }();
|
||||
/******/
|
||||
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
||||
/******/ !function() {
|
||||
/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
|
||||
/******/ }();
|
||||
/******/
|
||||
/************************************************************************/
|
||||
var __webpack_exports__ = {};
|
||||
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
|
||||
!function() {
|
||||
|
||||
// EXPORTS
|
||||
__webpack_require__.d(__webpack_exports__, {
|
||||
"default": function() { return /* binding */ auto_render; }
|
||||
});
|
||||
|
||||
// EXTERNAL MODULE: external "katex"
|
||||
var external_katex_ = __webpack_require__(771);
|
||||
var external_katex_default = /*#__PURE__*/__webpack_require__.n(external_katex_);
|
||||
;// CONCATENATED MODULE: ./contrib/auto-render/splitAtDelimiters.js
|
||||
/* eslint no-constant-condition:0 */
|
||||
const findEndOfMath = function (delimiter, text, startIndex) {
|
||||
// Adapted from
|
||||
// https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx
|
||||
let index = startIndex;
|
||||
let braceLevel = 0;
|
||||
const delimLength = delimiter.length;
|
||||
|
||||
while (index < text.length) {
|
||||
const character = text[index];
|
||||
|
||||
if (braceLevel <= 0 && text.slice(index, index + delimLength) === delimiter) {
|
||||
return index;
|
||||
} else if (character === "\\") {
|
||||
index++;
|
||||
} else if (character === "{") {
|
||||
braceLevel++;
|
||||
} else if (character === "}") {
|
||||
braceLevel--;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
};
|
||||
|
||||
const escapeRegex = function (string) {
|
||||
return string.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
||||
};
|
||||
|
||||
const amsRegex = /^\\begin{/;
|
||||
|
||||
const splitAtDelimiters = function (text, delimiters) {
|
||||
let index;
|
||||
const data = [];
|
||||
const regexLeft = new RegExp("(" + delimiters.map(x => escapeRegex(x.left)).join("|") + ")");
|
||||
|
||||
while (true) {
|
||||
index = text.search(regexLeft);
|
||||
|
||||
if (index === -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (index > 0) {
|
||||
data.push({
|
||||
type: "text",
|
||||
data: text.slice(0, index)
|
||||
});
|
||||
text = text.slice(index); // now text starts with delimiter
|
||||
} // ... so this always succeeds:
|
||||
|
||||
|
||||
const i = delimiters.findIndex(delim => text.startsWith(delim.left));
|
||||
index = findEndOfMath(delimiters[i].right, text, delimiters[i].left.length);
|
||||
|
||||
if (index === -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
const rawData = text.slice(0, index + delimiters[i].right.length);
|
||||
const math = amsRegex.test(rawData) ? rawData : text.slice(delimiters[i].left.length, index);
|
||||
data.push({
|
||||
type: "math",
|
||||
data: math,
|
||||
rawData,
|
||||
display: delimiters[i].display
|
||||
});
|
||||
text = text.slice(index + delimiters[i].right.length);
|
||||
}
|
||||
|
||||
if (text !== "") {
|
||||
data.push({
|
||||
type: "text",
|
||||
data: text
|
||||
});
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
/* harmony default export */ var auto_render_splitAtDelimiters = (splitAtDelimiters);
|
||||
;// CONCATENATED MODULE: ./contrib/auto-render/auto-render.js
|
||||
/* eslint no-console:0 */
|
||||
|
||||
|
||||
/* Note: optionsCopy is mutated by this method. If it is ever exposed in the
|
||||
* API, we should copy it before mutating.
|
||||
*/
|
||||
|
||||
const renderMathInText = function (text, optionsCopy) {
|
||||
const data = auto_render_splitAtDelimiters(text, optionsCopy.delimiters);
|
||||
|
||||
if (data.length === 1 && data[0].type === 'text') {
|
||||
// There is no formula in the text.
|
||||
// Let's return null which means there is no need to replace
|
||||
// the current text node with a new one.
|
||||
return null;
|
||||
}
|
||||
|
||||
const fragment = document.createDocumentFragment();
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (data[i].type === "text") {
|
||||
fragment.appendChild(document.createTextNode(data[i].data));
|
||||
} else {
|
||||
const span = document.createElement("span");
|
||||
let math = data[i].data; // Override any display mode defined in the settings with that
|
||||
// defined by the text itself
|
||||
|
||||
optionsCopy.displayMode = data[i].display;
|
||||
|
||||
try {
|
||||
if (optionsCopy.preProcess) {
|
||||
math = optionsCopy.preProcess(math);
|
||||
}
|
||||
|
||||
external_katex_default().render(math, span, optionsCopy);
|
||||
} catch (e) {
|
||||
if (!(e instanceof (external_katex_default()).ParseError)) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
optionsCopy.errorCallback("KaTeX auto-render: Failed to parse `" + data[i].data + "` with ", e);
|
||||
fragment.appendChild(document.createTextNode(data[i].rawData));
|
||||
continue;
|
||||
}
|
||||
|
||||
fragment.appendChild(span);
|
||||
}
|
||||
}
|
||||
|
||||
return fragment;
|
||||
};
|
||||
|
||||
const renderElem = function (elem, optionsCopy) {
|
||||
for (let i = 0; i < elem.childNodes.length; i++) {
|
||||
const childNode = elem.childNodes[i];
|
||||
|
||||
if (childNode.nodeType === 3) {
|
||||
// Text node
|
||||
// Concatenate all sibling text nodes.
|
||||
// Webkit browsers split very large text nodes into smaller ones,
|
||||
// so the delimiters may be split across different nodes.
|
||||
let textContentConcat = childNode.textContent;
|
||||
let sibling = childNode.nextSibling;
|
||||
let nSiblings = 0;
|
||||
|
||||
while (sibling && sibling.nodeType === Node.TEXT_NODE) {
|
||||
textContentConcat += sibling.textContent;
|
||||
sibling = sibling.nextSibling;
|
||||
nSiblings++;
|
||||
}
|
||||
|
||||
const frag = renderMathInText(textContentConcat, optionsCopy);
|
||||
|
||||
if (frag) {
|
||||
// Remove extra text nodes
|
||||
for (let j = 0; j < nSiblings; j++) {
|
||||
childNode.nextSibling.remove();
|
||||
}
|
||||
|
||||
i += frag.childNodes.length - 1;
|
||||
elem.replaceChild(frag, childNode);
|
||||
} else {
|
||||
// If the concatenated text does not contain math
|
||||
// the siblings will not either
|
||||
i += nSiblings;
|
||||
}
|
||||
} else if (childNode.nodeType === 1) {
|
||||
// Element node
|
||||
const className = ' ' + childNode.className + ' ';
|
||||
const shouldRender = optionsCopy.ignoredTags.indexOf(childNode.nodeName.toLowerCase()) === -1 && optionsCopy.ignoredClasses.every(x => className.indexOf(' ' + x + ' ') === -1);
|
||||
|
||||
if (shouldRender) {
|
||||
renderElem(childNode, optionsCopy);
|
||||
}
|
||||
} // Otherwise, it's something else, and ignore it.
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
const renderMathInElement = function (elem, options) {
|
||||
if (!elem) {
|
||||
throw new Error("No element provided to render");
|
||||
}
|
||||
|
||||
const optionsCopy = {}; // Object.assign(optionsCopy, option)
|
||||
|
||||
for (const option in options) {
|
||||
if (options.hasOwnProperty(option)) {
|
||||
optionsCopy[option] = options[option];
|
||||
}
|
||||
} // default options
|
||||
|
||||
|
||||
optionsCopy.delimiters = optionsCopy.delimiters || [{
|
||||
left: "$$",
|
||||
right: "$$",
|
||||
display: true
|
||||
}, {
|
||||
left: "\\(",
|
||||
right: "\\)",
|
||||
display: false
|
||||
}, // LaTeX uses $…$, but it ruins the display of normal `$` in text:
|
||||
// {left: "$", right: "$", display: false},
|
||||
// $ must come after $$
|
||||
// Render AMS environments even if outside $$…$$ delimiters.
|
||||
{
|
||||
left: "\\begin{equation}",
|
||||
right: "\\end{equation}",
|
||||
display: true
|
||||
}, {
|
||||
left: "\\begin{align}",
|
||||
right: "\\end{align}",
|
||||
display: true
|
||||
}, {
|
||||
left: "\\begin{alignat}",
|
||||
right: "\\end{alignat}",
|
||||
display: true
|
||||
}, {
|
||||
left: "\\begin{gather}",
|
||||
right: "\\end{gather}",
|
||||
display: true
|
||||
}, {
|
||||
left: "\\begin{CD}",
|
||||
right: "\\end{CD}",
|
||||
display: true
|
||||
}, {
|
||||
left: "\\[",
|
||||
right: "\\]",
|
||||
display: true
|
||||
}];
|
||||
optionsCopy.ignoredTags = optionsCopy.ignoredTags || ["script", "noscript", "style", "textarea", "pre", "code", "option"];
|
||||
optionsCopy.ignoredClasses = optionsCopy.ignoredClasses || [];
|
||||
optionsCopy.errorCallback = optionsCopy.errorCallback || console.error; // Enable sharing of global macros defined via `\gdef` between different
|
||||
// math elements within a single call to `renderMathInElement`.
|
||||
|
||||
optionsCopy.macros = optionsCopy.macros || {};
|
||||
renderElem(elem, optionsCopy);
|
||||
};
|
||||
|
||||
/* harmony default export */ var auto_render = (renderMathInElement);
|
||||
}();
|
||||
__webpack_exports__ = __webpack_exports__["default"];
|
||||
/******/ return __webpack_exports__;
|
||||
/******/ })()
|
||||
;
|
||||
});
|
||||
-1
@@ -1 +0,0 @@
|
||||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("katex")):"function"==typeof define&&define.amd?define(["katex"],t):"object"==typeof exports?exports.renderMathInElement=t(require("katex")):e.renderMathInElement=t(e.katex)}("undefined"!=typeof self?self:this,(function(e){return function(){"use strict";var t={771:function(t){t.exports=e}},n={};function r(e){var o=n[e];if(void 0!==o)return o.exports;var i=n[e]={exports:{}};return t[e](i,i.exports,r),i.exports}r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,{a:t}),t},r.d=function(e,t){for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};var o={};return function(){r.d(o,{default:function(){return d}});var e=r(771),t=r.n(e);const n=function(e,t,n){let r=n,o=0;const i=e.length;for(;r<t.length;){const n=t[r];if(o<=0&&t.slice(r,r+i)===e)return r;"\\"===n?r++:"{"===n?o++:"}"===n&&o--,r++}return-1},i=/^\\begin{/;var a=function(e,t){let r;const o=[],a=new RegExp("("+t.map((e=>e.left.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"))).join("|")+")");for(;r=e.search(a),-1!==r;){r>0&&(o.push({type:"text",data:e.slice(0,r)}),e=e.slice(r));const a=t.findIndex((t=>e.startsWith(t.left)));if(r=n(t[a].right,e,t[a].left.length),-1===r)break;const l=e.slice(0,r+t[a].right.length),s=i.test(l)?l:e.slice(t[a].left.length,r);o.push({type:"math",data:s,rawData:l,display:t[a].display}),e=e.slice(r+t[a].right.length)}return""!==e&&o.push({type:"text",data:e}),o};const l=function(e,n){const r=a(e,n.delimiters);if(1===r.length&&"text"===r[0].type)return null;const o=document.createDocumentFragment();for(let e=0;e<r.length;e++)if("text"===r[e].type)o.appendChild(document.createTextNode(r[e].data));else{const i=document.createElement("span");let a=r[e].data;n.displayMode=r[e].display;try{n.preProcess&&(a=n.preProcess(a)),t().render(a,i,n)}catch(i){if(!(i instanceof t().ParseError))throw i;n.errorCallback("KaTeX auto-render: Failed to parse `"+r[e].data+"` with ",i),o.appendChild(document.createTextNode(r[e].rawData));continue}o.appendChild(i)}return o},s=function(e,t){for(let n=0;n<e.childNodes.length;n++){const r=e.childNodes[n];if(3===r.nodeType){let o=r.textContent,i=r.nextSibling,a=0;for(;i&&i.nodeType===Node.TEXT_NODE;)o+=i.textContent,i=i.nextSibling,a++;const s=l(o,t);if(s){for(let e=0;e<a;e++)r.nextSibling.remove();n+=s.childNodes.length-1,e.replaceChild(s,r)}else n+=a}else if(1===r.nodeType){const e=" "+r.className+" ";-1===t.ignoredTags.indexOf(r.nodeName.toLowerCase())&&t.ignoredClasses.every((t=>-1===e.indexOf(" "+t+" ")))&&s(r,t)}}};var d=function(e,t){if(!e)throw new Error("No element provided to render");const n={};for(const e in t)t.hasOwnProperty(e)&&(n[e]=t[e]);n.delimiters=n.delimiters||[{left:"$$",right:"$$",display:!0},{left:"\\(",right:"\\)",display:!1},{left:"\\begin{equation}",right:"\\end{equation}",display:!0},{left:"\\begin{align}",right:"\\end{align}",display:!0},{left:"\\begin{alignat}",right:"\\end{alignat}",display:!0},{left:"\\begin{gather}",right:"\\end{gather}",display:!0},{left:"\\begin{CD}",right:"\\end{CD}",display:!0},{left:"\\[",right:"\\]",display:!0}],n.ignoredTags=n.ignoredTags||["script","noscript","style","textarea","pre","code","option"],n.ignoredClasses=n.ignoredClasses||[],n.errorCallback=n.errorCallback||console.error,n.macros=n.macros||{},s(e,n)}}(),o=o.default}()}));
|
||||
@@ -1,244 +0,0 @@
|
||||
import katex from '../katex.mjs';
|
||||
|
||||
/* eslint no-constant-condition:0 */
|
||||
var findEndOfMath = function findEndOfMath(delimiter, text, startIndex) {
|
||||
// Adapted from
|
||||
// https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx
|
||||
var index = startIndex;
|
||||
var braceLevel = 0;
|
||||
var delimLength = delimiter.length;
|
||||
|
||||
while (index < text.length) {
|
||||
var character = text[index];
|
||||
|
||||
if (braceLevel <= 0 && text.slice(index, index + delimLength) === delimiter) {
|
||||
return index;
|
||||
} else if (character === "\\") {
|
||||
index++;
|
||||
} else if (character === "{") {
|
||||
braceLevel++;
|
||||
} else if (character === "}") {
|
||||
braceLevel--;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
};
|
||||
|
||||
var escapeRegex = function escapeRegex(string) {
|
||||
return string.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
||||
};
|
||||
|
||||
var amsRegex = /^\\begin{/;
|
||||
|
||||
var splitAtDelimiters = function splitAtDelimiters(text, delimiters) {
|
||||
var index;
|
||||
var data = [];
|
||||
var regexLeft = new RegExp("(" + delimiters.map(x => escapeRegex(x.left)).join("|") + ")");
|
||||
|
||||
while (true) {
|
||||
index = text.search(regexLeft);
|
||||
|
||||
if (index === -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (index > 0) {
|
||||
data.push({
|
||||
type: "text",
|
||||
data: text.slice(0, index)
|
||||
});
|
||||
text = text.slice(index); // now text starts with delimiter
|
||||
} // ... so this always succeeds:
|
||||
|
||||
|
||||
var i = delimiters.findIndex(delim => text.startsWith(delim.left));
|
||||
index = findEndOfMath(delimiters[i].right, text, delimiters[i].left.length);
|
||||
|
||||
if (index === -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
var rawData = text.slice(0, index + delimiters[i].right.length);
|
||||
var math = amsRegex.test(rawData) ? rawData : text.slice(delimiters[i].left.length, index);
|
||||
data.push({
|
||||
type: "math",
|
||||
data: math,
|
||||
rawData,
|
||||
display: delimiters[i].display
|
||||
});
|
||||
text = text.slice(index + delimiters[i].right.length);
|
||||
}
|
||||
|
||||
if (text !== "") {
|
||||
data.push({
|
||||
type: "text",
|
||||
data: text
|
||||
});
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
/* eslint no-console:0 */
|
||||
/* Note: optionsCopy is mutated by this method. If it is ever exposed in the
|
||||
* API, we should copy it before mutating.
|
||||
*/
|
||||
|
||||
var renderMathInText = function renderMathInText(text, optionsCopy) {
|
||||
var data = splitAtDelimiters(text, optionsCopy.delimiters);
|
||||
|
||||
if (data.length === 1 && data[0].type === 'text') {
|
||||
// There is no formula in the text.
|
||||
// Let's return null which means there is no need to replace
|
||||
// the current text node with a new one.
|
||||
return null;
|
||||
}
|
||||
|
||||
var fragment = document.createDocumentFragment();
|
||||
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
if (data[i].type === "text") {
|
||||
fragment.appendChild(document.createTextNode(data[i].data));
|
||||
} else {
|
||||
var span = document.createElement("span");
|
||||
var math = data[i].data; // Override any display mode defined in the settings with that
|
||||
// defined by the text itself
|
||||
|
||||
optionsCopy.displayMode = data[i].display;
|
||||
|
||||
try {
|
||||
if (optionsCopy.preProcess) {
|
||||
math = optionsCopy.preProcess(math);
|
||||
}
|
||||
|
||||
katex.render(math, span, optionsCopy);
|
||||
} catch (e) {
|
||||
if (!(e instanceof katex.ParseError)) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
optionsCopy.errorCallback("KaTeX auto-render: Failed to parse `" + data[i].data + "` with ", e);
|
||||
fragment.appendChild(document.createTextNode(data[i].rawData));
|
||||
continue;
|
||||
}
|
||||
|
||||
fragment.appendChild(span);
|
||||
}
|
||||
}
|
||||
|
||||
return fragment;
|
||||
};
|
||||
|
||||
var renderElem = function renderElem(elem, optionsCopy) {
|
||||
for (var i = 0; i < elem.childNodes.length; i++) {
|
||||
var childNode = elem.childNodes[i];
|
||||
|
||||
if (childNode.nodeType === 3) {
|
||||
// Text node
|
||||
// Concatenate all sibling text nodes.
|
||||
// Webkit browsers split very large text nodes into smaller ones,
|
||||
// so the delimiters may be split across different nodes.
|
||||
var textContentConcat = childNode.textContent;
|
||||
var sibling = childNode.nextSibling;
|
||||
var nSiblings = 0;
|
||||
|
||||
while (sibling && sibling.nodeType === Node.TEXT_NODE) {
|
||||
textContentConcat += sibling.textContent;
|
||||
sibling = sibling.nextSibling;
|
||||
nSiblings++;
|
||||
}
|
||||
|
||||
var frag = renderMathInText(textContentConcat, optionsCopy);
|
||||
|
||||
if (frag) {
|
||||
// Remove extra text nodes
|
||||
for (var j = 0; j < nSiblings; j++) {
|
||||
childNode.nextSibling.remove();
|
||||
}
|
||||
|
||||
i += frag.childNodes.length - 1;
|
||||
elem.replaceChild(frag, childNode);
|
||||
} else {
|
||||
// If the concatenated text does not contain math
|
||||
// the siblings will not either
|
||||
i += nSiblings;
|
||||
}
|
||||
} else if (childNode.nodeType === 1) {
|
||||
(function () {
|
||||
// Element node
|
||||
var className = ' ' + childNode.className + ' ';
|
||||
var shouldRender = optionsCopy.ignoredTags.indexOf(childNode.nodeName.toLowerCase()) === -1 && optionsCopy.ignoredClasses.every(x => className.indexOf(' ' + x + ' ') === -1);
|
||||
|
||||
if (shouldRender) {
|
||||
renderElem(childNode, optionsCopy);
|
||||
}
|
||||
})();
|
||||
} // Otherwise, it's something else, and ignore it.
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
var renderMathInElement = function renderMathInElement(elem, options) {
|
||||
if (!elem) {
|
||||
throw new Error("No element provided to render");
|
||||
}
|
||||
|
||||
var optionsCopy = {}; // Object.assign(optionsCopy, option)
|
||||
|
||||
for (var option in options) {
|
||||
if (options.hasOwnProperty(option)) {
|
||||
optionsCopy[option] = options[option];
|
||||
}
|
||||
} // default options
|
||||
|
||||
|
||||
optionsCopy.delimiters = optionsCopy.delimiters || [{
|
||||
left: "$$",
|
||||
right: "$$",
|
||||
display: true
|
||||
}, {
|
||||
left: "\\(",
|
||||
right: "\\)",
|
||||
display: false
|
||||
}, // LaTeX uses $…$, but it ruins the display of normal `$` in text:
|
||||
// {left: "$", right: "$", display: false},
|
||||
// $ must come after $$
|
||||
// Render AMS environments even if outside $$…$$ delimiters.
|
||||
{
|
||||
left: "\\begin{equation}",
|
||||
right: "\\end{equation}",
|
||||
display: true
|
||||
}, {
|
||||
left: "\\begin{align}",
|
||||
right: "\\end{align}",
|
||||
display: true
|
||||
}, {
|
||||
left: "\\begin{alignat}",
|
||||
right: "\\end{alignat}",
|
||||
display: true
|
||||
}, {
|
||||
left: "\\begin{gather}",
|
||||
right: "\\end{gather}",
|
||||
display: true
|
||||
}, {
|
||||
left: "\\begin{CD}",
|
||||
right: "\\end{CD}",
|
||||
display: true
|
||||
}, {
|
||||
left: "\\[",
|
||||
right: "\\]",
|
||||
display: true
|
||||
}];
|
||||
optionsCopy.ignoredTags = optionsCopy.ignoredTags || ["script", "noscript", "style", "textarea", "pre", "code", "option"];
|
||||
optionsCopy.ignoredClasses = optionsCopy.ignoredClasses || [];
|
||||
optionsCopy.errorCallback = optionsCopy.errorCallback || console.error; // Enable sharing of global macros defined via `\gdef` between different
|
||||
// math elements within a single call to `renderMathInElement`.
|
||||
|
||||
optionsCopy.macros = optionsCopy.macros || {};
|
||||
renderElem(elem, optionsCopy);
|
||||
};
|
||||
|
||||
export { renderMathInElement as default };
|
||||
@@ -1,127 +0,0 @@
|
||||
(function webpackUniversalModuleDefinition(root, factory) {
|
||||
if(typeof exports === 'object' && typeof module === 'object')
|
||||
module.exports = factory();
|
||||
else if(typeof define === 'function' && define.amd)
|
||||
define([], factory);
|
||||
else {
|
||||
var a = factory();
|
||||
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
|
||||
}
|
||||
})((typeof self !== 'undefined' ? self : this), function() {
|
||||
return /******/ (function() { // webpackBootstrap
|
||||
/******/ "use strict";
|
||||
var __webpack_exports__ = {};
|
||||
|
||||
;// CONCATENATED MODULE: ./contrib/copy-tex/katex2tex.js
|
||||
// Set these to how you want inline and display math to be delimited.
|
||||
const defaultCopyDelimiters = {
|
||||
inline: ['$', '$'],
|
||||
// alternative: ['\(', '\)']
|
||||
display: ['$$', '$$'] // alternative: ['\[', '\]']
|
||||
|
||||
}; // Replace .katex elements with their TeX source (<annotation> element).
|
||||
// Modifies fragment in-place. Useful for writing your own 'copy' handler,
|
||||
// as in copy-tex.js.
|
||||
|
||||
function katexReplaceWithTex(fragment, copyDelimiters) {
|
||||
if (copyDelimiters === void 0) {
|
||||
copyDelimiters = defaultCopyDelimiters;
|
||||
}
|
||||
|
||||
// Remove .katex-html blocks that are preceded by .katex-mathml blocks
|
||||
// (which will get replaced below).
|
||||
const katexHtml = fragment.querySelectorAll('.katex-mathml + .katex-html');
|
||||
|
||||
for (let i = 0; i < katexHtml.length; i++) {
|
||||
const element = katexHtml[i];
|
||||
|
||||
if (element.remove) {
|
||||
element.remove();
|
||||
} else if (element.parentNode) {
|
||||
element.parentNode.removeChild(element);
|
||||
}
|
||||
} // Replace .katex-mathml elements with their annotation (TeX source)
|
||||
// descendant, with inline delimiters.
|
||||
|
||||
|
||||
const katexMathml = fragment.querySelectorAll('.katex-mathml');
|
||||
|
||||
for (let i = 0; i < katexMathml.length; i++) {
|
||||
const element = katexMathml[i];
|
||||
const texSource = element.querySelector('annotation');
|
||||
|
||||
if (texSource) {
|
||||
if (element.replaceWith) {
|
||||
element.replaceWith(texSource);
|
||||
} else if (element.parentNode) {
|
||||
element.parentNode.replaceChild(texSource, element);
|
||||
}
|
||||
|
||||
texSource.innerHTML = copyDelimiters.inline[0] + texSource.innerHTML + copyDelimiters.inline[1];
|
||||
}
|
||||
} // Switch display math to display delimiters.
|
||||
|
||||
|
||||
const displays = fragment.querySelectorAll('.katex-display annotation');
|
||||
|
||||
for (let i = 0; i < displays.length; i++) {
|
||||
const element = displays[i];
|
||||
element.innerHTML = copyDelimiters.display[0] + element.innerHTML.substr(copyDelimiters.inline[0].length, element.innerHTML.length - copyDelimiters.inline[0].length - copyDelimiters.inline[1].length) + copyDelimiters.display[1];
|
||||
}
|
||||
|
||||
return fragment;
|
||||
}
|
||||
/* harmony default export */ var katex2tex = (katexReplaceWithTex);
|
||||
;// CONCATENATED MODULE: ./contrib/copy-tex/copy-tex.js
|
||||
// Return <div class="katex"> element containing node, or null if not found.
|
||||
|
||||
function closestKatex(node) {
|
||||
// If node is a Text Node, for example, go up to containing Element,
|
||||
// where we can apply the `closest` method.
|
||||
const element = node instanceof Element ? node : node.parentElement;
|
||||
return element && element.closest('.katex');
|
||||
} // Global copy handler to modify behavior on/within .katex elements.
|
||||
|
||||
|
||||
document.addEventListener('copy', function (event) {
|
||||
const selection = window.getSelection();
|
||||
|
||||
if (selection.isCollapsed || !event.clipboardData) {
|
||||
return; // default action OK if selection is empty or unchangeable
|
||||
}
|
||||
|
||||
const clipboardData = event.clipboardData;
|
||||
const range = selection.getRangeAt(0); // When start point is within a formula, expand to entire formula.
|
||||
|
||||
const startKatex = closestKatex(range.startContainer);
|
||||
|
||||
if (startKatex) {
|
||||
range.setStartBefore(startKatex);
|
||||
} // Similarly, when end point is within a formula, expand to entire formula.
|
||||
|
||||
|
||||
const endKatex = closestKatex(range.endContainer);
|
||||
|
||||
if (endKatex) {
|
||||
range.setEndAfter(endKatex);
|
||||
}
|
||||
|
||||
const fragment = range.cloneContents();
|
||||
|
||||
if (!fragment.querySelector('.katex-mathml')) {
|
||||
return; // default action OK if no .katex-mathml elements
|
||||
}
|
||||
|
||||
const htmlContents = Array.prototype.map.call(fragment.childNodes, el => el instanceof Text ? el.textContent : el.outerHTML).join(''); // Preserve usual HTML copy/paste behavior.
|
||||
|
||||
clipboardData.setData('text/html', htmlContents); // Rewrite plain-text version.
|
||||
|
||||
clipboardData.setData('text/plain', katex2tex(fragment).textContent); // Prevent normal copy handling.
|
||||
|
||||
event.preventDefault();
|
||||
});
|
||||
__webpack_exports__ = __webpack_exports__["default"];
|
||||
/******/ return __webpack_exports__;
|
||||
/******/ })()
|
||||
;
|
||||
});
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var n=t();for(var o in n)("object"==typeof exports?exports:e)[o]=n[o]}}("undefined"!=typeof self?self:this,(function(){return function(){"use strict";var e={};const t={inline:["$","$"],display:["$$","$$"]};var n=function(e,n){void 0===n&&(n=t);const o=e.querySelectorAll(".katex-mathml + .katex-html");for(let e=0;e<o.length;e++){const t=o[e];t.remove?t.remove():t.parentNode&&t.parentNode.removeChild(t)}const r=e.querySelectorAll(".katex-mathml");for(let e=0;e<r.length;e++){const t=r[e],o=t.querySelector("annotation");o&&(t.replaceWith?t.replaceWith(o):t.parentNode&&t.parentNode.replaceChild(o,t),o.innerHTML=n.inline[0]+o.innerHTML+n.inline[1])}const l=e.querySelectorAll(".katex-display annotation");for(let e=0;e<l.length;e++){const t=l[e];t.innerHTML=n.display[0]+t.innerHTML.substr(n.inline[0].length,t.innerHTML.length-n.inline[0].length-n.inline[1].length)+n.display[1]}return e};function o(e){const t=e instanceof Element?e:e.parentElement;return t&&t.closest(".katex")}return document.addEventListener("copy",(function(e){const t=window.getSelection();if(t.isCollapsed||!e.clipboardData)return;const r=e.clipboardData,l=t.getRangeAt(0),i=o(l.startContainer);i&&l.setStartBefore(i);const a=o(l.endContainer);a&&l.setEndAfter(a);const s=l.cloneContents();if(!s.querySelector(".katex-mathml"))return;const c=Array.prototype.map.call(s.childNodes,(e=>e instanceof Text?e.textContent:e.outerHTML)).join("");r.setData("text/html",c),r.setData("text/plain",n(s).textContent),e.preventDefault()})),e=e.default}()}));
|
||||
@@ -1,105 +0,0 @@
|
||||
// Set these to how you want inline and display math to be delimited.
|
||||
var defaultCopyDelimiters = {
|
||||
inline: ['$', '$'],
|
||||
// alternative: ['\(', '\)']
|
||||
display: ['$$', '$$'] // alternative: ['\[', '\]']
|
||||
|
||||
}; // Replace .katex elements with their TeX source (<annotation> element).
|
||||
// Modifies fragment in-place. Useful for writing your own 'copy' handler,
|
||||
// as in copy-tex.js.
|
||||
|
||||
function katexReplaceWithTex(fragment, copyDelimiters) {
|
||||
if (copyDelimiters === void 0) {
|
||||
copyDelimiters = defaultCopyDelimiters;
|
||||
}
|
||||
|
||||
// Remove .katex-html blocks that are preceded by .katex-mathml blocks
|
||||
// (which will get replaced below).
|
||||
var katexHtml = fragment.querySelectorAll('.katex-mathml + .katex-html');
|
||||
|
||||
for (var i = 0; i < katexHtml.length; i++) {
|
||||
var element = katexHtml[i];
|
||||
|
||||
if (element.remove) {
|
||||
element.remove();
|
||||
} else if (element.parentNode) {
|
||||
element.parentNode.removeChild(element);
|
||||
}
|
||||
} // Replace .katex-mathml elements with their annotation (TeX source)
|
||||
// descendant, with inline delimiters.
|
||||
|
||||
|
||||
var katexMathml = fragment.querySelectorAll('.katex-mathml');
|
||||
|
||||
for (var _i = 0; _i < katexMathml.length; _i++) {
|
||||
var _element = katexMathml[_i];
|
||||
|
||||
var texSource = _element.querySelector('annotation');
|
||||
|
||||
if (texSource) {
|
||||
if (_element.replaceWith) {
|
||||
_element.replaceWith(texSource);
|
||||
} else if (_element.parentNode) {
|
||||
_element.parentNode.replaceChild(texSource, _element);
|
||||
}
|
||||
|
||||
texSource.innerHTML = copyDelimiters.inline[0] + texSource.innerHTML + copyDelimiters.inline[1];
|
||||
}
|
||||
} // Switch display math to display delimiters.
|
||||
|
||||
|
||||
var displays = fragment.querySelectorAll('.katex-display annotation');
|
||||
|
||||
for (var _i2 = 0; _i2 < displays.length; _i2++) {
|
||||
var _element2 = displays[_i2];
|
||||
_element2.innerHTML = copyDelimiters.display[0] + _element2.innerHTML.substr(copyDelimiters.inline[0].length, _element2.innerHTML.length - copyDelimiters.inline[0].length - copyDelimiters.inline[1].length) + copyDelimiters.display[1];
|
||||
}
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
function closestKatex(node) {
|
||||
// If node is a Text Node, for example, go up to containing Element,
|
||||
// where we can apply the `closest` method.
|
||||
var element = node instanceof Element ? node : node.parentElement;
|
||||
return element && element.closest('.katex');
|
||||
} // Global copy handler to modify behavior on/within .katex elements.
|
||||
|
||||
|
||||
document.addEventListener('copy', function (event) {
|
||||
var selection = window.getSelection();
|
||||
|
||||
if (selection.isCollapsed || !event.clipboardData) {
|
||||
return; // default action OK if selection is empty or unchangeable
|
||||
}
|
||||
|
||||
var clipboardData = event.clipboardData;
|
||||
var range = selection.getRangeAt(0); // When start point is within a formula, expand to entire formula.
|
||||
|
||||
var startKatex = closestKatex(range.startContainer);
|
||||
|
||||
if (startKatex) {
|
||||
range.setStartBefore(startKatex);
|
||||
} // Similarly, when end point is within a formula, expand to entire formula.
|
||||
|
||||
|
||||
var endKatex = closestKatex(range.endContainer);
|
||||
|
||||
if (endKatex) {
|
||||
range.setEndAfter(endKatex);
|
||||
}
|
||||
|
||||
var fragment = range.cloneContents();
|
||||
|
||||
if (!fragment.querySelector('.katex-mathml')) {
|
||||
return; // default action OK if no .katex-mathml elements
|
||||
}
|
||||
|
||||
var htmlContents = Array.prototype.map.call(fragment.childNodes, el => el instanceof Text ? el.textContent : el.outerHTML).join(''); // Preserve usual HTML copy/paste behavior.
|
||||
|
||||
clipboardData.setData('text/html', htmlContents); // Rewrite plain-text version.
|
||||
|
||||
clipboardData.setData('text/plain', katexReplaceWithTex(fragment).textContent); // Prevent normal copy handling.
|
||||
|
||||
event.preventDefault();
|
||||
});
|
||||
@@ -1,112 +0,0 @@
|
||||
(function webpackUniversalModuleDefinition(root, factory) {
|
||||
if(typeof exports === 'object' && typeof module === 'object')
|
||||
module.exports = factory(require("katex"));
|
||||
else if(typeof define === 'function' && define.amd)
|
||||
define(["katex"], factory);
|
||||
else {
|
||||
var a = typeof exports === 'object' ? factory(require("katex")) : factory(root["katex"]);
|
||||
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
|
||||
}
|
||||
})((typeof self !== 'undefined' ? self : this), function(__WEBPACK_EXTERNAL_MODULE__771__) {
|
||||
return /******/ (function() { // webpackBootstrap
|
||||
/******/ "use strict";
|
||||
/******/ var __webpack_modules__ = ({
|
||||
|
||||
/***/ 771:
|
||||
/***/ (function(module) {
|
||||
|
||||
module.exports = __WEBPACK_EXTERNAL_MODULE__771__;
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
||||
/************************************************************************/
|
||||
/******/ // The module cache
|
||||
/******/ var __webpack_module_cache__ = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/ // Check if module is in cache
|
||||
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
||||
/******/ if (cachedModule !== undefined) {
|
||||
/******/ return cachedModule.exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = __webpack_module_cache__[moduleId] = {
|
||||
/******/ // no module.id needed
|
||||
/******/ // no module.loaded needed
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/************************************************************************/
|
||||
/******/ /* webpack/runtime/compat get default export */
|
||||
/******/ !function() {
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function() { return module['default']; } :
|
||||
/******/ function() { return module; };
|
||||
/******/ __webpack_require__.d(getter, { a: getter });
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/ }();
|
||||
/******/
|
||||
/******/ /* webpack/runtime/define property getters */
|
||||
/******/ !function() {
|
||||
/******/ // define getter functions for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, definition) {
|
||||
/******/ for(var key in definition) {
|
||||
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
|
||||
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
|
||||
/******/ }
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/ }();
|
||||
/******/
|
||||
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
||||
/******/ !function() {
|
||||
/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
|
||||
/******/ }();
|
||||
/******/
|
||||
/************************************************************************/
|
||||
var __webpack_exports__ = {};
|
||||
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
|
||||
!function() {
|
||||
/* harmony import */ var katex__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(771);
|
||||
/* harmony import */ var katex__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(katex__WEBPACK_IMPORTED_MODULE_0__);
|
||||
|
||||
let scripts = document.body.getElementsByTagName("script");
|
||||
scripts = Array.prototype.slice.call(scripts);
|
||||
scripts.forEach(function (script) {
|
||||
if (!script.type || !script.type.match(/math\/tex/i)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const display = script.type.match(/mode\s*=\s*display(;|\s|\n|$)/) != null;
|
||||
const katexElement = document.createElement(display ? "div" : "span");
|
||||
katexElement.setAttribute("class", display ? "equation" : "inline-equation");
|
||||
|
||||
try {
|
||||
katex__WEBPACK_IMPORTED_MODULE_0___default().render(script.text, katexElement, {
|
||||
displayMode: display
|
||||
});
|
||||
} catch (err) {
|
||||
//console.error(err); linter doesn't like this
|
||||
katexElement.textContent = script.text;
|
||||
}
|
||||
|
||||
script.parentNode.replaceChild(katexElement, script);
|
||||
});
|
||||
}();
|
||||
__webpack_exports__ = __webpack_exports__["default"];
|
||||
/******/ return __webpack_exports__;
|
||||
/******/ })()
|
||||
;
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t(require("katex"));else if("function"==typeof define&&define.amd)define(["katex"],t);else{var n="object"==typeof exports?t(require("katex")):t(e.katex);for(var r in n)("object"==typeof exports?exports:e)[r]=n[r]}}("undefined"!=typeof self?self:this,(function(e){return function(){"use strict";var t={771:function(t){t.exports=e}},n={};function r(e){var o=n[e];if(void 0!==o)return o.exports;var i=n[e]={exports:{}};return t[e](i,i.exports,r),i.exports}r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,{a:t}),t},r.d=function(e,t){for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};var o={};return function(){var e=r(771),t=r.n(e);let n=document.body.getElementsByTagName("script");n=Array.prototype.slice.call(n),n.forEach((function(e){if(!e.type||!e.type.match(/math\/tex/i))return-1;const n=null!=e.type.match(/mode\s*=\s*display(;|\s|\n|$)/),r=document.createElement(n?"div":"span");r.setAttribute("class",n?"equation":"inline-equation");try{t().render(e.text,r,{displayMode:n})}catch(t){r.textContent=e.text}e.parentNode.replaceChild(r,e)}))}(),o=o.default}()}));
|
||||
@@ -1,24 +0,0 @@
|
||||
import katex from '../katex.mjs';
|
||||
|
||||
var scripts = document.body.getElementsByTagName("script");
|
||||
scripts = Array.prototype.slice.call(scripts);
|
||||
scripts.forEach(function (script) {
|
||||
if (!script.type || !script.type.match(/math\/tex/i)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
var display = script.type.match(/mode\s*=\s*display(;|\s|\n|$)/) != null;
|
||||
var katexElement = document.createElement(display ? "div" : "span");
|
||||
katexElement.setAttribute("class", display ? "equation" : "inline-equation");
|
||||
|
||||
try {
|
||||
katex.render(script.text, katexElement, {
|
||||
displayMode: display
|
||||
});
|
||||
} catch (err) {
|
||||
//console.error(err); linter doesn't like this
|
||||
katexElement.textContent = script.text;
|
||||
}
|
||||
|
||||
script.parentNode.replaceChild(katexElement, script);
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
Vendored
-1
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -1,890 +0,0 @@
|
||||
(function webpackUniversalModuleDefinition(root, factory) {
|
||||
if(typeof exports === 'object' && typeof module === 'object')
|
||||
module.exports = factory(require("katex"));
|
||||
else if(typeof define === 'function' && define.amd)
|
||||
define(["katex"], factory);
|
||||
else {
|
||||
var a = typeof exports === 'object' ? factory(require("katex")) : factory(root["katex"]);
|
||||
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
|
||||
}
|
||||
})((typeof self !== 'undefined' ? self : this), function(__WEBPACK_EXTERNAL_MODULE__771__) {
|
||||
return /******/ (function() { // webpackBootstrap
|
||||
/******/ "use strict";
|
||||
/******/ var __webpack_modules__ = ({
|
||||
|
||||
/***/ 771:
|
||||
/***/ (function(module) {
|
||||
|
||||
module.exports = __WEBPACK_EXTERNAL_MODULE__771__;
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
||||
/************************************************************************/
|
||||
/******/ // The module cache
|
||||
/******/ var __webpack_module_cache__ = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/ // Check if module is in cache
|
||||
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
||||
/******/ if (cachedModule !== undefined) {
|
||||
/******/ return cachedModule.exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = __webpack_module_cache__[moduleId] = {
|
||||
/******/ // no module.id needed
|
||||
/******/ // no module.loaded needed
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/************************************************************************/
|
||||
/******/ /* webpack/runtime/compat get default export */
|
||||
/******/ !function() {
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function() { return module['default']; } :
|
||||
/******/ function() { return module; };
|
||||
/******/ __webpack_require__.d(getter, { a: getter });
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/ }();
|
||||
/******/
|
||||
/******/ /* webpack/runtime/define property getters */
|
||||
/******/ !function() {
|
||||
/******/ // define getter functions for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, definition) {
|
||||
/******/ for(var key in definition) {
|
||||
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
|
||||
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
|
||||
/******/ }
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/ }();
|
||||
/******/
|
||||
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
||||
/******/ !function() {
|
||||
/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
|
||||
/******/ }();
|
||||
/******/
|
||||
/************************************************************************/
|
||||
var __webpack_exports__ = {};
|
||||
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
|
||||
!function() {
|
||||
/* harmony import */ var katex__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(771);
|
||||
/* harmony import */ var katex__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(katex__WEBPACK_IMPORTED_MODULE_0__);
|
||||
/**
|
||||
* renderA11yString returns a readable string.
|
||||
*
|
||||
* In some cases the string will have the proper semantic math
|
||||
* meaning,:
|
||||
* renderA11yString("\\frac{1}{2}"")
|
||||
* -> "start fraction, 1, divided by, 2, end fraction"
|
||||
*
|
||||
* However, other cases do not:
|
||||
* renderA11yString("f(x) = x^2")
|
||||
* -> "f, left parenthesis, x, right parenthesis, equals, x, squared"
|
||||
*
|
||||
* The commas in the string aim to increase ease of understanding
|
||||
* when read by a screenreader.
|
||||
*/
|
||||
// NOTE: since we're importing types here these files won't actually be
|
||||
// included in the build.
|
||||
// $FlowIgnore: we import the types directly anyways
|
||||
|
||||
const stringMap = {
|
||||
"(": "left parenthesis",
|
||||
")": "right parenthesis",
|
||||
"[": "open bracket",
|
||||
"]": "close bracket",
|
||||
"\\{": "left brace",
|
||||
"\\}": "right brace",
|
||||
"\\lvert": "open vertical bar",
|
||||
"\\rvert": "close vertical bar",
|
||||
"|": "vertical bar",
|
||||
"\\uparrow": "up arrow",
|
||||
"\\Uparrow": "up arrow",
|
||||
"\\downarrow": "down arrow",
|
||||
"\\Downarrow": "down arrow",
|
||||
"\\updownarrow": "up down arrow",
|
||||
"\\leftarrow": "left arrow",
|
||||
"\\Leftarrow": "left arrow",
|
||||
"\\rightarrow": "right arrow",
|
||||
"\\Rightarrow": "right arrow",
|
||||
"\\langle": "open angle",
|
||||
"\\rangle": "close angle",
|
||||
"\\lfloor": "open floor",
|
||||
"\\rfloor": "close floor",
|
||||
"\\int": "integral",
|
||||
"\\intop": "integral",
|
||||
"\\lim": "limit",
|
||||
"\\ln": "natural log",
|
||||
"\\log": "log",
|
||||
"\\sin": "sine",
|
||||
"\\cos": "cosine",
|
||||
"\\tan": "tangent",
|
||||
"\\cot": "cotangent",
|
||||
"\\sum": "sum",
|
||||
"/": "slash",
|
||||
",": "comma",
|
||||
".": "point",
|
||||
"-": "negative",
|
||||
"+": "plus",
|
||||
"~": "tilde",
|
||||
":": "colon",
|
||||
"?": "question mark",
|
||||
"'": "apostrophe",
|
||||
"\\%": "percent",
|
||||
" ": "space",
|
||||
"\\ ": "space",
|
||||
"\\$": "dollar sign",
|
||||
"\\angle": "angle",
|
||||
"\\degree": "degree",
|
||||
"\\circ": "circle",
|
||||
"\\vec": "vector",
|
||||
"\\triangle": "triangle",
|
||||
"\\pi": "pi",
|
||||
"\\prime": "prime",
|
||||
"\\infty": "infinity",
|
||||
"\\alpha": "alpha",
|
||||
"\\beta": "beta",
|
||||
"\\gamma": "gamma",
|
||||
"\\omega": "omega",
|
||||
"\\theta": "theta",
|
||||
"\\sigma": "sigma",
|
||||
"\\lambda": "lambda",
|
||||
"\\tau": "tau",
|
||||
"\\Delta": "delta",
|
||||
"\\delta": "delta",
|
||||
"\\mu": "mu",
|
||||
"\\rho": "rho",
|
||||
"\\nabla": "del",
|
||||
"\\ell": "ell",
|
||||
"\\ldots": "dots",
|
||||
// TODO: add entries for all accents
|
||||
"\\hat": "hat",
|
||||
"\\acute": "acute"
|
||||
};
|
||||
const powerMap = {
|
||||
"prime": "prime",
|
||||
"degree": "degrees",
|
||||
"circle": "degrees",
|
||||
"2": "squared",
|
||||
"3": "cubed"
|
||||
};
|
||||
const openMap = {
|
||||
"|": "open vertical bar",
|
||||
".": ""
|
||||
};
|
||||
const closeMap = {
|
||||
"|": "close vertical bar",
|
||||
".": ""
|
||||
};
|
||||
const binMap = {
|
||||
"+": "plus",
|
||||
"-": "minus",
|
||||
"\\pm": "plus minus",
|
||||
"\\cdot": "dot",
|
||||
"*": "times",
|
||||
"/": "divided by",
|
||||
"\\times": "times",
|
||||
"\\div": "divided by",
|
||||
"\\circ": "circle",
|
||||
"\\bullet": "bullet"
|
||||
};
|
||||
const relMap = {
|
||||
"=": "equals",
|
||||
"\\approx": "approximately equals",
|
||||
"≠": "does not equal",
|
||||
"\\geq": "is greater than or equal to",
|
||||
"\\ge": "is greater than or equal to",
|
||||
"\\leq": "is less than or equal to",
|
||||
"\\le": "is less than or equal to",
|
||||
">": "is greater than",
|
||||
"<": "is less than",
|
||||
"\\leftarrow": "left arrow",
|
||||
"\\Leftarrow": "left arrow",
|
||||
"\\rightarrow": "right arrow",
|
||||
"\\Rightarrow": "right arrow",
|
||||
":": "colon"
|
||||
};
|
||||
const accentUnderMap = {
|
||||
"\\underleftarrow": "left arrow",
|
||||
"\\underrightarrow": "right arrow",
|
||||
"\\underleftrightarrow": "left-right arrow",
|
||||
"\\undergroup": "group",
|
||||
"\\underlinesegment": "line segment",
|
||||
"\\utilde": "tilde"
|
||||
};
|
||||
|
||||
const buildString = (str, type, a11yStrings) => {
|
||||
if (!str) {
|
||||
return;
|
||||
}
|
||||
|
||||
let ret;
|
||||
|
||||
if (type === "open") {
|
||||
ret = str in openMap ? openMap[str] : stringMap[str] || str;
|
||||
} else if (type === "close") {
|
||||
ret = str in closeMap ? closeMap[str] : stringMap[str] || str;
|
||||
} else if (type === "bin") {
|
||||
ret = binMap[str] || str;
|
||||
} else if (type === "rel") {
|
||||
ret = relMap[str] || str;
|
||||
} else {
|
||||
ret = stringMap[str] || str;
|
||||
} // If the text to add is a number and there is already a string
|
||||
// in the list and the last string is a number then we should
|
||||
// combine them into a single number
|
||||
|
||||
|
||||
if (/^\d+$/.test(ret) && a11yStrings.length > 0 && // TODO(kevinb): check that the last item in a11yStrings is a string
|
||||
// I think we might be able to drop the nested arrays, which would make
|
||||
// this easier to type
|
||||
// $FlowFixMe
|
||||
/^\d+$/.test(a11yStrings[a11yStrings.length - 1])) {
|
||||
a11yStrings[a11yStrings.length - 1] += ret;
|
||||
} else if (ret) {
|
||||
a11yStrings.push(ret);
|
||||
}
|
||||
};
|
||||
|
||||
const buildRegion = (a11yStrings, callback) => {
|
||||
const regionStrings = [];
|
||||
a11yStrings.push(regionStrings);
|
||||
callback(regionStrings);
|
||||
};
|
||||
|
||||
const handleObject = (tree, a11yStrings, atomType) => {
|
||||
// Everything else is assumed to be an object...
|
||||
switch (tree.type) {
|
||||
case "accent":
|
||||
{
|
||||
buildRegion(a11yStrings, a11yStrings => {
|
||||
buildA11yStrings(tree.base, a11yStrings, atomType);
|
||||
a11yStrings.push("with");
|
||||
buildString(tree.label, "normal", a11yStrings);
|
||||
a11yStrings.push("on top");
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "accentUnder":
|
||||
{
|
||||
buildRegion(a11yStrings, a11yStrings => {
|
||||
buildA11yStrings(tree.base, a11yStrings, atomType);
|
||||
a11yStrings.push("with");
|
||||
buildString(accentUnderMap[tree.label], "normal", a11yStrings);
|
||||
a11yStrings.push("underneath");
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "accent-token":
|
||||
{
|
||||
// Used internally by accent symbols.
|
||||
break;
|
||||
}
|
||||
|
||||
case "atom":
|
||||
{
|
||||
const {
|
||||
text
|
||||
} = tree;
|
||||
|
||||
switch (tree.family) {
|
||||
case "bin":
|
||||
{
|
||||
buildString(text, "bin", a11yStrings);
|
||||
break;
|
||||
}
|
||||
|
||||
case "close":
|
||||
{
|
||||
buildString(text, "close", a11yStrings);
|
||||
break;
|
||||
}
|
||||
// TODO(kevinb): figure out what should be done for inner
|
||||
|
||||
case "inner":
|
||||
{
|
||||
buildString(tree.text, "inner", a11yStrings);
|
||||
break;
|
||||
}
|
||||
|
||||
case "open":
|
||||
{
|
||||
buildString(text, "open", a11yStrings);
|
||||
break;
|
||||
}
|
||||
|
||||
case "punct":
|
||||
{
|
||||
buildString(text, "punct", a11yStrings);
|
||||
break;
|
||||
}
|
||||
|
||||
case "rel":
|
||||
{
|
||||
buildString(text, "rel", a11yStrings);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
tree.family;
|
||||
throw new Error("\"" + tree.family + "\" is not a valid atom type");
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "color":
|
||||
{
|
||||
const color = tree.color.replace(/katex-/, "");
|
||||
buildRegion(a11yStrings, regionStrings => {
|
||||
regionStrings.push("start color " + color);
|
||||
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||
regionStrings.push("end color " + color);
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "color-token":
|
||||
{
|
||||
// Used by \color, \colorbox, and \fcolorbox but not directly rendered.
|
||||
// It's a leaf node and has no children so just break.
|
||||
break;
|
||||
}
|
||||
|
||||
case "delimsizing":
|
||||
{
|
||||
if (tree.delim && tree.delim !== ".") {
|
||||
buildString(tree.delim, "normal", a11yStrings);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "genfrac":
|
||||
{
|
||||
buildRegion(a11yStrings, regionStrings => {
|
||||
// genfrac can have unbalanced delimiters
|
||||
const {
|
||||
leftDelim,
|
||||
rightDelim
|
||||
} = tree; // NOTE: Not sure if this is a safe assumption
|
||||
// hasBarLine true -> fraction, false -> binomial
|
||||
|
||||
if (tree.hasBarLine) {
|
||||
regionStrings.push("start fraction");
|
||||
leftDelim && buildString(leftDelim, "open", regionStrings);
|
||||
buildA11yStrings(tree.numer, regionStrings, atomType);
|
||||
regionStrings.push("divided by");
|
||||
buildA11yStrings(tree.denom, regionStrings, atomType);
|
||||
rightDelim && buildString(rightDelim, "close", regionStrings);
|
||||
regionStrings.push("end fraction");
|
||||
} else {
|
||||
regionStrings.push("start binomial");
|
||||
leftDelim && buildString(leftDelim, "open", regionStrings);
|
||||
buildA11yStrings(tree.numer, regionStrings, atomType);
|
||||
regionStrings.push("over");
|
||||
buildA11yStrings(tree.denom, regionStrings, atomType);
|
||||
rightDelim && buildString(rightDelim, "close", regionStrings);
|
||||
regionStrings.push("end binomial");
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "hbox":
|
||||
{
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "kern":
|
||||
{
|
||||
// No op: we don't attempt to present kerning information
|
||||
// to the screen reader.
|
||||
break;
|
||||
}
|
||||
|
||||
case "leftright":
|
||||
{
|
||||
buildRegion(a11yStrings, regionStrings => {
|
||||
buildString(tree.left, "open", regionStrings);
|
||||
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||
buildString(tree.right, "close", regionStrings);
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "leftright-right":
|
||||
{
|
||||
// TODO: double check that this is a no-op
|
||||
break;
|
||||
}
|
||||
|
||||
case "lap":
|
||||
{
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "mathord":
|
||||
{
|
||||
buildString(tree.text, "normal", a11yStrings);
|
||||
break;
|
||||
}
|
||||
|
||||
case "op":
|
||||
{
|
||||
const {
|
||||
body,
|
||||
name
|
||||
} = tree;
|
||||
|
||||
if (body) {
|
||||
buildA11yStrings(body, a11yStrings, atomType);
|
||||
} else if (name) {
|
||||
buildString(name, "normal", a11yStrings);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "op-token":
|
||||
{
|
||||
// Used internally by operator symbols.
|
||||
buildString(tree.text, atomType, a11yStrings);
|
||||
break;
|
||||
}
|
||||
|
||||
case "ordgroup":
|
||||
{
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "overline":
|
||||
{
|
||||
buildRegion(a11yStrings, function (a11yStrings) {
|
||||
a11yStrings.push("start overline");
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
a11yStrings.push("end overline");
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "pmb":
|
||||
{
|
||||
a11yStrings.push("bold");
|
||||
break;
|
||||
}
|
||||
|
||||
case "phantom":
|
||||
{
|
||||
a11yStrings.push("empty space");
|
||||
break;
|
||||
}
|
||||
|
||||
case "raisebox":
|
||||
{
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "rule":
|
||||
{
|
||||
a11yStrings.push("rectangle");
|
||||
break;
|
||||
}
|
||||
|
||||
case "sizing":
|
||||
{
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "spacing":
|
||||
{
|
||||
a11yStrings.push("space");
|
||||
break;
|
||||
}
|
||||
|
||||
case "styling":
|
||||
{
|
||||
// We ignore the styling and just pass through the contents
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "sqrt":
|
||||
{
|
||||
buildRegion(a11yStrings, regionStrings => {
|
||||
const {
|
||||
body,
|
||||
index
|
||||
} = tree;
|
||||
|
||||
if (index) {
|
||||
const indexString = flatten(buildA11yStrings(index, [], atomType)).join(",");
|
||||
|
||||
if (indexString === "3") {
|
||||
regionStrings.push("cube root of");
|
||||
buildA11yStrings(body, regionStrings, atomType);
|
||||
regionStrings.push("end cube root");
|
||||
return;
|
||||
}
|
||||
|
||||
regionStrings.push("root");
|
||||
regionStrings.push("start index");
|
||||
buildA11yStrings(index, regionStrings, atomType);
|
||||
regionStrings.push("end index");
|
||||
return;
|
||||
}
|
||||
|
||||
regionStrings.push("square root of");
|
||||
buildA11yStrings(body, regionStrings, atomType);
|
||||
regionStrings.push("end square root");
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "supsub":
|
||||
{
|
||||
const {
|
||||
base,
|
||||
sub,
|
||||
sup
|
||||
} = tree;
|
||||
let isLog = false;
|
||||
|
||||
if (base) {
|
||||
buildA11yStrings(base, a11yStrings, atomType);
|
||||
isLog = base.type === "op" && base.name === "\\log";
|
||||
}
|
||||
|
||||
if (sub) {
|
||||
const regionName = isLog ? "base" : "subscript";
|
||||
buildRegion(a11yStrings, function (regionStrings) {
|
||||
regionStrings.push("start " + regionName);
|
||||
buildA11yStrings(sub, regionStrings, atomType);
|
||||
regionStrings.push("end " + regionName);
|
||||
});
|
||||
}
|
||||
|
||||
if (sup) {
|
||||
buildRegion(a11yStrings, function (regionStrings) {
|
||||
const supString = flatten(buildA11yStrings(sup, [], atomType)).join(",");
|
||||
|
||||
if (supString in powerMap) {
|
||||
regionStrings.push(powerMap[supString]);
|
||||
return;
|
||||
}
|
||||
|
||||
regionStrings.push("start superscript");
|
||||
buildA11yStrings(sup, regionStrings, atomType);
|
||||
regionStrings.push("end superscript");
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "text":
|
||||
{
|
||||
// TODO: handle other fonts
|
||||
if (tree.font === "\\textbf") {
|
||||
buildRegion(a11yStrings, function (regionStrings) {
|
||||
regionStrings.push("start bold text");
|
||||
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||
regionStrings.push("end bold text");
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
buildRegion(a11yStrings, function (regionStrings) {
|
||||
regionStrings.push("start text");
|
||||
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||
regionStrings.push("end text");
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "textord":
|
||||
{
|
||||
buildString(tree.text, atomType, a11yStrings);
|
||||
break;
|
||||
}
|
||||
|
||||
case "smash":
|
||||
{
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "enclose":
|
||||
{
|
||||
// TODO: create a map for these.
|
||||
// TODO: differentiate between a body with a single atom, e.g.
|
||||
// "cancel a" instead of "start cancel, a, end cancel"
|
||||
if (/cancel/.test(tree.label)) {
|
||||
buildRegion(a11yStrings, function (regionStrings) {
|
||||
regionStrings.push("start cancel");
|
||||
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||
regionStrings.push("end cancel");
|
||||
});
|
||||
break;
|
||||
} else if (/box/.test(tree.label)) {
|
||||
buildRegion(a11yStrings, function (regionStrings) {
|
||||
regionStrings.push("start box");
|
||||
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||
regionStrings.push("end box");
|
||||
});
|
||||
break;
|
||||
} else if (/sout/.test(tree.label)) {
|
||||
buildRegion(a11yStrings, function (regionStrings) {
|
||||
regionStrings.push("start strikeout");
|
||||
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||
regionStrings.push("end strikeout");
|
||||
});
|
||||
break;
|
||||
} else if (/phase/.test(tree.label)) {
|
||||
buildRegion(a11yStrings, function (regionStrings) {
|
||||
regionStrings.push("start phase angle");
|
||||
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||
regionStrings.push("end phase angle");
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
throw new Error("KaTeX-a11y: enclose node with " + tree.label + " not supported yet");
|
||||
}
|
||||
|
||||
case "vcenter":
|
||||
{
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "vphantom":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: vphantom not implemented yet");
|
||||
}
|
||||
|
||||
case "hphantom":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: hphantom not implemented yet");
|
||||
}
|
||||
|
||||
case "operatorname":
|
||||
{
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "array":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: array not implemented yet");
|
||||
}
|
||||
|
||||
case "raw":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: raw not implemented yet");
|
||||
}
|
||||
|
||||
case "size":
|
||||
{
|
||||
// Although there are nodes of type "size" in the parse tree, they have
|
||||
// no semantic meaning and should be ignored.
|
||||
break;
|
||||
}
|
||||
|
||||
case "url":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: url not implemented yet");
|
||||
}
|
||||
|
||||
case "tag":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: tag not implemented yet");
|
||||
}
|
||||
|
||||
case "verb":
|
||||
{
|
||||
buildString("start verbatim", "normal", a11yStrings);
|
||||
buildString(tree.body, "normal", a11yStrings);
|
||||
buildString("end verbatim", "normal", a11yStrings);
|
||||
break;
|
||||
}
|
||||
|
||||
case "environment":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: environment not implemented yet");
|
||||
}
|
||||
|
||||
case "horizBrace":
|
||||
{
|
||||
buildString("start " + tree.label.slice(1), "normal", a11yStrings);
|
||||
buildA11yStrings(tree.base, a11yStrings, atomType);
|
||||
buildString("end " + tree.label.slice(1), "normal", a11yStrings);
|
||||
break;
|
||||
}
|
||||
|
||||
case "infix":
|
||||
{
|
||||
// All infix nodes are replace with other nodes.
|
||||
break;
|
||||
}
|
||||
|
||||
case "includegraphics":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: includegraphics not implemented yet");
|
||||
}
|
||||
|
||||
case "font":
|
||||
{
|
||||
// TODO: callout the start/end of specific fonts
|
||||
// TODO: map \BBb{N} to "the naturals" or something like that
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "href":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: href not implemented yet");
|
||||
}
|
||||
|
||||
case "cr":
|
||||
{
|
||||
// This is used by environments.
|
||||
throw new Error("KaTeX-a11y: cr not implemented yet");
|
||||
}
|
||||
|
||||
case "underline":
|
||||
{
|
||||
buildRegion(a11yStrings, function (a11yStrings) {
|
||||
a11yStrings.push("start underline");
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
a11yStrings.push("end underline");
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "xArrow":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: xArrow not implemented yet");
|
||||
}
|
||||
|
||||
case "cdlabel":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: cdlabel not implemented yet");
|
||||
}
|
||||
|
||||
case "cdlabelparent":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: cdlabelparent not implemented yet");
|
||||
}
|
||||
|
||||
case "mclass":
|
||||
{
|
||||
// \neq and \ne are macros so we let "htmlmathml" render the mathmal
|
||||
// side of things and extract the text from that.
|
||||
const atomType = tree.mclass.slice(1); // $FlowFixMe: drop the leading "m" from the values in mclass
|
||||
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "mathchoice":
|
||||
{
|
||||
// TODO: track which style we're using, e.g. display, text, etc.
|
||||
// default to text style if even that may not be the correct style
|
||||
buildA11yStrings(tree.text, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "htmlmathml":
|
||||
{
|
||||
buildA11yStrings(tree.mathml, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "middle":
|
||||
{
|
||||
buildString(tree.delim, atomType, a11yStrings);
|
||||
break;
|
||||
}
|
||||
|
||||
case "internal":
|
||||
{
|
||||
// internal nodes are never included in the parse tree
|
||||
break;
|
||||
}
|
||||
|
||||
case "html":
|
||||
{
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
tree.type;
|
||||
throw new Error("KaTeX a11y un-recognized type: " + tree.type);
|
||||
}
|
||||
};
|
||||
|
||||
const buildA11yStrings = function (tree, a11yStrings, atomType) {
|
||||
if (a11yStrings === void 0) {
|
||||
a11yStrings = [];
|
||||
}
|
||||
|
||||
if (tree instanceof Array) {
|
||||
for (let i = 0; i < tree.length; i++) {
|
||||
buildA11yStrings(tree[i], a11yStrings, atomType);
|
||||
}
|
||||
} else {
|
||||
handleObject(tree, a11yStrings, atomType);
|
||||
}
|
||||
|
||||
return a11yStrings;
|
||||
};
|
||||
|
||||
const flatten = function (array) {
|
||||
let result = [];
|
||||
array.forEach(function (item) {
|
||||
if (item instanceof Array) {
|
||||
result = result.concat(flatten(item));
|
||||
} else {
|
||||
result.push(item);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
const renderA11yString = function (text, settings) {
|
||||
const tree = katex__WEBPACK_IMPORTED_MODULE_0___default().__parse(text, settings);
|
||||
|
||||
const a11yStrings = buildA11yStrings(tree, [], "normal");
|
||||
return flatten(a11yStrings).join(", ");
|
||||
};
|
||||
|
||||
/* harmony default export */ __webpack_exports__["default"] = (renderA11yString);
|
||||
}();
|
||||
__webpack_exports__ = __webpack_exports__["default"];
|
||||
/******/ return __webpack_exports__;
|
||||
/******/ })()
|
||||
;
|
||||
});
|
||||
File diff suppressed because one or more lines are too long
@@ -1,800 +0,0 @@
|
||||
import katex from '../katex.mjs';
|
||||
|
||||
/**
|
||||
* renderA11yString returns a readable string.
|
||||
*
|
||||
* In some cases the string will have the proper semantic math
|
||||
* meaning,:
|
||||
* renderA11yString("\\frac{1}{2}"")
|
||||
* -> "start fraction, 1, divided by, 2, end fraction"
|
||||
*
|
||||
* However, other cases do not:
|
||||
* renderA11yString("f(x) = x^2")
|
||||
* -> "f, left parenthesis, x, right parenthesis, equals, x, squared"
|
||||
*
|
||||
* The commas in the string aim to increase ease of understanding
|
||||
* when read by a screenreader.
|
||||
*/
|
||||
var stringMap = {
|
||||
"(": "left parenthesis",
|
||||
")": "right parenthesis",
|
||||
"[": "open bracket",
|
||||
"]": "close bracket",
|
||||
"\\{": "left brace",
|
||||
"\\}": "right brace",
|
||||
"\\lvert": "open vertical bar",
|
||||
"\\rvert": "close vertical bar",
|
||||
"|": "vertical bar",
|
||||
"\\uparrow": "up arrow",
|
||||
"\\Uparrow": "up arrow",
|
||||
"\\downarrow": "down arrow",
|
||||
"\\Downarrow": "down arrow",
|
||||
"\\updownarrow": "up down arrow",
|
||||
"\\leftarrow": "left arrow",
|
||||
"\\Leftarrow": "left arrow",
|
||||
"\\rightarrow": "right arrow",
|
||||
"\\Rightarrow": "right arrow",
|
||||
"\\langle": "open angle",
|
||||
"\\rangle": "close angle",
|
||||
"\\lfloor": "open floor",
|
||||
"\\rfloor": "close floor",
|
||||
"\\int": "integral",
|
||||
"\\intop": "integral",
|
||||
"\\lim": "limit",
|
||||
"\\ln": "natural log",
|
||||
"\\log": "log",
|
||||
"\\sin": "sine",
|
||||
"\\cos": "cosine",
|
||||
"\\tan": "tangent",
|
||||
"\\cot": "cotangent",
|
||||
"\\sum": "sum",
|
||||
"/": "slash",
|
||||
",": "comma",
|
||||
".": "point",
|
||||
"-": "negative",
|
||||
"+": "plus",
|
||||
"~": "tilde",
|
||||
":": "colon",
|
||||
"?": "question mark",
|
||||
"'": "apostrophe",
|
||||
"\\%": "percent",
|
||||
" ": "space",
|
||||
"\\ ": "space",
|
||||
"\\$": "dollar sign",
|
||||
"\\angle": "angle",
|
||||
"\\degree": "degree",
|
||||
"\\circ": "circle",
|
||||
"\\vec": "vector",
|
||||
"\\triangle": "triangle",
|
||||
"\\pi": "pi",
|
||||
"\\prime": "prime",
|
||||
"\\infty": "infinity",
|
||||
"\\alpha": "alpha",
|
||||
"\\beta": "beta",
|
||||
"\\gamma": "gamma",
|
||||
"\\omega": "omega",
|
||||
"\\theta": "theta",
|
||||
"\\sigma": "sigma",
|
||||
"\\lambda": "lambda",
|
||||
"\\tau": "tau",
|
||||
"\\Delta": "delta",
|
||||
"\\delta": "delta",
|
||||
"\\mu": "mu",
|
||||
"\\rho": "rho",
|
||||
"\\nabla": "del",
|
||||
"\\ell": "ell",
|
||||
"\\ldots": "dots",
|
||||
// TODO: add entries for all accents
|
||||
"\\hat": "hat",
|
||||
"\\acute": "acute"
|
||||
};
|
||||
var powerMap = {
|
||||
"prime": "prime",
|
||||
"degree": "degrees",
|
||||
"circle": "degrees",
|
||||
"2": "squared",
|
||||
"3": "cubed"
|
||||
};
|
||||
var openMap = {
|
||||
"|": "open vertical bar",
|
||||
".": ""
|
||||
};
|
||||
var closeMap = {
|
||||
"|": "close vertical bar",
|
||||
".": ""
|
||||
};
|
||||
var binMap = {
|
||||
"+": "plus",
|
||||
"-": "minus",
|
||||
"\\pm": "plus minus",
|
||||
"\\cdot": "dot",
|
||||
"*": "times",
|
||||
"/": "divided by",
|
||||
"\\times": "times",
|
||||
"\\div": "divided by",
|
||||
"\\circ": "circle",
|
||||
"\\bullet": "bullet"
|
||||
};
|
||||
var relMap = {
|
||||
"=": "equals",
|
||||
"\\approx": "approximately equals",
|
||||
"≠": "does not equal",
|
||||
"\\geq": "is greater than or equal to",
|
||||
"\\ge": "is greater than or equal to",
|
||||
"\\leq": "is less than or equal to",
|
||||
"\\le": "is less than or equal to",
|
||||
">": "is greater than",
|
||||
"<": "is less than",
|
||||
"\\leftarrow": "left arrow",
|
||||
"\\Leftarrow": "left arrow",
|
||||
"\\rightarrow": "right arrow",
|
||||
"\\Rightarrow": "right arrow",
|
||||
":": "colon"
|
||||
};
|
||||
var accentUnderMap = {
|
||||
"\\underleftarrow": "left arrow",
|
||||
"\\underrightarrow": "right arrow",
|
||||
"\\underleftrightarrow": "left-right arrow",
|
||||
"\\undergroup": "group",
|
||||
"\\underlinesegment": "line segment",
|
||||
"\\utilde": "tilde"
|
||||
};
|
||||
|
||||
var buildString = (str, type, a11yStrings) => {
|
||||
if (!str) {
|
||||
return;
|
||||
}
|
||||
|
||||
var ret;
|
||||
|
||||
if (type === "open") {
|
||||
ret = str in openMap ? openMap[str] : stringMap[str] || str;
|
||||
} else if (type === "close") {
|
||||
ret = str in closeMap ? closeMap[str] : stringMap[str] || str;
|
||||
} else if (type === "bin") {
|
||||
ret = binMap[str] || str;
|
||||
} else if (type === "rel") {
|
||||
ret = relMap[str] || str;
|
||||
} else {
|
||||
ret = stringMap[str] || str;
|
||||
} // If the text to add is a number and there is already a string
|
||||
// in the list and the last string is a number then we should
|
||||
// combine them into a single number
|
||||
|
||||
|
||||
if (/^\d+$/.test(ret) && a11yStrings.length > 0 && // TODO(kevinb): check that the last item in a11yStrings is a string
|
||||
// I think we might be able to drop the nested arrays, which would make
|
||||
// this easier to type
|
||||
// $FlowFixMe
|
||||
/^\d+$/.test(a11yStrings[a11yStrings.length - 1])) {
|
||||
a11yStrings[a11yStrings.length - 1] += ret;
|
||||
} else if (ret) {
|
||||
a11yStrings.push(ret);
|
||||
}
|
||||
};
|
||||
|
||||
var buildRegion = (a11yStrings, callback) => {
|
||||
var regionStrings = [];
|
||||
a11yStrings.push(regionStrings);
|
||||
callback(regionStrings);
|
||||
};
|
||||
|
||||
var handleObject = (tree, a11yStrings, atomType) => {
|
||||
// Everything else is assumed to be an object...
|
||||
switch (tree.type) {
|
||||
case "accent":
|
||||
{
|
||||
buildRegion(a11yStrings, a11yStrings => {
|
||||
buildA11yStrings(tree.base, a11yStrings, atomType);
|
||||
a11yStrings.push("with");
|
||||
buildString(tree.label, "normal", a11yStrings);
|
||||
a11yStrings.push("on top");
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "accentUnder":
|
||||
{
|
||||
buildRegion(a11yStrings, a11yStrings => {
|
||||
buildA11yStrings(tree.base, a11yStrings, atomType);
|
||||
a11yStrings.push("with");
|
||||
buildString(accentUnderMap[tree.label], "normal", a11yStrings);
|
||||
a11yStrings.push("underneath");
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "accent-token":
|
||||
{
|
||||
// Used internally by accent symbols.
|
||||
break;
|
||||
}
|
||||
|
||||
case "atom":
|
||||
{
|
||||
var {
|
||||
text
|
||||
} = tree;
|
||||
|
||||
switch (tree.family) {
|
||||
case "bin":
|
||||
{
|
||||
buildString(text, "bin", a11yStrings);
|
||||
break;
|
||||
}
|
||||
|
||||
case "close":
|
||||
{
|
||||
buildString(text, "close", a11yStrings);
|
||||
break;
|
||||
}
|
||||
// TODO(kevinb): figure out what should be done for inner
|
||||
|
||||
case "inner":
|
||||
{
|
||||
buildString(tree.text, "inner", a11yStrings);
|
||||
break;
|
||||
}
|
||||
|
||||
case "open":
|
||||
{
|
||||
buildString(text, "open", a11yStrings);
|
||||
break;
|
||||
}
|
||||
|
||||
case "punct":
|
||||
{
|
||||
buildString(text, "punct", a11yStrings);
|
||||
break;
|
||||
}
|
||||
|
||||
case "rel":
|
||||
{
|
||||
buildString(text, "rel", a11yStrings);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
tree.family;
|
||||
throw new Error("\"" + tree.family + "\" is not a valid atom type");
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "color":
|
||||
{
|
||||
var color = tree.color.replace(/katex-/, "");
|
||||
buildRegion(a11yStrings, regionStrings => {
|
||||
regionStrings.push("start color " + color);
|
||||
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||
regionStrings.push("end color " + color);
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "color-token":
|
||||
{
|
||||
// Used by \color, \colorbox, and \fcolorbox but not directly rendered.
|
||||
// It's a leaf node and has no children so just break.
|
||||
break;
|
||||
}
|
||||
|
||||
case "delimsizing":
|
||||
{
|
||||
if (tree.delim && tree.delim !== ".") {
|
||||
buildString(tree.delim, "normal", a11yStrings);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "genfrac":
|
||||
{
|
||||
buildRegion(a11yStrings, regionStrings => {
|
||||
// genfrac can have unbalanced delimiters
|
||||
var {
|
||||
leftDelim,
|
||||
rightDelim
|
||||
} = tree; // NOTE: Not sure if this is a safe assumption
|
||||
// hasBarLine true -> fraction, false -> binomial
|
||||
|
||||
if (tree.hasBarLine) {
|
||||
regionStrings.push("start fraction");
|
||||
leftDelim && buildString(leftDelim, "open", regionStrings);
|
||||
buildA11yStrings(tree.numer, regionStrings, atomType);
|
||||
regionStrings.push("divided by");
|
||||
buildA11yStrings(tree.denom, regionStrings, atomType);
|
||||
rightDelim && buildString(rightDelim, "close", regionStrings);
|
||||
regionStrings.push("end fraction");
|
||||
} else {
|
||||
regionStrings.push("start binomial");
|
||||
leftDelim && buildString(leftDelim, "open", regionStrings);
|
||||
buildA11yStrings(tree.numer, regionStrings, atomType);
|
||||
regionStrings.push("over");
|
||||
buildA11yStrings(tree.denom, regionStrings, atomType);
|
||||
rightDelim && buildString(rightDelim, "close", regionStrings);
|
||||
regionStrings.push("end binomial");
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "hbox":
|
||||
{
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "kern":
|
||||
{
|
||||
// No op: we don't attempt to present kerning information
|
||||
// to the screen reader.
|
||||
break;
|
||||
}
|
||||
|
||||
case "leftright":
|
||||
{
|
||||
buildRegion(a11yStrings, regionStrings => {
|
||||
buildString(tree.left, "open", regionStrings);
|
||||
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||
buildString(tree.right, "close", regionStrings);
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "leftright-right":
|
||||
{
|
||||
// TODO: double check that this is a no-op
|
||||
break;
|
||||
}
|
||||
|
||||
case "lap":
|
||||
{
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "mathord":
|
||||
{
|
||||
buildString(tree.text, "normal", a11yStrings);
|
||||
break;
|
||||
}
|
||||
|
||||
case "op":
|
||||
{
|
||||
var {
|
||||
body,
|
||||
name
|
||||
} = tree;
|
||||
|
||||
if (body) {
|
||||
buildA11yStrings(body, a11yStrings, atomType);
|
||||
} else if (name) {
|
||||
buildString(name, "normal", a11yStrings);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "op-token":
|
||||
{
|
||||
// Used internally by operator symbols.
|
||||
buildString(tree.text, atomType, a11yStrings);
|
||||
break;
|
||||
}
|
||||
|
||||
case "ordgroup":
|
||||
{
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "overline":
|
||||
{
|
||||
buildRegion(a11yStrings, function (a11yStrings) {
|
||||
a11yStrings.push("start overline");
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
a11yStrings.push("end overline");
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "pmb":
|
||||
{
|
||||
a11yStrings.push("bold");
|
||||
break;
|
||||
}
|
||||
|
||||
case "phantom":
|
||||
{
|
||||
a11yStrings.push("empty space");
|
||||
break;
|
||||
}
|
||||
|
||||
case "raisebox":
|
||||
{
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "rule":
|
||||
{
|
||||
a11yStrings.push("rectangle");
|
||||
break;
|
||||
}
|
||||
|
||||
case "sizing":
|
||||
{
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "spacing":
|
||||
{
|
||||
a11yStrings.push("space");
|
||||
break;
|
||||
}
|
||||
|
||||
case "styling":
|
||||
{
|
||||
// We ignore the styling and just pass through the contents
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "sqrt":
|
||||
{
|
||||
buildRegion(a11yStrings, regionStrings => {
|
||||
var {
|
||||
body,
|
||||
index
|
||||
} = tree;
|
||||
|
||||
if (index) {
|
||||
var indexString = flatten(buildA11yStrings(index, [], atomType)).join(",");
|
||||
|
||||
if (indexString === "3") {
|
||||
regionStrings.push("cube root of");
|
||||
buildA11yStrings(body, regionStrings, atomType);
|
||||
regionStrings.push("end cube root");
|
||||
return;
|
||||
}
|
||||
|
||||
regionStrings.push("root");
|
||||
regionStrings.push("start index");
|
||||
buildA11yStrings(index, regionStrings, atomType);
|
||||
regionStrings.push("end index");
|
||||
return;
|
||||
}
|
||||
|
||||
regionStrings.push("square root of");
|
||||
buildA11yStrings(body, regionStrings, atomType);
|
||||
regionStrings.push("end square root");
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "supsub":
|
||||
{
|
||||
var {
|
||||
base,
|
||||
sub,
|
||||
sup
|
||||
} = tree;
|
||||
var isLog = false;
|
||||
|
||||
if (base) {
|
||||
buildA11yStrings(base, a11yStrings, atomType);
|
||||
isLog = base.type === "op" && base.name === "\\log";
|
||||
}
|
||||
|
||||
if (sub) {
|
||||
var regionName = isLog ? "base" : "subscript";
|
||||
buildRegion(a11yStrings, function (regionStrings) {
|
||||
regionStrings.push("start " + regionName);
|
||||
buildA11yStrings(sub, regionStrings, atomType);
|
||||
regionStrings.push("end " + regionName);
|
||||
});
|
||||
}
|
||||
|
||||
if (sup) {
|
||||
buildRegion(a11yStrings, function (regionStrings) {
|
||||
var supString = flatten(buildA11yStrings(sup, [], atomType)).join(",");
|
||||
|
||||
if (supString in powerMap) {
|
||||
regionStrings.push(powerMap[supString]);
|
||||
return;
|
||||
}
|
||||
|
||||
regionStrings.push("start superscript");
|
||||
buildA11yStrings(sup, regionStrings, atomType);
|
||||
regionStrings.push("end superscript");
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "text":
|
||||
{
|
||||
// TODO: handle other fonts
|
||||
if (tree.font === "\\textbf") {
|
||||
buildRegion(a11yStrings, function (regionStrings) {
|
||||
regionStrings.push("start bold text");
|
||||
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||
regionStrings.push("end bold text");
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
buildRegion(a11yStrings, function (regionStrings) {
|
||||
regionStrings.push("start text");
|
||||
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||
regionStrings.push("end text");
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "textord":
|
||||
{
|
||||
buildString(tree.text, atomType, a11yStrings);
|
||||
break;
|
||||
}
|
||||
|
||||
case "smash":
|
||||
{
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "enclose":
|
||||
{
|
||||
// TODO: create a map for these.
|
||||
// TODO: differentiate between a body with a single atom, e.g.
|
||||
// "cancel a" instead of "start cancel, a, end cancel"
|
||||
if (/cancel/.test(tree.label)) {
|
||||
buildRegion(a11yStrings, function (regionStrings) {
|
||||
regionStrings.push("start cancel");
|
||||
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||
regionStrings.push("end cancel");
|
||||
});
|
||||
break;
|
||||
} else if (/box/.test(tree.label)) {
|
||||
buildRegion(a11yStrings, function (regionStrings) {
|
||||
regionStrings.push("start box");
|
||||
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||
regionStrings.push("end box");
|
||||
});
|
||||
break;
|
||||
} else if (/sout/.test(tree.label)) {
|
||||
buildRegion(a11yStrings, function (regionStrings) {
|
||||
regionStrings.push("start strikeout");
|
||||
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||
regionStrings.push("end strikeout");
|
||||
});
|
||||
break;
|
||||
} else if (/phase/.test(tree.label)) {
|
||||
buildRegion(a11yStrings, function (regionStrings) {
|
||||
regionStrings.push("start phase angle");
|
||||
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||
regionStrings.push("end phase angle");
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
throw new Error("KaTeX-a11y: enclose node with " + tree.label + " not supported yet");
|
||||
}
|
||||
|
||||
case "vcenter":
|
||||
{
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "vphantom":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: vphantom not implemented yet");
|
||||
}
|
||||
|
||||
case "hphantom":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: hphantom not implemented yet");
|
||||
}
|
||||
|
||||
case "operatorname":
|
||||
{
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "array":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: array not implemented yet");
|
||||
}
|
||||
|
||||
case "raw":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: raw not implemented yet");
|
||||
}
|
||||
|
||||
case "size":
|
||||
{
|
||||
// Although there are nodes of type "size" in the parse tree, they have
|
||||
// no semantic meaning and should be ignored.
|
||||
break;
|
||||
}
|
||||
|
||||
case "url":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: url not implemented yet");
|
||||
}
|
||||
|
||||
case "tag":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: tag not implemented yet");
|
||||
}
|
||||
|
||||
case "verb":
|
||||
{
|
||||
buildString("start verbatim", "normal", a11yStrings);
|
||||
buildString(tree.body, "normal", a11yStrings);
|
||||
buildString("end verbatim", "normal", a11yStrings);
|
||||
break;
|
||||
}
|
||||
|
||||
case "environment":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: environment not implemented yet");
|
||||
}
|
||||
|
||||
case "horizBrace":
|
||||
{
|
||||
buildString("start " + tree.label.slice(1), "normal", a11yStrings);
|
||||
buildA11yStrings(tree.base, a11yStrings, atomType);
|
||||
buildString("end " + tree.label.slice(1), "normal", a11yStrings);
|
||||
break;
|
||||
}
|
||||
|
||||
case "infix":
|
||||
{
|
||||
// All infix nodes are replace with other nodes.
|
||||
break;
|
||||
}
|
||||
|
||||
case "includegraphics":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: includegraphics not implemented yet");
|
||||
}
|
||||
|
||||
case "font":
|
||||
{
|
||||
// TODO: callout the start/end of specific fonts
|
||||
// TODO: map \BBb{N} to "the naturals" or something like that
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "href":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: href not implemented yet");
|
||||
}
|
||||
|
||||
case "cr":
|
||||
{
|
||||
// This is used by environments.
|
||||
throw new Error("KaTeX-a11y: cr not implemented yet");
|
||||
}
|
||||
|
||||
case "underline":
|
||||
{
|
||||
buildRegion(a11yStrings, function (a11yStrings) {
|
||||
a11yStrings.push("start underline");
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
a11yStrings.push("end underline");
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "xArrow":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: xArrow not implemented yet");
|
||||
}
|
||||
|
||||
case "cdlabel":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: cdlabel not implemented yet");
|
||||
}
|
||||
|
||||
case "cdlabelparent":
|
||||
{
|
||||
throw new Error("KaTeX-a11y: cdlabelparent not implemented yet");
|
||||
}
|
||||
|
||||
case "mclass":
|
||||
{
|
||||
// \neq and \ne are macros so we let "htmlmathml" render the mathmal
|
||||
// side of things and extract the text from that.
|
||||
var _atomType = tree.mclass.slice(1); // $FlowFixMe: drop the leading "m" from the values in mclass
|
||||
|
||||
|
||||
buildA11yStrings(tree.body, a11yStrings, _atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "mathchoice":
|
||||
{
|
||||
// TODO: track which style we're using, e.g. display, text, etc.
|
||||
// default to text style if even that may not be the correct style
|
||||
buildA11yStrings(tree.text, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "htmlmathml":
|
||||
{
|
||||
buildA11yStrings(tree.mathml, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "middle":
|
||||
{
|
||||
buildString(tree.delim, atomType, a11yStrings);
|
||||
break;
|
||||
}
|
||||
|
||||
case "internal":
|
||||
{
|
||||
// internal nodes are never included in the parse tree
|
||||
break;
|
||||
}
|
||||
|
||||
case "html":
|
||||
{
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
tree.type;
|
||||
throw new Error("KaTeX a11y un-recognized type: " + tree.type);
|
||||
}
|
||||
};
|
||||
|
||||
var buildA11yStrings = function buildA11yStrings(tree, a11yStrings, atomType) {
|
||||
if (a11yStrings === void 0) {
|
||||
a11yStrings = [];
|
||||
}
|
||||
|
||||
if (tree instanceof Array) {
|
||||
for (var i = 0; i < tree.length; i++) {
|
||||
buildA11yStrings(tree[i], a11yStrings, atomType);
|
||||
}
|
||||
} else {
|
||||
handleObject(tree, a11yStrings, atomType);
|
||||
}
|
||||
|
||||
return a11yStrings;
|
||||
};
|
||||
|
||||
var flatten = function flatten(array) {
|
||||
var result = [];
|
||||
array.forEach(function (item) {
|
||||
if (item instanceof Array) {
|
||||
result = result.concat(flatten(item));
|
||||
} else {
|
||||
result.push(item);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
var renderA11yString = function renderA11yString(text, settings) {
|
||||
var tree = katex.__parse(text, settings);
|
||||
|
||||
var a11yStrings = buildA11yStrings(tree, [], "normal");
|
||||
return flatten(a11yStrings).join(", ");
|
||||
};
|
||||
|
||||
export { renderA11yString as default };
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
-18998
File diff suppressed because it is too large
Load Diff
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
-1
File diff suppressed because one or more lines are too long
-18458
File diff suppressed because it is too large
Load Diff
@@ -1,8 +0,0 @@
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
for (const m of document.querySelectorAll(".math.inline")) {
|
||||
katex.render(m.textContent, m, {throwOnError: false, displayMode: false});
|
||||
}
|
||||
for (const m of document.querySelectorAll(".math.display")) {
|
||||
katex.render(m.textContent, m, {throwOnError: false, displayMode: true});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user