@@ -0,0 +1,16 @@ | |||
import { defineComponent } from "vue"; | |||
export default defineComponent({ | |||
name: "dj-donate", | |||
setup: () => { | |||
const imgsrc = | |||
"https://img.buymeacoffee.com/button-api/?text=Buy me a coffee&emoji=&slug=djledda&button_colour=FFDD00&font_colour=000000&font_family=Cookie&outline_colour=000000&coffee_colour=ffffff"; | |||
return () => ( | |||
<div class="dj-donate"> | |||
<a href="https://www.buymeacoffee.com/djledda"> | |||
<img src={imgsrc} /> | |||
</a> | |||
</div> | |||
); | |||
}, | |||
}); |
@@ -14,7 +14,7 @@ export default defineComponent({ | |||
} | |||
return () => ( | |||
<a href="#" onClick={clickMail}> | |||
{ slots.default ? slots.default() : 'dan.j.ledda [at] gmail [dot] com' } | |||
{slots.default ? slots.default() : "dan.j.ledda [at] gmail [dot] com"} | |||
</a> | |||
); | |||
}, | |||
@@ -0,0 +1,80 @@ | |||
import { watchEffect, watch, onMounted, type CSSProperties, defineComponent, ref } from "vue"; | |||
const carrierStyle = { | |||
opacity: "0", | |||
display: "block", | |||
pointerEvents: "none", | |||
backgroundColor: "black", | |||
border: "white solid 1px", | |||
color: "white", | |||
padding: "10px", | |||
position: "absolute", | |||
zIndex: "1", | |||
overflow: "hidden", | |||
height: "0", | |||
width: "0", | |||
transition: "opacity 200ms, height 200ms, width 200ms", | |||
} satisfies CSSProperties; | |||
const textCarrierStyle = { | |||
fontSize: '16px', | |||
fontFamily: "Roboto, serif", | |||
display: "block", | |||
overflow: "hidden", | |||
} satisfies CSSProperties; | |||
const defaultWidth = 350; | |||
export default defineComponent({ | |||
name: "dj-sexy-tooltip", | |||
props: { | |||
tooltip: { | |||
type: String, | |||
required: true, | |||
}, | |||
}, | |||
setup(props, { slots, attrs }) { | |||
const active = ref(false); | |||
const carrier = ref<HTMLElement | null>(null); | |||
const textCarrier = ref<HTMLElement | null>(null); | |||
onMounted(() => { | |||
document.addEventListener("mousemove", (event) => { | |||
const pos = { x: event.pageX, y: event.pageY }; | |||
if (carrier.value && getComputedStyle(carrier.value).opacity !== "0") { | |||
if (pos.x + 15 + carrier.value.clientWidth <= document.body.scrollWidth) { | |||
carrier.value.style.left = (pos.x + 15) + "px"; | |||
} else { | |||
carrier.value.style.left = (document.body.scrollWidth - carrier.value.clientWidth - 5) + "px"; | |||
} | |||
if (pos.y + carrier.value.clientHeight <= document.body.scrollHeight) { | |||
carrier.value.style.top = pos.y + "px"; | |||
} else { | |||
carrier.value.style.top = (document.body.scrollHeight - carrier.value.clientHeight - 5) + "px"; | |||
} | |||
} | |||
}); | |||
}); | |||
watchEffect(() => { | |||
if (carrier.value) { | |||
carrier.value.style.height = active.value ? '16px' : '0'; | |||
carrier.value.style.opacity = active.value ? '1' : '0'; | |||
carrier.value.style.width = active.value ? '350px' : '0'; | |||
} | |||
}); | |||
return () => <> | |||
<div class="tooltip-container" {...attrs} | |||
onMouseenter={() => { active.value = true; }} | |||
onMouseleave={() => { active.value = false; }} | |||
> | |||
{slots.default && <slots.default />} | |||
</div> | |||
<div style={carrierStyle} ref={carrier}> | |||
<span style={textCarrierStyle}>{props.tooltip}</span> | |||
</div> | |||
</>; | |||
}, | |||
}); |
@@ -0,0 +1,14 @@ | |||
export type DJAPIEndpoint = "/rp-articles"; | |||
export interface DJAPIResultMap extends Record<DJAPIEndpoint, unknown> { | |||
"/rp-articles": { slug: string; name: string }[]; | |||
} | |||
export type DJAPIResult = DJAPIResultMap[DJAPIEndpoint]; | |||
export default async function getDJAPI<T extends DJAPIEndpoint>( | |||
hostUrl: string, | |||
endpoint: T, | |||
): Promise<DJAPIResultMap[typeof endpoint]> { | |||
return await (await fetch(`${hostUrl}/api${endpoint}`)).json(); | |||
} |
@@ -1,194 +1,7 @@ | |||
import { defineComponent, computed, ref } from 'vue'; | |||
/* | |||
class GrainInput { | |||
static inputs = []; | |||
name; | |||
unit; | |||
mpg; | |||
inputEl; | |||
reference; | |||
* @param {{ | |||
* name: string, | |||
* mpg: number, | |||
* reference: GrainInput | 'self', | |||
* unit: string, | |||
* step?: number, | |||
* }} opts | |||
constructor(opts) { | |||
this.name = opts.name; | |||
this.mpg = opts.mpg; | |||
this.unit = opts.unit; | |||
this.reference = opts.reference === "self" ? this : opts.reference; | |||
this.inputEl = h("input", { type: "number", min: 0, step: opts.step ?? 1 }); | |||
eventBus.addListener(this); | |||
} | |||
attachInput(insertionPoint) { | |||
insertionPoint.appendChild(this.inputEl); | |||
this.inputEl.valueAsNumber = this.mpg; | |||
this.inputEl.addEventListener("input", (e) => { | |||
const newVal = (e.currentTarget)?.valueAsNumber ?? 0; | |||
if (this !== this.reference) { | |||
this.reference.inputEl.valueAsNumber = newVal / this.mpg; | |||
} | |||
eventBus.sendChange(this); | |||
}); | |||
} | |||
onChange() { | |||
if (!this.reference || this === this.reference) return; | |||
this.inputEl.valueAsNumber = this.mpg * this.reference.inputEl.valueAsNumber; | |||
} | |||
getCurrentValue() { | |||
return this.inputEl.valueAsNumber; | |||
} | |||
} | |||
class RatiosController { | |||
t3Ratio = h("input", { type: "number", min: 0, step: 1 }); | |||
t4Ratio = h("input", { type: "number", min: 0, step: 1 }); | |||
t3Syn = h("input", { type: "number", min: 0 }); | |||
t4Syn = h("input", { type: "number", min: 0 }); | |||
t3 = 1; | |||
t4 = 4; | |||
reference; | |||
constructor(referenceInput) { | |||
this.reference = referenceInput; | |||
} | |||
attachInputs(inputs) { | |||
inputs.t3Ratio.replaceWith(this.t3Ratio); | |||
inputs.t4Ratio.replaceWith(this.t4Ratio); | |||
inputs.t3Syn.replaceWith(this.t3Syn); | |||
inputs.t4Syn.replaceWith(this.t4Syn); | |||
this.t3Ratio.addEventListener("input", (e) => { | |||
const newVal = (e.currentTarget)?.valueAsNumber ?? 0; | |||
this.t3 = newVal; | |||
this.onChange(); | |||
}); | |||
this.t4Ratio.addEventListener("input", (e) => { | |||
const newVal = (e.currentTarget)?.valueAsNumber ?? 0; | |||
this.t4 = newVal; | |||
this.onChange(); | |||
}); | |||
this.t3Syn.addEventListener("input", (e) => { | |||
const newVal = (e.currentTarget)?.valueAsNumber ?? 0; | |||
this.reference.inputEl.valueAsNumber = newVal / MPG_T3_SYN + this.t4Syn.valueAsNumber / MPG_T4_SYN; | |||
this.t3 = newVal; | |||
this.t4 = this.t4Syn.valueAsNumber; | |||
this.updateRatio(); | |||
this.updateSyn(); | |||
eventBus.sendChange(this); | |||
}); | |||
this.t4Syn.addEventListener("input", (e) => { | |||
const newVal = (e.currentTarget)?.valueAsNumber ?? 0; | |||
this.reference.inputEl.valueAsNumber = newVal / MPG_T4_SYN + this.t3Syn.valueAsNumber / MPG_T3_SYN; | |||
this.t3 = this.t3Syn.valueAsNumber; | |||
this.t4 = newVal; | |||
this.updateRatio(); | |||
this.updateSyn(); | |||
eventBus.sendChange(this); | |||
}); | |||
eventBus.addListener(this); | |||
this.onChange(); | |||
} | |||
onChange() { | |||
this.updateRatio(); | |||
this.updateSyn(); | |||
} | |||
updateRatio() { | |||
this.t3Ratio.valueAsNumber = this.t3; | |||
this.t4Ratio.valueAsNumber = this.t4; | |||
} | |||
updateSyn() { | |||
const total = this.t3 + this.t4; | |||
const t3Proportion = this.t3 / total; | |||
const t4Proportion = this.t4 / total; | |||
const grainsSyn = t3Proportion / MPG_T3_SYN + t4Proportion / MPG_T4_SYN; | |||
const multiplierSyn = this.reference.getCurrentValue() / grainsSyn; | |||
this.t3Syn.valueAsNumber = t3Proportion * multiplierSyn; | |||
this.t4Syn.valueAsNumber = t4Proportion * multiplierSyn; | |||
} | |||
} | |||
class ThyroidConverter extends DJElement { | |||
static styles = css``; | |||
static template = html; | |||
constructor() { | |||
super(); | |||
const mainGrainInput = new GrainInput({ | |||
name: "Grains", | |||
mpg: 1, | |||
reference: "self", | |||
unit: "", | |||
step: 0.1, | |||
}); | |||
const ratiosController = new RatiosController(mainGrainInput); | |||
const inputs = [ | |||
mainGrainInput, | |||
new GrainInput({ | |||
name: "Armour, Natural Dessicated Thyroid", | |||
mpg: 60, | |||
unit: "mg", | |||
reference: mainGrainInput, | |||
}), | |||
new GrainInput({ | |||
name: 'Liothyronine (Triiodothyronine, "Cytomel", T3)', | |||
mpg: MPG_T3_SYN, | |||
unit: "mcg", | |||
reference: mainGrainInput, | |||
}), | |||
new GrainInput({ | |||
name: 'Levothyroxine (Thyroxine, "Cynoplus", T4)', | |||
mpg: MPG_T4_SYN, | |||
unit: "mcg", | |||
reference: mainGrainInput, | |||
}), | |||
]; | |||
const tableBody = qs("#table-body", this.root); | |||
const compoundedStart = qs("#compounded-start", this.root); | |||
for (const field of inputs) { | |||
const newRow = h("tr", {}, [ | |||
h("td", { textContent: field.name }), | |||
h("td", {}, [ | |||
h("div", { className: "input", style: "display: inline-block;" }), | |||
h("span", { className: "breathe", textContent: field.unit }), | |||
]), | |||
]); | |||
field.attachInput(qs("div.input", newRow)); | |||
tableBody.insertBefore(newRow, compoundedStart); | |||
} | |||
ratiosController.attachInputs({ | |||
t3Ratio: qs(".ratios .t3", tableBody), | |||
t4Ratio: qs(".ratios .t4", tableBody), | |||
t3Syn: qs(".synthetic .t3", tableBody), | |||
t4Syn: qs(".synthetic .t4", tableBody), | |||
}); | |||
} | |||
} | |||
*/ | |||
import { computed, defineComponent, ref } from "vue"; | |||
export default defineComponent({ | |||
name: 'ge-calculator', | |||
name: "ge-calculator", | |||
setup() { | |||
const t3Ratio = ref(1); | |||
const t4Ratio = ref(4); | |||
@@ -209,12 +22,12 @@ export default defineComponent({ | |||
unit: "mg", | |||
}, | |||
{ | |||
name: 'Liothyronine (Triiodothyronine, "Cytomel", T3)', | |||
name: 'Liothyronine (Triiodothyronine, "Cytomel/Cynomel", T3)', | |||
mpg: MPG_T3_SYN, | |||
unit: "mcg", | |||
}, | |||
{ | |||
name: 'Levothyroxine (Thyroxine, "Cynoplus", T4)', | |||
name: "Levothyroxine (Thyroxine, T4)", | |||
mpg: MPG_T4_SYN, | |||
unit: "mcg", | |||
}, | |||
@@ -222,7 +35,6 @@ export default defineComponent({ | |||
const numGrains = ref(2); | |||
const ratio = computed(() => t3Ratio.value + t4Ratio.value); | |||
const compounded = computed(() => { | |||
const total = t3Ratio.value + t4Ratio.value; | |||
const t3Proportion = t3Ratio.value / total; | |||
@@ -235,121 +47,138 @@ export default defineComponent({ | |||
}; | |||
}); | |||
return () => <> | |||
<header> | |||
<h1>Thyroid Calculator</h1> | |||
</header> | |||
<div class="text-slab"> | |||
<p> | |||
Use this calculator to convert between different forms and units of thyroid hormone. Common | |||
synthetic, pure and natural dessicated forms are listed. The last section of the table provides | |||
input fields for a specified ratio of T3 to T4, and will give the dosages required for both the | |||
synthetic and pure forms. All dosages given are based on the "Grains" field at the beginning of the | |||
table, but editing any field will automatically update the rest to keep them in sync. | |||
</p> | |||
</div> | |||
<div class="text-slab ge-calculator"> | |||
<table> | |||
<tbody> | |||
{ inputDefs.map((_, i) => ( | |||
<tr key={_.name}> | |||
return () => ( | |||
<> | |||
<header> | |||
<h1>Thyroid Calculator</h1> | |||
</header> | |||
<div class="text-slab"> | |||
<p> | |||
Use this calculator to convert between different forms and units of thyroid hormone. Common | |||
synthetic, pure and natural dessicated forms are listed. The last section of the table provides | |||
input fields for a specified ratio of T3 to T4, and will give the dosages required for both the | |||
synthetic and pure forms. All dosages given are based on the "Grains" field at the beginning of | |||
the table, but editing any field will automatically update the rest to keep them in sync. | |||
</p> | |||
</div> | |||
<div class="text-slab ge-calculator"> | |||
<table> | |||
<tbody> | |||
{inputDefs.map((_) => ( | |||
<tr key={_.name}> | |||
<td> | |||
{_.name} | |||
</td> | |||
<td class="right"> | |||
<div style="display: inline-block;"> | |||
<input | |||
value={_.name === "Grains" ? numGrains.value : _.mpg * numGrains.value} | |||
onInput={(e) => { | |||
const val = (e.target as HTMLInputElement).valueAsNumber; | |||
if (_.name === "Grains") { | |||
numGrains.value = val; | |||
} else { | |||
numGrains.value = _.mpg / val; | |||
} | |||
}} | |||
min="0" | |||
step={_.step ?? 1} | |||
type="number" | |||
/> | |||
</div> | |||
<span class="breathe">{_.unit}</span> | |||
</td> | |||
</tr> | |||
))} | |||
<tr> | |||
<td colspan="2"> | |||
<strong>Compounded (T3 and T4, "Cynoplus")</strong> | |||
</td> | |||
</tr> | |||
<tr class="ratios"> | |||
<td> | |||
Desired Ratio (T3:T4) | |||
</td> | |||
<td class="right"> | |||
<div> | |||
<input | |||
value={t3Ratio.value} | |||
onInput={(e) => { | |||
t3Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber; | |||
}} | |||
min="0" | |||
step="1" | |||
type="number" | |||
/> | |||
</div>{" "} | |||
:{" "} | |||
<div> | |||
<input | |||
value={t4Ratio.value} | |||
onInput={(e) => { | |||
t4Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber; | |||
}} | |||
min="0" | |||
step="1" | |||
type="number" | |||
/> | |||
</div> | |||
</td> | |||
</tr> | |||
<tr class="synthetic"> | |||
<td> | |||
{ _.name } | |||
Synthetic T3/T4 Combo | |||
</td> | |||
<td class="right"> | |||
<div style="display: inline-block;"> | |||
<input | |||
value={_.name === 'Grains' ? numGrains.value : _.mpg * numGrains.value} | |||
onInput={(e) => { | |||
const val = (e.target as HTMLInputElement).valueAsNumber; | |||
if (_.name === 'Grains') { | |||
numGrains.value = val; | |||
} else { | |||
numGrains.value = _.mpg / val; | |||
} | |||
}} | |||
min="0" | |||
step={ _.step ?? 1 } | |||
type="number" /> | |||
<div> | |||
<input | |||
value={compounded.value.t3Syn} | |||
onInput={(e) => { | |||
t4Ratio.value = compounded.value.t4Syn; | |||
t3Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber; | |||
numGrains.value = t3Ratio.value / MPG_T3_SYN + | |||
t4Ratio.value / MPG_T4_SYN; | |||
}} | |||
min="0" | |||
step="1" | |||
type="number" | |||
/> | |||
</div>{" "} | |||
:{" "} | |||
<div> | |||
<input | |||
value={compounded.value.t4Syn} | |||
onInput={(e) => { | |||
t3Ratio.value = compounded.value.t3Syn; | |||
t4Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber; | |||
numGrains.value = t3Ratio.value / MPG_T3_SYN + | |||
t4Ratio.value / MPG_T4_SYN; | |||
}} | |||
min="0" | |||
step="1" | |||
type="number" | |||
/> | |||
</div> | |||
<span class="breathe">{ _.unit }</span> | |||
</td> | |||
</tr> | |||
))} | |||
<tr><td colspan="2"><strong>Compounded (T3 and T4, "Cynpolus")</strong></td></tr> | |||
<tr class="ratios"> | |||
<td> | |||
Desired Ratio (T3:T4) | |||
</td> | |||
<td class="right"> | |||
<div> | |||
<input | |||
value={t3Ratio.value} | |||
onInput={(e) => { | |||
t3Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber; | |||
}} | |||
min="0" | |||
step="1" | |||
type="number" /> | |||
</div> : <div> | |||
<input | |||
value={t4Ratio.value} | |||
onInput={(e) => { | |||
t4Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber; | |||
}} | |||
min="0" | |||
step="1" | |||
type="number" /> | |||
</div> | |||
</td> | |||
</tr> | |||
<tr class="synthetic"> | |||
<td> | |||
Synthetic T3/T4 Combo | |||
</td> | |||
<td class="right"> | |||
<div> | |||
<input | |||
value={compounded.value.t3Syn} | |||
onInput={(e) => { | |||
t4Ratio.value = compounded.value.t4Syn; | |||
t3Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber; | |||
numGrains.value = t3Ratio.value / MPG_T3_SYN + t4Ratio.value / MPG_T4_SYN; | |||
}} | |||
min="0" | |||
step="1" | |||
type="number" /> | |||
</div> : <div> | |||
<input | |||
value={compounded.value.t4Syn} | |||
onInput={(e) => { | |||
t3Ratio.value = compounded.value.t3Syn; | |||
t4Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber; | |||
numGrains.value = t3Ratio.value / MPG_T3_SYN + t4Ratio.value / MPG_T4_SYN; | |||
}} | |||
min="0" | |||
step="1" | |||
type="number" /> | |||
</div> | |||
</td> | |||
</tr> | |||
</tbody> | |||
</table> | |||
</div> | |||
<div class="text-slab"> | |||
<strong>Changelog:</strong> | |||
<p> | |||
<ul> | |||
<li> | |||
November 2024: Migrated to new web framework and fixed some buggy input. | |||
</li> | |||
<li> | |||
13th March 2024: Removed the synthetic/pure distinction as it was confusing and unnecessary | |||
bloat. | |||
</li> | |||
</ul> | |||
</p> | |||
</div> | |||
</>; | |||
} | |||
</tbody> | |||
</table> | |||
</div> | |||
<div class="text-slab"> | |||
<strong>Changelog:</strong> | |||
<p> | |||
<ul> | |||
<li> | |||
November 2024: Migrated to new web framework and fixed some buggy input. | |||
</li> | |||
<li> | |||
13th March 2024: Removed the synthetic/pure distinction as it was confusing and | |||
unnecessary bloat. | |||
</li> | |||
</ul> | |||
</p> | |||
</div> | |||
</> | |||
); | |||
}, | |||
}); |
@@ -1,61 +1,76 @@ | |||
import { defineComponent } from 'vue'; | |||
import { RouterLink } from 'vue-router'; | |||
import { defineComponent } from "vue"; | |||
import { RouterLink } from "vue-router"; | |||
import DJEmail from "@/DJEmail.tsx"; | |||
import useHead from "@/useHead.ts"; | |||
import useAsyncState from "@/useAsyncState.ts"; | |||
import getDJAPI from "@/api.ts"; | |||
export default defineComponent({ | |||
name: 'ge-deutsch', | |||
setup() { | |||
return () => <> | |||
<header> | |||
<h1>Ray Peat Deutsche Übersetzungen</h1> | |||
</header> | |||
<div class="text-slab"> | |||
<p> | |||
Auf dieser Seite befindet sich eine Auswahl der Artikel von Dr. Raymond Peat, die ich in meiner | |||
Freizeit ins Deutsche übersetzt habe. | |||
</p> | |||
<p> | |||
Ray Peat war ein US-Amerikaner aus Eugene, Oregon, der neben seinem Studium von Linguistik, | |||
Literatur, und Malerei eine Promotion in der Biologie und Physiologie des Alterns und degenerativer | |||
Krankheiten absolvierte, mit besonderem Fokus auf Zellen-Stoffwechsel, Hormone, und | |||
Ernährungsphysiologie. | |||
</p> | |||
<p> | |||
Nach mehreren Jahren als Professor an diversen Universitäten in den USA und Mexiko begann er Artikel | |||
in Form eines Newsletters herauszugeben, von denen mehrere auf <a href="http://raypeat.com/" | |||
>seiner Website</a> zu finden sind. | |||
</p> | |||
<p> | |||
Da er in meinem Leben zu einem enormen Verständnis meines Körpers und einem unglaublichen | |||
Zurückerlangen meiner Gesundheit und meines Wohlbefindens trotz dem Scheitern mehrerer Ärzte | |||
beigetragen hat, und sein Nachlass in Deutschland kaum Publikum aufgrund schwieriger physiologischer | |||
Texte genießt, habe ich entschieden hauptsächlich für Freunde und Bekannte einige seiner Artikel ins | |||
Deutsche zu übersetzen, damit vielleicht auch ein breiteres Publikum von seinen Ideen profitieren | |||
könnte. | |||
</p> | |||
<p> | |||
Falls was bei der Übersetzung auffällt oder besonders unidiomatisch klingt, bzw. der deutschen | |||
Fachsprache der Medizin nicht gerecht sein sollte, kannst du mir unter <DJEmail /> eine Mail senden. | |||
Meine Muttersprache ist schließlich Englisch und ich bin kein professioneller Übersetzer! | |||
</p> | |||
<p> | |||
Zusätzlich zu der Funktion, den Artikel mit dem Button oben in der Originalversion anzusehen, hat | |||
jeder Absatz oben rechts beim drüberhovern mit der Maus einen zusätzlichen Button über den man den | |||
jeweiligen Absatz in die andere Sprache wechseln kann, um die Versionen beim Lesen schnell zu | |||
vergleichen zu können. | |||
</p> | |||
</div> | |||
<div class="text-slab"> | |||
<h3>Artikel auswählen:</h3> | |||
<ul id="article"> | |||
<li> | |||
<RouterLink to={{ name: 'GEDeutschArticle', params: { articleName: 'hypothyroidism'}}}>Indikatoren Schilddrüsenunterfunktion</RouterLink> | |||
</li> | |||
<li> | |||
<RouterLink to={{ name: 'GEDeutschArticle', params: { articleName: 'caffeine'}}}>Koffein</RouterLink> | |||
</li> | |||
</ul> | |||
</div> | |||
</>; | |||
} | |||
name: "ge-deutsch", | |||
async setup() { | |||
useHead({ title: "Ray Peat Artikel auf Deutsch" }); | |||
const { | |||
result: rpArticles, | |||
stateIsReady, | |||
} = useAsyncState("rp-articles", ({ hostUrl }) => getDJAPI(hostUrl, "/rp-articles")); | |||
await stateIsReady; | |||
return () => ( | |||
<> | |||
<header> | |||
<h1>Ray Peat Deutsche Übersetzungen</h1> | |||
</header> | |||
<div class="text-slab"> | |||
<p> | |||
Auf dieser Seite befindet sich eine Auswahl der Artikel von Dr. Raymond Peat, die ich in meiner | |||
Freizeit ins Deutsche übersetzt habe. | |||
</p> | |||
<p> | |||
Ray Peat war ein US-Amerikaner aus Eugene, Oregon, der neben seinem Studium von Linguistik, | |||
Literatur, und Malerei eine Promotion in der Biologie und Physiologie des Alterns und | |||
degenerativer Krankheiten absolvierte, mit besonderem Fokus auf Zellen-Stoffwechsel, Hormone, | |||
und Ernährungsphysiologie. | |||
</p> | |||
<p> | |||
Nach mehreren Jahren als Professor an diversen Universitäten in den USA und Mexiko begann er | |||
Artikel in Form eines Newsletters herauszugeben, von denen mehrere auf{" "} | |||
<a href="http://raypeat.com/">seiner Website</a> zu finden sind. | |||
</p> | |||
<p> | |||
Da er in meinem Leben zu einem enormen Verständnis meines Körpers und einem unglaublichen | |||
Zurückerlangen meiner Gesundheit und meines Wohlbefindens trotz dem Scheitern mehrerer Ärzte | |||
beigetragen hat, und sein Nachlass in Deutschland kaum Publikum aufgrund schwieriger | |||
physiologischer Texte genießt, habe ich entschieden hauptsächlich für Freunde und Bekannte | |||
einige seiner Artikel ins Deutsche zu übersetzen, damit vielleicht auch ein breiteres Publikum | |||
von seinen Ideen profitieren könnte. | |||
</p> | |||
<p> | |||
Falls was bei der Übersetzung auffällt oder besonders unidiomatisch klingt, bzw. der deutschen | |||
Fachsprache der Medizin nicht gerecht sein sollte, kannst du mir unter <DJEmail />{" "} | |||
eine Mail senden. Meine Muttersprache ist schließlich Englisch und ich bin kein professioneller | |||
Übersetzer! | |||
</p> | |||
<p> | |||
Zusätzlich zu der Funktion, den Artikel mit dem Button oben in der Originalversion anzusehen, | |||
hat jeder Absatz oben rechts beim drüberhovern mit der Maus einen zusätzlichen Button über den | |||
man den jeweiligen Absatz in die andere Sprache wechseln kann, um die Versionen beim Lesen | |||
schnell zu vergleichen zu können. | |||
</p> | |||
</div> | |||
<div class="text-slab"> | |||
<h3>Artikel auswählen:</h3> | |||
<ul id="article"> | |||
{rpArticles.value && rpArticles.value.map((_) => ( | |||
<li> | |||
<RouterLink to={{ name: "GEDeutschArticle", params: { articleName: _.slug } }}> | |||
{_.name} | |||
</RouterLink> | |||
</li> | |||
))} | |||
</ul> | |||
</div> | |||
</> | |||
); | |||
}, | |||
}); |
@@ -1,9 +1,10 @@ | |||
import { h, inject, defineComponent, ref, createTextVNode, type VNode } from 'vue'; | |||
import { RouterLink } from 'vue-router'; | |||
import { createTextVNode, defineComponent, h, inject, onServerPrefetch, ref, type VNode, watchEffect } from "vue"; | |||
import { RouterLink } from "vue-router"; | |||
import useAsyncState from "@/useAsyncState.ts"; | |||
import useHead from "@/useHead.ts"; | |||
export default defineComponent({ | |||
name: 'ge-deutsch-article', | |||
name: "ge-deutsch-article", | |||
props: { | |||
articleName: { | |||
type: String, | |||
@@ -17,24 +18,50 @@ export default defineComponent({ | |||
currentLang.value = currentLang.value === "en" ? "de" : "en"; | |||
} | |||
const parseDom = inject('dom-parse', (innerHTML: string) => Object.assign(document.createElement('div'), { innerHTML })); | |||
const parseDom = inject( | |||
"dom-parse", | |||
(innerHTML: string) => Object.assign(document.createElement("div"), { innerHTML }), | |||
); | |||
const { result: articleContent, stateIsReady } = useAsyncState('ge-deutsch-article-data', async ({ hostUrl }) => { | |||
const articleResponse = await fetch(`${ hostUrl }/generative-energy/static/content/${ props.articleName }.html`); | |||
return await articleResponse.text(); | |||
}); | |||
const title = ref(""); | |||
const { result: articleContent, stateIsReady } = useAsyncState( | |||
"ge-deutsch-article-data", | |||
async ({ hostUrl }) => { | |||
const articleResponse = await fetch(`${hostUrl}/generative-energy/content/${props.articleName}.html`); | |||
const result = await articleResponse.text(); | |||
title.value = result.split('<h1 lang="de">')[1].split("</h1>")[0]; | |||
return result; | |||
}, | |||
); | |||
useHead({ title }); | |||
onServerPrefetch(() => | |||
new Promise<void>((res) => { | |||
watchEffect(() => { | |||
if (title.value !== "") { | |||
console.log("resolve", title.value); | |||
res(); | |||
} | |||
}); | |||
}) | |||
); | |||
function transformArticleNode(node: Node): VNode | string { | |||
if (node.nodeType === node.ELEMENT_NODE) { | |||
const el = node as Element; | |||
const attrs: Record<string, string> = {}; | |||
const children = [...node.childNodes].map(_ => transformArticleNode(_)); | |||
const attrs: Record<string, string> = {}; | |||
const children = [...node.childNodes].map((_) => transformArticleNode(_)); | |||
if (el.tagName === 'P') { | |||
el.classList.add('text-slab'); | |||
children.unshift(h('button', { class: 'swap', onClick: (e) => { | |||
e.target.parentElement.classList.toggle('swap'); | |||
} }, '↻')); | |||
if (el.tagName === "P") { | |||
el.classList.add("text-slab"); | |||
children.unshift(h("button", { | |||
class: "swap", | |||
onClick: (e) => { | |||
e.target.parentElement.classList.toggle("swap"); | |||
}, | |||
}, "↻")); | |||
} | |||
for (let i = 0; i < el.attributes.length; i++) { | |||
@@ -46,30 +73,32 @@ export default defineComponent({ | |||
return h((node as Element).tagName, attrs, children); | |||
} else { | |||
return createTextVNode(node.textContent ?? ''); | |||
return createTextVNode(node.textContent ?? ""); | |||
} | |||
} | |||
function ArticleContentTransformed() { | |||
if (articleContent.value) { | |||
const dom = parseDom(articleContent.value); | |||
return h('div', {}, [...dom.children].map(_ => transformArticleNode(_))); | |||
return h("div", {}, [...dom.children].map((_) => transformArticleNode(_))); | |||
} | |||
return <div>Artikel lädt...</div>; | |||
} | |||
await stateIsReady; | |||
return () => <div class="ge-article"> | |||
<div class="header"> | |||
<RouterLink to={{ name: 'GEDeutsch' }}>Zur Artikelübersicht</RouterLink> | |||
<button onClick={clickBtn}> | |||
Sprache auf <span>{currentLang.value === "en" ? "Deutsch" : "Englisch"}</span> umschalten | |||
</button> | |||
return () => ( | |||
<div class="ge-article"> | |||
<div class="header"> | |||
<RouterLink to={{ name: "GEDeutsch" }}>Zur Artikelübersicht</RouterLink> | |||
<button onClick={clickBtn}> | |||
Sprache auf <span>{currentLang.value === "en" ? "Deutsch" : "Englisch"}</span> umschalten | |||
</button> | |||
</div> | |||
<article class={`lang-${currentLang.value}`}> | |||
<ArticleContentTransformed /> | |||
</article> | |||
</div> | |||
<article class={`lang-${ currentLang.value }`}> | |||
<ArticleContentTransformed /> | |||
</article> | |||
</div>; | |||
} | |||
); | |||
}, | |||
}); |
@@ -1,33 +1,39 @@ | |||
import { RouterLink } from 'vue-router'; | |||
import { RouterLink } from "vue-router"; | |||
import useHead from "@/useHead.ts"; | |||
export default { | |||
name: 'ge-main', | |||
name: "ge-main", | |||
setup() { | |||
return () => <> | |||
<header> | |||
<h1>Generative Energy</h1> | |||
</header> | |||
<div class="text-slab"> | |||
<p> | |||
This page is dedicated to Dr. Raymond Peat († 2022), who has had a profound impact on my health and | |||
my life in general. Hover over the links below for more detail. | |||
</p> | |||
</div> | |||
<div class="text-slab"> | |||
<h2>Links</h2> | |||
<ul> | |||
<li> | |||
<RouterLink to={{ name: 'GECalculator' }}>Thyroid Calculator</RouterLink> | |||
<span style="display: none" class="tooltip">Convert to and from grains, set ratios, etc.</span> | |||
</li> | |||
<li> | |||
<RouterLink to={{ name: 'GEDeutsch' }}>Ray Peat Articles in German</RouterLink> | |||
<span style="display: none" class="tooltip"> | |||
A selection of articles by Ray that I have translated in my spare time into German. | |||
</span> | |||
</li> | |||
</ul> | |||
</div> | |||
</> | |||
useHead({ title: "Generative Energy" }); | |||
return () => ( | |||
<> | |||
<header> | |||
<h1>Generative Energy</h1> | |||
</header> | |||
<div class="text-slab"> | |||
<p> | |||
This page is dedicated to Dr. Raymond Peat († 2022), who has had a profound impact on my health | |||
and my life in general. Hover over the links below for more detail. | |||
</p> | |||
</div> | |||
<div class="text-slab"> | |||
<h2>Links</h2> | |||
<ul> | |||
<li> | |||
<RouterLink to={{ name: "GECalculator" }}>Thyroid Calculator</RouterLink> | |||
<span style="display: none" class="tooltip"> | |||
Convert to and from grains, set ratios, etc. | |||
</span> | |||
</li> | |||
<li> | |||
<RouterLink to={{ name: "GEDeutsch" }}>Ray Peat Articles in German</RouterLink> | |||
<span style="display: none" class="tooltip"> | |||
A selection of articles by Ray that I have translated in my spare time into German. | |||
</span> | |||
</li> | |||
</ul> | |||
</div> | |||
</> | |||
); | |||
}, | |||
}; |
@@ -1,21 +0,0 @@ | |||
import { defineComponent } from "vue"; | |||
export default defineComponent({ | |||
name: "dj-paypal-donate", | |||
setup: () => { | |||
return () => ( | |||
<form action="https://www.paypal.com/donate" method="post" target="_top"> | |||
<input type="hidden" name="hosted_button_id" value="9NXC6V5HDPGFL" /> | |||
<input | |||
id="ppbutton" | |||
type="image" | |||
src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" | |||
name="submit" | |||
title="Thanks for your support!" | |||
alt="Donate with PayPal button" | |||
/> | |||
<img alt="" src="https://www.paypal.com/en_DE/i/scr/pixel.gif" width="1" height="1" /> | |||
</form> | |||
); | |||
}, | |||
}); |
@@ -1,34 +1,34 @@ | |||
import { defineComponent, type VNode, Suspense } from "vue"; | |||
import { useRoute, RouterLink, RouterView, type RouteRecordRaw } from 'vue-router'; | |||
import { defineComponent, Suspense, type VNode } from "vue"; | |||
import { type RouteRecordRaw, RouterLink, RouterView, useRoute } from "vue-router"; | |||
import GEMain from "@/generative-energy/GEMain.tsx"; | |||
import DJEmail from "@/DJEmail.tsx"; | |||
import GEPaypal from "@/generative-energy/GEPaypal.tsx"; | |||
import GEDeutsch from "@/generative-energy/GEDeutsch.tsx"; | |||
import GEDeutschArticle from "@/generative-energy/GEDeutschArticle.tsx"; | |||
import GECalculator from "@/generative-energy/GECalculator.tsx"; | |||
import DJDonate from "@/DJDonate.tsx"; | |||
export const routes: RouteRecordRaw[] = [ | |||
{ | |||
path: '/', | |||
name: 'GEMain', | |||
path: "/", | |||
name: "GEMain", | |||
component: GEMain, | |||
}, | |||
{ | |||
path: '/calculator', | |||
name: 'GECalculator', | |||
path: "/calculator", | |||
name: "GECalculator", | |||
component: GECalculator, | |||
}, | |||
{ | |||
path: '/raypeat-deutsch', | |||
name: 'GEDeutsch', | |||
path: "/raypeat-deutsch", | |||
name: "GEDeutsch", | |||
component: GEDeutsch, | |||
}, | |||
{ | |||
path: '/raypeat-deutsch/article/:articleName', | |||
name: 'GEDeutschArticle', | |||
path: "/raypeat-deutsch/article/:articleName", | |||
name: "GEDeutschArticle", | |||
component: GEDeutschArticle, | |||
props: ({ params }) => { | |||
if ('articleName' in params) { | |||
if ("articleName" in params) { | |||
return { articleName: params.articleName }; | |||
} else { | |||
return false; | |||
@@ -41,27 +41,35 @@ export default defineComponent({ | |||
name: "ge-root", | |||
setup() { | |||
const route = useRoute(); | |||
return () => <> | |||
<main> | |||
<RouterLink class={"home-btn" + (route.name === 'GEMain' ? ' hide' : '') } | |||
to={{ name: 'GEMain' }}> | |||
Generative Energy Home | |||
</RouterLink> | |||
<RouterView>{{ default: ({ Component }: { Component: VNode }) => (Component && | |||
<Suspense>{{ | |||
default: () => Component, | |||
fallback: () => <div>Page loading...</div>, | |||
}}</Suspense> | |||
)}}</RouterView> | |||
<footer> | |||
<div class="bottom"> | |||
<div> | |||
<a href="https://djledda.de/">djledda.de</a> 2023 - <DJEmail>{ () => 'Contact' }</DJEmail> | |||
return () => ( | |||
<> | |||
<main> | |||
<RouterLink class={"home-btn" + (route.name === "GEMain" ? " hide" : "")} to={{ name: "GEMain" }}> | |||
Generative Energy Home | |||
</RouterLink> | |||
<RouterView> | |||
{{ | |||
default: ({ Component }: { Component: VNode }) => (Component && | |||
( | |||
<Suspense> | |||
{{ | |||
default: () => Component, | |||
fallback: () => <div>Page loading...</div>, | |||
}} | |||
</Suspense> | |||
)), | |||
}} | |||
</RouterView> | |||
<footer> | |||
<div class="bottom"> | |||
<div> | |||
<a href="/">djledda.de</a> 2023 - <DJEmail>{() => "Contact"}</DJEmail> | |||
</div> | |||
<DJDonate /> | |||
</div> | |||
<GEPaypal /> | |||
</div> | |||
</footer> | |||
</main> | |||
</>; | |||
</footer> | |||
</main> | |||
</> | |||
); | |||
}, | |||
}); |
@@ -4,7 +4,7 @@ import GERoot, { routes } from "@/generative-energy/GERoot.tsx"; | |||
createSSRApp(GERoot) | |||
.use(createRouter({ | |||
routes, | |||
history: createWebHistory('/generative-energy') | |||
routes, | |||
history: createWebHistory("/generative-energy"), | |||
})) | |||
.mount('#app-root'); | |||
.mount("#app-root"); |
@@ -0,0 +1,12 @@ | |||
import { createSSRApp } from "vue"; | |||
import { createMemoryHistory, createRouter } from "vue-router"; | |||
import GERoot, { routes } from "@/generative-energy/GERoot.tsx"; | |||
export default function createApp() { | |||
const router = createRouter({ | |||
routes, | |||
history: createMemoryHistory("/generative-energy"), | |||
}); | |||
const app = createSSRApp(GERoot).use(router); | |||
return { app, router }; | |||
} |
@@ -1,19 +1,75 @@ | |||
import { computed, defineComponent, ref } from "vue"; | |||
import { defineComponent } from "vue"; | |||
import useHead from "@/useHead.ts"; | |||
import DJTooltip from "@/DJTooltip.tsx"; | |||
import DJEmail from "@/DJEmail.tsx"; | |||
export default defineComponent({ | |||
name: "app-root", | |||
setup() { | |||
const count = ref(0); | |||
const countDouble = computed(() => count.value * 2); | |||
count.value++; | |||
useHead({ title: "DJ Ledda's Homepage" }); | |||
return () => ( | |||
<div class="app-main"> | |||
<button class="test" onClick={() => count.value++}>Click me!</button> | |||
<div>Count: {count.value}</div> | |||
<div>Count Double: {countDouble.value}</div> | |||
<p> | |||
<a href="/generative-energy/">Go to this other site hosted here but a different Vue app!</a> | |||
</p> | |||
<div class="supercontainer"> | |||
<div class="shakeable"> | |||
<div class="title_name"> | |||
<DJTooltip tooltip="I wonder what he's listening to?"> | |||
<img src="/home/img/dj.gif" alt="dj legt krasse Mucke auf" class="dude" /> | |||
</DJTooltip> | |||
<DJTooltip tooltip="Easily the coolest guy out there."> | |||
<span>DJ Ledda</span> | |||
</DJTooltip> | |||
<DJTooltip tooltip="I once heard this guy played at revs."> | |||
<img src="/home/img/dj.gif" alt="dj laying down some sick beats" class="dude" /> | |||
</DJTooltip> | |||
</div> | |||
<div class="main"> | |||
<div class="subject"> | |||
<div class="resourcelist"> | |||
<a href="https://drum-slayer.com"> | |||
<DJTooltip class="resource" tooltip="Small app for designing multitrack looped rhythms with local save and multiple files. Originally built using just vanilla TypeScript and CSS, now with Vue."> | |||
Drum Slayer | |||
</DJTooltip> | |||
</a> | |||
<a href="/somaesque"> | |||
<DJTooltip class="resource" tooltip="Puzzle solver app for puzzle cubes resembling the original Soma Cube puzzle. Save and edit your own puzzles! Built with Svelte, THREE.js and AssemblyScript."> | |||
Somaesque | |||
</DJTooltip> | |||
</a> | |||
<a href="/generative-energy"> | |||
<DJTooltip class="resource" tooltip="Thyroid calculator, German translations, and more..."> | |||
Generative Energy - Ray Peat Resources | |||
</DJTooltip> | |||
</a> | |||
<a href="/home/muenchen-auf-englisch.html"> | |||
<DJTooltip class="resource" tooltip=" | |||
Authentic historically accurate translations of all of Munich's S-Bahn and U-Bahn | |||
stations, as well as the main municipalities, into English. You live in Allach? It's | |||
Axleigh now. Giesing? Nope! Kyesing! This is a WIP. | |||
"> | |||
München auf Englisch - Munich in English | |||
</DJTooltip> | |||
</a> | |||
<a href="/kadi/"> | |||
<DJTooltip class="resource" tooltip="Make an account and start saving paper and tracking your Yatzy stats with your | |||
friends! Make your own rulesets, and more. Built with React, express.js, and | |||
MongoDB. Currently inactive."> | |||
K A D I: Online Yatzy Scoresheets | |||
</DJTooltip> | |||
</a> | |||
<a href="http://git.djledda.de/Ledda"> | |||
<DJTooltip class="resource" tooltip="Check out what I'm coding!"> | |||
My git projects | |||
</DJTooltip> | |||
</a> | |||
<DJEmail> | |||
<DJTooltip class="resource" tooltip="You'll see my address when you click here."> | |||
Click here to get in touch | |||
</DJTooltip> | |||
</DJEmail> | |||
</div> | |||
</div> | |||
</div> | |||
<div id="tooltipCarrier"></div> | |||
</div> | |||
</div> | |||
); | |||
}, | |||
@@ -0,0 +1,4 @@ | |||
import { createSSRApp } from "vue"; | |||
import App from "@/home/App.tsx"; | |||
createSSRApp(App).mount("#app-root"); |
@@ -0,0 +1,7 @@ | |||
import { createSSRApp } from "vue"; | |||
import App from "@/home/App.tsx"; | |||
export default function createApp() { | |||
const app = createSSRApp(App); | |||
return { app, router: null }; | |||
} |
@@ -1,13 +0,0 @@ | |||
import { useSSRContext, toValue, type MaybeRefOrGetter } from 'vue'; | |||
export default function useHead(params: { | |||
title: MaybeRefOrGetter<string>, | |||
}) { | |||
const context = useSSRContext(); | |||
if (context) { | |||
context.meta ??= { | |||
title: toValue(params.title), | |||
meta: {}, | |||
}; | |||
} | |||
} |
@@ -1,122 +0,0 @@ | |||
import { h, qsa } from "@/util.ts"; | |||
import { type CSSProperties, defineComponent, onBeforeUnmount, onMounted, ref } from "vue"; | |||
const carrierStyle = { | |||
opacity: "0", | |||
display: "block", | |||
pointerEvents: "none", | |||
backgroundColor: "black", | |||
border: "white solid 1px", | |||
color: "white", | |||
padding: "10px", | |||
position: "absolute", | |||
zIndex: "1", | |||
overflow: "hidden", | |||
height: "0", | |||
width: "0", | |||
transition: "opacity 200ms, height 200ms, width 200ms", | |||
} satisfies CSSProperties; | |||
const textCarrierStyle = { | |||
width: "350px", | |||
display: "block", | |||
overflow: "hidden", | |||
} satisfies CSSProperties; | |||
const defaultWidth = 350; | |||
export default defineComponent({ | |||
name: "dj-sexy-tooltip", | |||
setup() { | |||
const active = ref(false); | |||
const carrier = ref<HTMLElement | null>(null); | |||
const textCarrier = ref<HTMLElement | null>(null); | |||
onMounted(() => { | |||
document.body.appendChild(h("style", { textContent: `.tooltip { display: none; }` })); | |||
document.body.style.position = "relative"; | |||
document.addEventListener("mousemove", (event) => this.rerenderTooltip({ x: event.pageX, y: event.pageY })); | |||
}); | |||
function rerenderTooltip(mousePos: { x: number; y: number }) { | |||
const newText = this.getTooltipTextForHoveredElement(mousePos); | |||
if (newText !== this.textCarrier) { | |||
this.transitionTooltipSize(newText); | |||
const tooltipHasText = newText !== ""; | |||
if (tooltipHasText !== this.active) { | |||
this.toggleActive(); | |||
} | |||
} | |||
if (this.isStillVisible()) { | |||
this.updatePosition(mousePos); | |||
} | |||
} | |||
function isStillVisible() { | |||
return getComputedStyle(carrier.value).opacity !== "0"; | |||
} | |||
function updatePosition(pos: { x: number; y: number }) { | |||
if (pos.x + 15 + this.carrier.clientWidth <= document.body.scrollWidth) { | |||
this.carrier.style.left = (pos.x + 15) + "px"; | |||
} else { | |||
this.carrier.style.left = (document.body.scrollWidth - this.carrier.clientWidth - 5) + "px"; | |||
} | |||
if (pos.y + this.carrier.clientHeight <= document.body.scrollHeight) { | |||
this.carrier.style.top = pos.y + "px"; | |||
} else { | |||
this.carrier.style.top = (document.body.scrollHeight - this.carrier.clientHeight - 5) + "px"; | |||
} | |||
} | |||
function toggleActive() { | |||
if (this.active) { | |||
this.active = false; | |||
this.carrier.style.opacity = "0"; | |||
this.carrier.style.padding = "0"; | |||
} else { | |||
this.active = true; | |||
this.carrier.style.opacity = "1"; | |||
this.carrier.style.padding = SexyTooltip.carrierStyle.padding; | |||
} | |||
} | |||
function transitionTooltipSize(text: string) { | |||
const testDiv = h("div"); | |||
testDiv.style.height = "auto"; | |||
testDiv.style.width = SexyTooltip.defaultWidth + "px"; | |||
testDiv.innerHTML = text; | |||
document.body.appendChild(testDiv); | |||
const calculatedHeight = testDiv.scrollHeight; | |||
testDiv.style.display = "inline"; | |||
testDiv.style.width = "auto"; | |||
document.body.removeChild(testDiv); | |||
const size = { height: calculatedHeight + "px", width: "350px" }; | |||
this.carrier.style.height = size.height; | |||
this.carrier.style.width = text === "" ? "0" : size.width; | |||
this.textCarrier.innerHTML = text; | |||
} | |||
function getTooltipTextForHoveredElement(mousePos: { x: number; y: number }) { | |||
for (const elem of this.elementsWithTooltips) { | |||
const boundingRect = elem.getBoundingClientRect(); | |||
const inYRange = mousePos.y >= boundingRect.top + window.pageYOffset - 1 && | |||
mousePos.y <= boundingRect.bottom + window.pageYOffset + 1; | |||
const inXRange = mousePos.x >= boundingRect.left + window.pageXOffset - 1 && | |||
mousePos.x <= boundingRect.right + window.pageXOffset + 1; | |||
if (inYRange && inXRange) { | |||
return elem.nextElementSibling?.innerText ?? ""; | |||
} | |||
} | |||
return ""; | |||
} | |||
return () => ( | |||
<div ref={carrier}> | |||
<span ref={textCarrier} /> | |||
</div> | |||
); | |||
}, | |||
}); |
@@ -1,17 +1,22 @@ | |||
import { onMounted, onServerPrefetch, useSSRContext, shallowRef, type ShallowRef } from 'vue'; | |||
import { onMounted, onServerPrefetch, type ShallowRef, shallowRef } from "vue"; | |||
import useDJSSRContext from "@/useDJSSRContext.ts"; | |||
declare global { | |||
// deno-lint-ignore no-var | |||
var appstate: Partial<Record<string, unknown>>; | |||
} | |||
export default function useAsyncState<T>(key: string, getter: (context: { hostUrl: string }) => Promise<T | null>, options?: { suspensible: boolean }): { result: ShallowRef<T | null>, stateIsReady: Promise<unknown> } { | |||
const ssrContext = useSSRContext<{ registry: Record<string, unknown> }>(); | |||
const isClient = typeof ssrContext === 'undefined'; | |||
export default function useAsyncState<T>( | |||
key: string, | |||
getter: (context: { hostUrl: string }) => Promise<T | null>, | |||
options?: { suspensible: boolean }, | |||
): { result: ShallowRef<T | null>; stateIsReady: Promise<unknown> } { | |||
const ssrContext = useDJSSRContext(); | |||
const isClient = typeof ssrContext === "undefined"; | |||
const registry = ssrContext?.registry ?? globalThis?.appstate; | |||
const hostUrl = isClient ? globalThis.location.origin : 'http://localhost:8080'; | |||
const hostUrl = isClient ? globalThis.location.origin : "http://localhost:8080"; | |||
const state = shallowRef<T | null>(null); | |||
@@ -0,0 +1,12 @@ | |||
import { type MaybeRefOrGetter, useSSRContext } from "vue"; | |||
export type DJSSRContext = { | |||
head: { | |||
title: MaybeRefOrGetter<string>; | |||
}; | |||
registry: Record<string, unknown>; | |||
}; | |||
export default function useDJSSRContext() { | |||
return useSSRContext<DJSSRContext>(); | |||
} |
@@ -0,0 +1,19 @@ | |||
import { watchEffect, toValue, type MaybeRefOrGetter } from 'vue'; | |||
import useDJSSRContext from "@/useDJSSRContext.ts"; | |||
export default function useHead(params: { | |||
title: MaybeRefOrGetter<string>, | |||
}) { | |||
const context = useDJSSRContext(); | |||
if (context) { | |||
context.head.title = params.title; | |||
} else { | |||
watchEffect(() => { | |||
const newTitle = toValue(params.title); | |||
if (newTitle) { | |||
document.title = newTitle; | |||
} | |||
}); | |||
} | |||
} |
@@ -11,11 +11,13 @@ | |||
"jsr:@std/encoding@^1.0.5": "1.0.5", | |||
"jsr:@std/fmt@0.223": "0.223.0", | |||
"jsr:@std/fmt@^1.0.3": "1.0.3", | |||
"jsr:@std/fs@*": "0.223.0", | |||
"jsr:@std/fs@0.223": "0.223.0", | |||
"jsr:@std/http@*": "1.0.9", | |||
"jsr:@std/io@0.223": "0.223.0", | |||
"jsr:@std/media-types@^1.0.3": "1.0.3", | |||
"jsr:@std/net@^1.0.4": "1.0.4", | |||
"jsr:@std/path@*": "1.0.7", | |||
"jsr:@std/path@0.223": "0.223.0", | |||
"jsr:@std/path@^1.0.7": "1.0.7", | |||
"jsr:@std/streams@^1.0.7": "1.0.7", | |||
@@ -34,7 +36,7 @@ | |||
"dependencies": [ | |||
"jsr:@deno/graph", | |||
"jsr:@std/fmt@0.223", | |||
"jsr:@std/fs", | |||
"jsr:@std/fs@0.223", | |||
"jsr:@std/io", | |||
"jsr:@std/path@0.223" | |||
] | |||
@@ -68,7 +70,11 @@ | |||
"integrity": "97765c16aa32245ff4e2204ecf7d8562496a3cb8592340a80e7e554e0bb9149f" | |||
}, | |||
"@std/fs@0.223.0": { | |||
"integrity": "3b4b0550b2c524cbaaa5a9170c90e96cbb7354e837ad1bdaf15fc9df1ae9c31c" | |||
"integrity": "3b4b0550b2c524cbaaa5a9170c90e96cbb7354e837ad1bdaf15fc9df1ae9c31c", | |||
"dependencies": [ | |||
"jsr:@std/assert", | |||
"jsr:@std/path@0.223" | |||
] | |||
}, | |||
"@std/http@1.0.9": { | |||
"integrity": "d409fc319a5e8d4a154e576c758752e9700282d74f31357a12fec6420f9ecb6c", | |||
@@ -1,4 +1,6 @@ | |||
import "jsr:@deno/emit"; | |||
import "jsr:@std/http"; | |||
import "vue"; | |||
import 'jsr:@b-fuze/deno-dom'; | |||
import "jsr:@b-fuze/deno-dom"; | |||
import "jsr:@std/fs"; | |||
import "jsr:@std/path"; |
@@ -1,16 +1,85 @@ | |||
import { serveFile } from "jsr:@std/http/file-server"; | |||
import { createSSRApp } from "vue"; | |||
import { type App, toValue } from "vue"; | |||
import { type Router } from "vue-router"; | |||
import { renderToString } from "vue/server-renderer"; | |||
import { createRouter, createMemoryHistory } from 'vue-router'; | |||
import Home from "@/home/App.tsx"; | |||
import GERoot, { routes as geRoutes } from "@/generative-energy/GERoot.tsx"; | |||
import transpileResponse from "./transpile.ts"; | |||
import { DOMParser } from 'jsr:@b-fuze/deno-dom' | |||
import { DOMParser } from "jsr:@b-fuze/deno-dom"; | |||
import { join } from "jsr:@std/path/join"; | |||
import { exists } from "jsr:@std/fs"; | |||
import { type DJSSRContext } from "@/useDJSSRContext.ts"; | |||
import { type DJAPIResult, type DJAPIResultMap } from "@/api.ts"; | |||
const utf8Decoder = new TextDecoder("utf-8"); | |||
const parser = new DOMParser(); | |||
function appHeaderScript(params: { appstate: Record<string, unknown>; entryPath: string }) { | |||
return `<script type="importmap"> | |||
{ | |||
"imports": { | |||
"vue": "/deps/vue/dist/vue.esm-browser.prod.js", | |||
"vue-router": "/deps/vue-router/dist/vue-router.esm-browser.js", | |||
"vue/jsx-runtime": "/deps/vue/jsx-runtime/index.mjs", | |||
"@vue/devtools-api": "/deps/@vue/devtools-api/lib/esm/index.js", | |||
"@/": "/app/" | |||
} | |||
} | |||
</script> | |||
<script type="module"> | |||
window.appstate = ${JSON.stringify(params.appstate)}; | |||
import('${params.entryPath}'); | |||
</script>`; | |||
} | |||
async function* siteEntries(path: string): AsyncGenerator<string> { | |||
for await (const dirEnt of Deno.readDir(path)) { | |||
if (dirEnt.isDirectory) { | |||
yield* siteEntries(join(path, dirEnt.name)); | |||
} else if (dirEnt.name === "index_template.html") { | |||
yield path.split("/")[1] ?? ""; | |||
} | |||
} | |||
} | |||
const publicFiles = siteEntries("public"); | |||
const sites: string[] = []; | |||
for await (const path of publicFiles) { | |||
sites.push(path); | |||
} | |||
function getAPIResponse(apiReq: Request): Response { | |||
let jsonResponse: DJAPIResult | { error: string } | null = null; | |||
let status = 200; | |||
const pathname = URL.parse(apiReq.url)?.pathname; | |||
if (!pathname) { | |||
jsonResponse = { error: "Invalid Route" }; | |||
status = 400; | |||
} | |||
if (!jsonResponse && pathname) { | |||
const apiPath = pathname.split("/api")[1]; | |||
if (apiPath === "/rp-articles") { | |||
jsonResponse = [ | |||
{ name: "Koffein: ein vitamin-ähnlicher Nährstoff, oder Adaptogen", slug: "caffeine" }, | |||
{ | |||
name: "TSH, Temperatur, Puls, und andere Indikatoren bei einer Schilddrüsenunterfunktion", | |||
slug: "hypothyroidism", | |||
}, | |||
] satisfies DJAPIResultMap["/rp-articles"]; | |||
} | |||
} | |||
const headers = new Headers(); | |||
headers.set("Content-Type", "application/json"); | |||
return new Response(JSON.stringify(jsonResponse), { | |||
status, | |||
headers, | |||
}); | |||
} | |||
Deno.serve({ | |||
port: 8080, | |||
hostname: "0.0.0.0", | |||
@@ -18,63 +87,78 @@ Deno.serve({ | |||
console.log(`Listening on port http://${hostname}:${port}/`); | |||
}, | |||
}, async (req, _conn) => { | |||
let response: Response | null = null; | |||
if (req.method === "GET") { | |||
const pathname = URL.parse(req.url)?.pathname ?? "/"; | |||
if (pathname.startsWith('/static/') || | |||
pathname.startsWith('/generative-energy/static/')) { | |||
if (pathname.startsWith('/static/')) { | |||
return serveFile(req, `public/home/${ pathname }`); | |||
} else { | |||
return serveFile(req, `public${pathname}`); | |||
if (pathname.startsWith("/api/")) { | |||
response = getAPIResponse(req); | |||
} | |||
// Public/static files | |||
if (!response) { | |||
let filepath = join(".", "public", pathname); | |||
if (filepath.endsWith("/")) { | |||
filepath = join(filepath, "index.html"); | |||
} | |||
} else if (pathname === "/") { | |||
const rendered = await renderToString(createSSRApp(Home)); | |||
const content = utf8Decoder.decode(await Deno.readFile("./public/home/index.html")) | |||
.replace(`<!-- SSR OUTLET -->`, rendered) | |||
.replace(`<!-- SSR HEAD OUTLET -->`, ""); | |||
return new Response(content, { headers: { "Content-Type": "text/html" } }); | |||
} else if (pathname.startsWith('/generative-energy')) { | |||
const app = createSSRApp(GERoot); | |||
const router = createRouter({ | |||
routes: geRoutes, | |||
history: createMemoryHistory('/generative-energy'), | |||
}); | |||
app.use(router); | |||
app.provide('dom-parse', (innerHTML: string) => { | |||
return parser.parseFromString(innerHTML, 'text/html').documentElement; | |||
}); | |||
const ssrContext = { registry: {}}; | |||
await router.replace(pathname.split('/generative-energy')[1]); | |||
await router.isReady(); | |||
const rendered = await renderToString(app, ssrContext); | |||
const content = utf8Decoder.decode(await Deno.readFile("./public/generative-energy/index.html")) | |||
.replace('%TITLE%', 'Generative Energy') | |||
.replace('<!-- SSR HEAD OUTLET -->', ` | |||
<script type="importmap"> | |||
{ | |||
"imports": { | |||
"vue": "/deps/vue/dist/vue.esm-browser.prod.js", | |||
"vue-router": "/deps/vue-router/dist/vue-router.esm-browser.js", | |||
"vue/jsx-runtime": "/deps/vue/jsx-runtime/index.mjs", | |||
"@vue/devtools-api": "/deps/@vue/devtools-api/lib/esm/index.js", | |||
"@/": "/app/" | |||
if (await exists(filepath, { isFile: true })) { | |||
response = await serveFile(req, filepath); | |||
} | |||
} | |||
// NPM Vendor deps | |||
if (response === null && pathname.startsWith("/deps")) { | |||
response = await serveFile(req, `node_modules/${pathname.split("/deps")[1]}`); | |||
} | |||
// Transpile Code | |||
if ( | |||
response === null && | |||
pathname.startsWith("/app") && | |||
(pathname.endsWith(".ts") || pathname.endsWith(".tsx")) && | |||
!pathname.endsWith("server.ts") | |||
) { | |||
response = await serveFile(req, "./" + pathname); | |||
response = await transpileResponse(response, req.url, pathname); | |||
} | |||
// SSR | |||
if (response === null) { | |||
const baseDirectoryName = pathname.split("/")[1] ?? ""; | |||
if (sites.includes(baseDirectoryName)) { | |||
const appLocation = baseDirectoryName === "" ? "home" : baseDirectoryName; | |||
const siteTemplate = join("public", baseDirectoryName, "index_template.html"); | |||
const siteEntry = join("app", appLocation, "server.ts"); | |||
const clientEntry = join("@", appLocation, "client.ts"); | |||
const { app, router } = (await import("./" + siteEntry)).default() as { | |||
app: App; | |||
router: Router | null; | |||
}; | |||
app.provide("dom-parse", (innerHTML: string) => { | |||
return parser.parseFromString(innerHTML, "text/html").documentElement; | |||
}); | |||
const ssrContext: DJSSRContext = { registry: {}, head: { title: "" } }; | |||
if (router) { | |||
await router.replace(pathname.split("/generative-energy")[1]); | |||
await router.isReady(); | |||
} | |||
const rendered = await renderToString(app, ssrContext); | |||
const content = utf8Decoder.decode(await Deno.readFile(siteTemplate)) | |||
.replace(`<!-- SSR OUTLET -->`, rendered) | |||
.replaceAll("%TITLE%", toValue(ssrContext.head?.title) ?? "Site") | |||
.replace( | |||
`<!-- SSR HEAD OUTLET -->`, | |||
appHeaderScript({ appstate: ssrContext.registry, entryPath: clientEntry }), | |||
); | |||
response = new Response(content, { headers: { "Content-Type": "text/html" } }); | |||
} | |||
</script> | |||
<script> window.appstate = ${ JSON.stringify(ssrContext.registry) }; </script> | |||
`) | |||
.replace(`<!-- SSR OUTLET -->`, rendered); | |||
return new Response(content, { headers: { "Content-Type": "text/html" } }); | |||
} else if (pathname.startsWith("/app") && (pathname.endsWith(".ts") || pathname.endsWith(".tsx"))) { | |||
const response = await serveFile(req, "./" + pathname); | |||
return await transpileResponse(response, req.url, pathname); | |||
} else if (pathname.startsWith("/deps")) { | |||
return serveFile(req, `node_modules/${pathname.split("/deps")[1]}`); | |||
} | |||
return new Response("Not found.", { status: 404 }); | |||
} | |||
} else { | |||
return new Response("Only GET allowed.", { status: 500 }); | |||
response = new Response("Only GET allowed.", { status: 500 }); | |||
} | |||
return response ?? new Response("Not found.", { status: 404 }); | |||
}); | |||
Deno.addSignalListener("SIGINT", () => { | |||
@@ -1,7 +1,5 @@ | |||
<h1> | |||
<span lang="en">Caffeine: A vitamin-like nutrient, or adaptogen</span> | |||
<span lang="de">Koffein: ein vitamin-ähnlicher Nährstoff, oder Adaptogen</span> | |||
</h1> | |||
<h1 lang="en">Caffeine: A vitamin-like nutrient, or adaptogen</h1> | |||
<h1 lang="de">Koffein: ein vitamin-ähnlicher Nährstoff, oder Adaptogen</h1> | |||
<p> | |||
<span lang="en"><strong>Questions about tea and coffee, cancer and other degenerative diseases, and the hormones.</strong></span> |
@@ -5,7 +5,7 @@ | |||
<meta name="viewport" content="width=device-width, initial-scale=1"> | |||
<title>%TITLE%</title> | |||
<link rel="stylesheet" href="/generative-energy/static/styles.css"> | |||
<link rel="stylesheet" href="/generative-energy/styles.css"> | |||
<link | |||
href="https://fonts.googleapis.com/css2?family=Roboto&family=Roboto+Slab:wght@600&display=swap" | |||
rel="stylesheet"> |
@@ -139,17 +139,26 @@ hr { | |||
background: #999; | |||
} | |||
footer .bottom { | |||
display: flex; | |||
flex-direction: row; | |||
justify-content: space-between; | |||
} | |||
footer { | |||
margin-bottom: 20px; | |||
} | |||
.bottom { | |||
display: flex; | |||
flex-direction: row; | |||
justify-content: space-between; | |||
align-items: center; | |||
> * { | |||
flex: 1; | |||
} | |||
.dj-donate { | |||
text-align: right; | |||
} | |||
img { | |||
display: inline-block; | |||
} | |||
} | |||
} | |||
.ge-calculator { | |||
table { | |||
@@ -186,13 +195,3 @@ footer { | |||
} | |||
} | |||
} | |||
.fade-enter-active, | |||
.fade-leave-active { | |||
transition: opacity 0.5s ease; | |||
} | |||
.fade-enter-from, | |||
.fade-leave-to { | |||
opacity: 0; | |||
} |
@@ -1,18 +0,0 @@ | |||
<!DOCTYPE html> | |||
<html> | |||
<head> | |||
<title>G'day</title> | |||
<link rel="icon" href="icon.webp" /> | |||
<link rel="stylesheet" href="/static/styles.css" /> | |||
<!-- SSR HEAD OUTLET --> | |||
<script type="module" src="/static/app.js"></script> | |||
</head> | |||
<body> | |||
<main class="container"> | |||
<h1>G'day, mate!</h1> | |||
<h2>YOUR SITE GOES HERE</h2> | |||
<img src="/static/image.jpeg" alt="KANGAROO" /> | |||
<div id="app-root"><!-- SSR OUTLET --></div> | |||
</main> | |||
</body> | |||
</html> |
@@ -0,0 +1,130 @@ | |||
interface Point { | |||
x: number; | |||
y: number; | |||
} | |||
interface Size { | |||
height: string; | |||
width: string; | |||
} | |||
class SexyTooltip { | |||
private static readonly carrierStyle: Partial<CSSStyleDeclaration> = { | |||
opacity: "0", | |||
display: "block", | |||
pointerEvents: "none", | |||
backgroundColor: "black", | |||
border: "white solid 1px", | |||
color: "white", | |||
padding: "10px", | |||
position: "absolute", | |||
zIndex: "1", | |||
overflow: "hidden", | |||
height: "0", | |||
width: "0", | |||
transition: "opacity 200ms, height 200ms, width 200ms", | |||
}; | |||
private static readonly textCarrierStyle: Partial<CSSStyleDeclaration> = { | |||
width: "350px", | |||
display: "block", | |||
overflow: "hidden", | |||
}; | |||
private static readonly defaultWidth = 350; | |||
private readonly carrier: HTMLDivElement; | |||
private readonly textCarrier: HTMLSpanElement; | |||
private readonly elementsWithTooltips: Element[] = []; | |||
private active: boolean = false; | |||
constructor(carrier: HTMLDivElement) { | |||
if (carrier.childNodes.length > 0) { | |||
throw new Error("Incorrect setup for tooltip! Remove the child nodes from the tooltip div."); | |||
} | |||
this.carrier = carrier; | |||
this.textCarrier = document.createElement("span"); | |||
this.carrier.appendChild(this.textCarrier); | |||
Object.assign(this.carrier.style, SexyTooltip.carrierStyle); | |||
Object.assign(this.textCarrier.style, SexyTooltip.textCarrierStyle); | |||
document.querySelectorAll(".tooltip").forEach((element) => { | |||
if (element.nodeName === "SPAN") { | |||
console.log(element.previousElementSibling); | |||
this.elementsWithTooltips.push(element.previousElementSibling); | |||
} | |||
}); | |||
document.addEventListener("mousemove", (event) => this.rerenderTooltip({ x: event.pageX, y: event.pageY })); | |||
} | |||
rerenderTooltip(mousePos: Point) { | |||
const newText = this.getTooltipTextForHoveredElement(mousePos); | |||
if (newText !== this.textCarrier.innerHTML) { | |||
this.transitionTooltipSize(newText); | |||
const tooltipHasText = newText !== ""; | |||
if (tooltipHasText !== this.active) { | |||
this.toggleActive(); | |||
} | |||
} | |||
if (this.isStillVisible()) { | |||
this.updatePosition(mousePos); | |||
} | |||
} | |||
isStillVisible() { | |||
return getComputedStyle(this.carrier).opacity !== "0"; | |||
} | |||
updatePosition(pos: Point) { | |||
if (pos.x + 15 + this.carrier.clientWidth <= document.body.scrollWidth) { | |||
this.carrier.style.left = (pos.x + 15) + "px"; | |||
} else { | |||
this.carrier.style.left = (document.body.scrollWidth - this.carrier.clientWidth - 5) + "px"; | |||
} | |||
if (pos.y + this.carrier.clientHeight <= document.body.scrollHeight) { | |||
this.carrier.style.top = pos.y + "px"; | |||
} else { | |||
this.carrier.style.top = (document.body.scrollHeight - this.carrier.clientHeight - 5) + "px"; | |||
} | |||
} | |||
toggleActive() { | |||
if (this.active) { | |||
this.active = false; | |||
this.carrier.style.opacity = "0"; | |||
this.carrier.style.padding = "0"; | |||
} else { | |||
this.active = true; | |||
this.carrier.style.opacity = "1"; | |||
this.carrier.style.padding = SexyTooltip.carrierStyle.padding; | |||
} | |||
} | |||
transitionTooltipSize(text: string) { | |||
const testDiv = document.createElement("div"); | |||
testDiv.style.height = "auto"; | |||
testDiv.style.width = SexyTooltip.defaultWidth + "px"; | |||
testDiv.innerHTML = text; | |||
document.body.appendChild(testDiv); | |||
const calculatedHeight = testDiv.scrollHeight; | |||
testDiv.style.display = "inline"; | |||
testDiv.style.width = "auto"; | |||
document.body.removeChild(testDiv); | |||
const size = { height: calculatedHeight + "px", width: "350px" }; | |||
this.carrier.style.height = size.height; | |||
this.carrier.style.width = text === "" ? "0" : size.width; | |||
this.textCarrier.innerHTML = text; | |||
} | |||
getTooltipTextForHoveredElement(mousePos: Point): string { | |||
for (const elem of this.elementsWithTooltips) { | |||
const boundingRect = elem.getBoundingClientRect(); | |||
const inYRange = mousePos.y >= boundingRect.top + window.pageYOffset - 1 && | |||
mousePos.y <= boundingRect.bottom + window.pageYOffset + 1; | |||
const inXRange = mousePos.x >= boundingRect.left + window.pageXOffset - 1 && | |||
mousePos.x <= boundingRect.right + window.pageXOffset + 1; | |||
if (inYRange && inXRange) { | |||
return (elem.nextElementSibling as HTMLSpanElement)?.innerText ?? ""; | |||
} | |||
} | |||
return ""; | |||
} | |||
} | |||
export default SexyTooltip; |
@@ -0,0 +1,47 @@ | |||
import SexyTooltip from "./SexyTooltip.js"; | |||
const tooltipDiv = document.getElementById("tooltipCarrier") as HTMLDivElement; | |||
const tooltip = new SexyTooltip(tooltipDiv); | |||
const dudes = document.querySelectorAll(".dude") as NodeListOf<HTMLImageElement>; | |||
const shakers = document.querySelectorAll(".shakeable") as NodeListOf<HTMLElement>; | |||
const emailLink = document.getElementById("emailLink") as HTMLAnchorElement; | |||
let numDudesDroppingSickBeats: number = 0; | |||
dudes.forEach((dude) => dude.addEventListener("mouseup", () => toggleDude(dude))); | |||
function toggleDude(dude: HTMLImageElement) { | |||
if (dude.classList.contains("spinMe")) { | |||
numDudesDroppingSickBeats -= 1; | |||
dude.addEventListener("animationiteration", function listener() { | |||
dude.classList.remove("spinMe"); | |||
dude.removeEventListener("animationiteration", listener as EventListenerOrEventListenerObject); | |||
}); | |||
} else { | |||
numDudesDroppingSickBeats += 1; | |||
dude.classList.add("spinMe"); | |||
} | |||
updateShakers(); | |||
} | |||
function updateShakers() { | |||
shakers.forEach((shaker) => { | |||
if (numDudesDroppingSickBeats === 0) { | |||
shaker.classList.remove("shakeMe"); | |||
} else if (!shaker.classList.contains("shakeMe")) { | |||
shaker.classList.add("shakeMe"); | |||
} | |||
}); | |||
} | |||
emailLink.addEventListener("click", (event) => { | |||
const myDomain = "gmail"; | |||
const myTld = "com"; | |||
const myName = "danjledda"; | |||
const dot = "."; | |||
const at = "@"; | |||
let link = "mailto:" + myName + myDomain + myTld; | |||
link = link.slice(0, 10) + dot + link.slice(10, 11) + dot + link.slice(11, 16) + at + link.slice(16, 21) + dot + | |||
link.slice(21); | |||
window.location.href = link; | |||
}); |
@@ -0,0 +1,189 @@ | |||
:root { | |||
--subject-spacing: 40px; | |||
} | |||
html, | |||
body { | |||
height: 100%; | |||
width: 100%; | |||
margin: 0; | |||
padding: 0; | |||
} | |||
body { | |||
background-color: #292929; | |||
font-family: "Roboto", serif; | |||
} | |||
.title_name { | |||
font-size: 50px; | |||
color: floralwhite; | |||
font-family: "Roboto Slab", "Times New Roman", Times, serif; | |||
text-align: center; | |||
} | |||
.title_name img { | |||
margin: 20px; | |||
height: 100px; | |||
width: auto; | |||
vertical-align: middle; | |||
} | |||
.spinMe { | |||
animation: spin 1s infinite linear; | |||
} | |||
.shakeMe { | |||
animation: shake 0.2s infinite linear; | |||
} | |||
@keyframes shake { | |||
0% { | |||
transform: scale(1) translate(1px, 1px) rotate(0deg); | |||
} | |||
25% { | |||
transform: scale(0.95) translate(-1px, -2px) rotate(-1deg); | |||
} | |||
50% { | |||
transform: scale(0.9) translate(-3px, 0px) rotate(1deg); | |||
} | |||
75% { | |||
transform: scale(0.95) translate(3px, 2px) rotate(0deg); | |||
} | |||
100% { | |||
transform: scale(1) translate(-1px, 2px) rotate(-1deg); | |||
} | |||
} | |||
@keyframes spin { | |||
0% { | |||
transform: rotate(0deg) scale(1); | |||
filter: hue-rotate(0deg) saturate(5); | |||
} | |||
25% { | |||
transform: rotate(90deg) scale(2); | |||
} | |||
50% { | |||
transform: rotate(180deg) scale(1); | |||
} | |||
75% { | |||
transform: rotate(270deg) scale(2); | |||
} | |||
100% { | |||
transform: rotate(360deg) scale(1); | |||
filter: hue-rotate(360deg) saturate(5); | |||
} | |||
} | |||
.supercontainer { | |||
padding-top: 3em; | |||
margin: auto; | |||
} | |||
.main { | |||
width: 50em; | |||
margin: 20px auto; | |||
text-align: center; | |||
} | |||
@media only screen and (max-width: 1024px) { | |||
.main { | |||
width: 35em; | |||
padding: 20px; | |||
} | |||
:root { | |||
--subject-spacing: 20px; | |||
} | |||
} | |||
@media only screen and (max-width: 768px) { | |||
.title_name img { | |||
margin: 10px; | |||
height: 60px; | |||
width: auto; | |||
} | |||
.title_name { | |||
font-size: 30px; | |||
} | |||
.main { | |||
width: 20em; | |||
padding: 20px; | |||
} | |||
} | |||
span.subjecttitle { | |||
height: 100%; | |||
font-family: "Roboto Slab", "Times New Roman", Times, serif; | |||
font-size: 20px; | |||
padding-right: 5px; | |||
} | |||
.subject:first-child { | |||
margin-top: 0; | |||
} | |||
.subject:last-child { | |||
margin-bottom: 0; | |||
} | |||
.subject { | |||
margin-top: var(--subject-spacing); | |||
margin-bottom: var(--subject-spacing); | |||
} | |||
.resourcelist { | |||
margin-top: 2px; | |||
background-color: white; | |||
border-style: solid; | |||
border-color: #292929; | |||
border-width: 1px; | |||
border-radius: 5px; | |||
a:first-of-type { | |||
.resource { | |||
padding: 15px 20px 10px 20px; | |||
border-radius: 5px 5px 0 0; | |||
} | |||
} | |||
a:last-of-type { | |||
.resource { | |||
padding: 10px 20px 15px 20px; | |||
border-radius: 0 0 5px 5px; | |||
} | |||
} | |||
a:only-of-type { | |||
.resource { | |||
border-radius: 5px; | |||
} | |||
} | |||
} | |||
.resource { | |||
position: relative; | |||
background-color: white; | |||
padding: 10px 20px 10px 20px; | |||
display: block; | |||
color: #333333; | |||
text-decoration: none; | |||
transition: background-color 200ms; | |||
} | |||
.resource:hover { | |||
background-color: #bde4ff; | |||
} | |||
.resource:active { | |||
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.50) inset; | |||
} | |||
a { | |||
color: #000; | |||
text-decoration: none; | |||
cursor: pointer; | |||
} | |||
.tooltip-container { | |||
display: inline-block; | |||
} |
@@ -0,0 +1,740 @@ | |||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> | |||
<html> | |||
<head> | |||
<meta http-equiv="content-type" content="text/html; charset=utf-8"/> | |||
<title></title> | |||
<meta name="generator" content="LibreOffice 6.0.7.3 (Linux)"/> | |||
<meta name="created" content="2020-06-26T11:18:35.466195148"/> | |||
<meta name="changed" content="2020-06-29T14:58:13.220640138"/> | |||
<style type="text/css"> | |||
@page { margin: 0.79in } | |||
p { margin-bottom: 0.1in; line-height: 115% } | |||
td p { margin-bottom: 0in } | |||
a:link { so-language: zxx } | |||
table, th, td { border: 1px solid black !important } | |||
td { padding: 10px !important } | |||
td { width: auto !important; height: auto !important } | |||
table { border-collapse: collapse } | |||
</style> | |||
</head> | |||
<body lang="en-US" dir="ltr"> | |||
<p style="margin-bottom: 0in; line-height: 100%"><br/> | |||
</p> | |||
<table> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Allach">Allach</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Axleigh | |||
</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p>Ahaloh → Allach, Aha → Axe, Loh → Leigh (“Aha + lohe” | |||
→ “Flusslichtung”)</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" height="22" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Altstadt_(M%C3%BCnchen)">Altstadt</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Oldstead</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Am_Hart">Am Hart</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>At the Woods</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Am_Moosfeld">Am Moosfeld</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>At the Mossfield</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Am_Riesenfeld">Am | |||
Riesenfeld</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>At the Giantfield</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Au_(M%C3%BCnchen)">Au</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Ey</span></p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">as in “Romsey”, | |||
“Athelney”, “Beverley Hills”</span></p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Aubing">Aubing</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Eving</span></p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Kein Beleg für Herkunft, | |||
erste Ewähnung 16. April 1010 unter “Ubingun”. Bajuwarischer | |||
Name “Ubo” könnte naheliegen.</span></p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Berg_am_Laim">Berg am | |||
Laim</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Loam Hill</span></p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Bogenhausen">Bogenhausen</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Bobehouse</span></p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Bajuware Pubo, Pupi</span></p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Daglfing">Daglfing</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Dighelfing /daɪəlfɪŋ/ | |||
</span> | |||
</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Bajuware Tagolf/Thachulf</span></p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Denning_(M%C3%BCnchen)">Denning</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Denning </span> | |||
</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Bajuware Tenno</span></p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Englschalking">Englschalking</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Angelshalking</span></p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">(en. <i>shalk </i>→ a | |||
servant; Englschalch → “strenger Knecht”)</span></p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Fasangarten">Fasangarten</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Pheasantgarden</span></p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Feldmoching">Feldmoching</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Field Mocking </span> | |||
</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Feld der Anhänger des | |||
Mocho, imagined as en. Mocko</span></p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Forstenried">Forstenried</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Forestley</span></p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Freiham">Freiham</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Freeham</span></p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Freimann">Freimann</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Freeman</span></p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/F%C3%BCrstenried">Fürstenried</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Princeley</span></p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Obergiesing">Obergiesing</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Upper </span><span style="background: transparent">Kye</span><span style="background: transparent">sing | |||
/</span><span style="background: transparent">ka</span>ɪz<span style="background: transparent">ɪŋ/ | |||
</span> | |||
</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Bajuware </span><span style="background: transparent">Kyso | |||
→</span><span style="background: transparent"> </span><span style="background: transparent">perhaps | |||
en. </span><i><span style="background: transparent">Kyeso | |||
</span></i><span style="font-style: normal"><span style="background: transparent">(/</span></span><span style="font-style: normal"><span style="background: transparent">ka</span></span><span style="font-style: normal"><span style="background: transparent">ɪ</span></span><span style="font-style: normal"><span style="background: transparent">z</span></span><span style="font-style: normal"><span style="background: transparent">oʊ/)</span></span><span style="background: transparent">? | |||
K and S stay the same, </span><span style="background: transparent">ahd. | |||
</span><i><span style="background: transparent">y</span></i><span style="background: transparent"> | |||
is interchangeable with </span><i><span style="background: transparent">i</span></i><span style="background: transparent">, | |||
probably </span><span style="background: transparent">would have | |||
become /</span><span style="background: transparent">a</span><span style="background: transparent">ɪ</span><span style="background: transparent">/</span><span style="background: transparent">)</span></p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Untergiesing">Untergiesing</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Lower Kyesing</span></p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Hadern">Hadern</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Hathern /hei</span>ð<span style="background: transparent">ɜ:</span><span style="background: transparent">n/</span></p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Von ahd. </span><i><span style="background: transparent">Haderun | |||
</span></i><span style="background: transparent">(“bei den | |||
Waldleuten”</span><span style="background: transparent">)</span></p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/w/index.php?title=Holzapfelkreuth&action=edit&redlink=1">Holzapfelkreuth</a> | |||
</p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Woodapple Glade</span></p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Kreuth → <i>das | |||
Gereutete</i>, nhd. <i>Gerodete</i>, related to “rid” → a | |||
clearing, “rid of trees”</span></p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Haidhausen">Haidhausen</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p><span style="background: transparent">Heathhouse</span></p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Harlaching">Harlaching</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Heathleighing | |||
</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p>Von ahd. <i>Hadaleih </i>+ <i>ing →</i><span style="font-style: normal">gleich: | |||
“Heide + lohe + ing”</span></p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Hasenbergl">Hasenbergl</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Hare Hill</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Isarvorstadt">Isarvorstadt</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Isar Forestead</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Johanneskirchen_(M%C3%BCnchen)">Johanneskirchen</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Johnkirk</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p>en. -<i>kirk → </i><span style="font-style: normal">Church</span></p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Laim">Laim</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Loam</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p>Laim = nhd. <i>Lehm = </i><span style="font-style: normal">en. | |||
</span><i>Loam</i></p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Langwied_(M%C3%BCnchen)">Langwied</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Longwood</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p>Wied → ahd. witu “Wald”, en. “wood”</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Lehel_(M%C3%BCnchen)">Lehel</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Fief | |||
</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p>(von nhd. <i>Lehen</i><span style="font-style: normal">,</span><i> | |||
</i>cf. en. <i>Loan</i>)</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Lochhausen_(M%C3%BCnchen)">Lochhausen</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Lockhouse</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Ludwigsvorstadt">Ludwigsvorstadt</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Ludwig Forestead</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Maxvorstadt">Maxvorstadt</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Max Forestead</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Milbertshofen_(Bezirksteil)">Milbertshofen</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Ilvinghope | |||
</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p>ca. 1149 “Ilmungeshoven” von Bajuware | |||
Ilbunch/Ilbung/Ilmung. War Einsiedlerhof, auf den man ausgesiedelt | |||
wurde wegen Krankheit oder als Strafe → aussprache wurde | |||
absichtlich undeutlich um die Beziehung zu diesem Dorf zu | |||
verstecken. | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p> <a href="https://de.wikipedia.org/wiki/Moosach_(M%C3%BCnchen)">Moosach</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Moorey</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p>Moor + ach (“Au”). R wird zu S durch Rhotazismus (cf. | |||
frieren → Frost, verlieren → Verlust, waren → gewesen)</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Neuhausen_(M%C3%BCnchen)">Neuhausen</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Newhouse</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Nymphenburg_(M%C3%BCnchen)">Nymphenburg</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Nymphbury</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Oberf%C3%B6hring">Oberföhring</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Upper Fairing</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p>Bajuware Fero</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Obermenzing">Obermenzing</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Upper Menting</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p>Bajuware Manzo (wegen -ing umgelautet)</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Pasing">Pasing</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Paising /peɪzɪŋ/</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p>Bajuware Paoso, Paso</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Perlach_(M%C3%BCnchen)">Perlach</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Pearley</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Ramersdorf_(M%C3%BCnchen)">Ramersdorf</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Rammlethorpe /ræməlθɔ:p/ | |||
</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p>“Rumoltesdorf” ca. 1006. Kein Fugen-S im Englischen</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Riem">Riem</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Rim</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p>cf. “Pacific Rim”</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Schwabing">Schwabing</a> | |||
</p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Swabing /sweɪbɪŋ/</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p>Bajuware Swapo</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Schwanthalerh%C3%B6he">Schwanthalerhöhe</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Swandale Heights</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Sendling">Sendling</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Senthling</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p>Bajuware Sentilo</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Solln">Solln</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Soal</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p>Also closely related: en. soil, slough</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Steinhausen_(M%C3%BCnchen)">Steinhausen</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Stonehouse</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Thalkirchen">Thalkirchen</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Dalekirk</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Trudering">Trudering</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Droughthring /drauθrɪŋ/ | |||
</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p>Bajuware Truchtaro/Truhtheri/Drudheri/Tructeri</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Untermenzing">Untermenzing</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Lower Menting</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
<tr> | |||
<td width="122" style="border: none; padding: 0in"> | |||
<p><a href="https://de.wikipedia.org/wiki/Zamdorf">Zamdorf</a></p> | |||
</td> | |||
<td width="188" style="border: none; padding: 0in"> | |||
<p>Tamthorpe /tæmθɔ:p/</p> | |||
</td> | |||
<td width="344" style="border: none; padding: 0in"> | |||
<p><br/> | |||
</p> | |||
</td> | |||
</tr> | |||
</table> | |||
<p style="margin-bottom: 0in; line-height: 100%"><br/> | |||
</p> | |||
<p style="margin-bottom: 0in; line-height: 100%">Quellen:</p> | |||
<p style="margin-bottom: 0in; line-height: 100%"><br/> | |||
</p> | |||
<p style="margin-bottom: 0in; line-height: 100%">Wikipedia Seiten zu | |||
den jeweiligen Orten</p> | |||
<p style="margin-bottom: 0in; line-height: 100%"><br/> | |||
</p> | |||
<p style="margin-bottom: 0in; line-height: 100%">Wiktionary fuer | |||
einige Rueckbildungen (zB Slough, Rim, Long<b>weed</b><span style="font-weight: normal">, | |||
Loam, Angel</span><b>shalk</b><span style="font-weight: normal">ing)</span></p> | |||
<p style="margin-bottom: 0in; line-height: 100%"><br/> | |||
</p> | |||
<p style="margin-bottom: 0in; line-height: 100%"><a href="https://en.wikipedia.org/wiki/List_of_generic_forms_in_place_names_in_Ireland_and_the_United_Kingdom">https://en.wikipedia.org/wiki/List_of_generic_forms_in_place_names_in_Ireland_and_the_United_Kingdom</a></p> | |||
<p style="margin-bottom: 0in; line-height: 100%"><br/> | |||
</p> | |||
<p style="margin-bottom: 0in; line-height: 100%"><a href="https://de.wikipedia.org/wiki/Kategorie:Ortsnamen-Endung">https://de.wikipedia.org/wiki/Kategorie:Ortsnamen-Endung</a></p> | |||
<p style="margin-bottom: 0in; line-height: 100%"><br/> | |||
</p> | |||
<p style="margin-bottom: 0in; line-height: 100%"><a href="http://www.fam-schweden.de/Muenchen/muenchen.html">http://www.fam-schweden.de/Muenchen/muenchen.html</a></p> | |||
<p style="margin-bottom: 0in; line-height: 100%"><br/> | |||
</p> | |||
<p style="margin-bottom: 0in; line-height: 100%"><a href="https://de.wikipedia.org/wiki/Zweite_Lautverschiebung#Phase_1">https://de.wikipedia.org/wiki/Zweite_Lautverschiebung#Phase_1</a></p> | |||
<p style="margin-bottom: 0in; line-height: 100%"><br/> | |||
</p> | |||
<p style="margin-bottom: 0in; line-height: 100%"><a href="https://www.munichkindl.net/besiedlung">https://www.munichkindl.net/besiedlung</a></p> | |||
<p style="margin-bottom: 0in; line-height: 100%"><br/> | |||
</p> | |||
<p style="margin-bottom: 0in; line-height: 100%"><a href="https://www.gutenberg.org/files/22636/22636-h/22636-h.htm">https://www.gutenberg.org/files/22636/22636-h/22636-h.htm</a></p> | |||
<p style="margin-bottom: 0in; line-height: 100%"><br/> | |||
</p> | |||
<p style="margin-bottom: 0in; line-height: 100%"><br/> | |||
</p> | |||
<p style="margin-bottom: 0in; line-height: 100%"><br/> | |||
</p> | |||
</body> | |||
</html> |
@@ -1,22 +0,0 @@ | |||
.container { | |||
margin: auto; | |||
text-align: center; | |||
width: 100%; | |||
background-color: #fffafa; | |||
box-shadow: #000000; | |||
padding-top: 10px; | |||
min-height: calc(100vh - 10px); | |||
box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.2); | |||
@media (min-width: 992px) { | |||
width: 900px; | |||
} | |||
h1 { | |||
margin: 0; | |||
} | |||
} | |||
body { | |||
margin: 0; | |||
} |
@@ -0,0 +1,88 @@ | |||
<!DOCTYPE html> | |||
<html lang="en"> | |||
<head> | |||
<meta charset="utf-8" /> | |||
<title>DJ Ledda's Homepage</title> | |||
<meta name="description" content="the coolest homepage in the whole galaxy" /> | |||
<meta name="viewport" content="width=device-width, initial-scale=1" /> | |||
<link rel="manifest" href="site.webmanifest" /> | |||
<link rel="icon" href="img/dj.gif" /> | |||
<link rel="stylesheet" href="css/main.css" /> | |||
<link | |||
href="https://fonts.googleapis.com/css2?family=Roboto&family=Roboto+Slab:wght@600&display=swap" | |||
rel="stylesheet" | |||
/> | |||
<meta name="theme-color" content="#fafafa" /> | |||
</head> | |||
<body> | |||
<!--[if IE]> | |||
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a href="https://browsehappy.com/">upgrade your browser</a> to improve your experience and security.</p> | |||
<![endif]--> | |||
<!-- Site Content --> | |||
<div class="supercontainer"> | |||
<div class="shakeable"> | |||
<div class="title_name"> | |||
<img src="img/dj.gif" alt="dj legt krasse Mucke auf" class="dude" /> | |||
<span class="tooltip">I wonder what he's listening to?</span> | |||
<span>DJ Ledda</span> | |||
<span class="tooltip">Easily the coolest guy out there.</span> | |||
<img src="img/dj.gif" alt="dj laying down some sick beats" class="dude" /> | |||
<span class="tooltip">I once heard this guy played at revs.</span> | |||
</div> | |||
<div class="main"> | |||
<div class="subject"> | |||
<div class="resourcelist"> | |||
<a class="resource" href="https://drum-slayer.com"> | |||
Drum Slayer | |||
</a> | |||
<span class="tooltip" | |||
>Small app for designing multitrack looped rhythms with local save and multiple files. | |||
Originally built using just vanilla TypeScript and CSS, now with Vue.</span> | |||
<a class="resource" href="/somaesque"> | |||
Somaesque | |||
</a> | |||
<span class="tooltip" | |||
>Puzzle solver app for puzzle cubes resembling the original Soma Cube puzzle. Save and edit | |||
your own puzzles! Built with Svelte, THREE.js and AssemblyScript.</span> | |||
<a class="resource" href="/generative-energy"> | |||
Generative Energy - Ray Peat Resources | |||
</a> | |||
<span class="tooltip">Thyroid calculator, German translations, and more...</span> | |||
<a class="resource" href="/muenchen-auf-englisch.html"> | |||
München auf Englisch - Munich in English | |||
</a> | |||
<span class="tooltip" | |||
>Authentic historically accurate translations of all of Munich's S-Bahn and U-Bahn stations, | |||
as well as the main municipalities, into English. You live in Allach? It's Axleigh now. | |||
Giesing? Nope! Kyesing! This is a WIP.</span> | |||
<a class="resource" href="/kadi/"> | |||
K A D I: Online Yatzy Scoresheets | |||
</a> | |||
<span class="tooltip" | |||
>Make an account and start saving paper and tracking your Yatzy stats with your friends! | |||
Make your own rulesets, and more. Built with React, express.js, and MongoDB. Currently | |||
inactive.</span> | |||
<a class="resource" href="http://git.djledda.de/Ledda"> | |||
My git projects | |||
</a> | |||
<span class="tooltip">Check out what I'm coding!</span> | |||
<a id="emailLink" class="resource"> | |||
Click here to get in touch | |||
</a> | |||
<span class="tooltip">You'll see my address when you click here.</span> | |||
</div> | |||
</div> | |||
</div> | |||
<div id="tooltipCarrier"></div> | |||
</div> | |||
</div> | |||
<!-- End Content --> | |||
<script src="js/main.js" type="module"></script> | |||
</body> | |||
</html> |
@@ -0,0 +1,18 @@ | |||
<!DOCTYPE html> | |||
<html> | |||
<head> | |||
<title>G'day</title> | |||
<link rel="icon" href="/home/icon.webp" /> | |||
<link rel="stylesheet" href="/home/main.css" /> | |||
<link rel="icon" href="img/dj.gif" /> | |||
<link | |||
href="https://fonts.googleapis.com/css2?family=Roboto&family=Roboto+Slab:wght@600&display=swap" | |||
rel="stylesheet" | |||
/> | |||
<!-- SSR HEAD OUTLET --> | |||
</head> | |||
<body> | |||
<div id="app-root"><!-- SSR OUTLET --></div> | |||
</body> | |||
</html> |
@@ -0,0 +1,5 @@ | |||
# www.robotstxt.org/ | |||
# Allow crawling of all content | |||
User-agent: * | |||
Disallow: |