diff --git a/app/DJDonate.tsx b/app/DJDonate.tsx new file mode 100644 index 0000000..a8eaf77 --- /dev/null +++ b/app/DJDonate.tsx @@ -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 () => ( +
+ + + +
+ ); + }, +}); diff --git a/app/DJEmail.tsx b/app/DJEmail.tsx index 8b7dc37..3bcfbc0 100644 --- a/app/DJEmail.tsx +++ b/app/DJEmail.tsx @@ -14,7 +14,7 @@ export default defineComponent({ } return () => ( - { slots.default ? slots.default() : 'dan.j.ledda [at] gmail [dot] com' } + {slots.default ? slots.default() : "dan.j.ledda [at] gmail [dot] com"} ); }, diff --git a/app/DJTooltip.tsx b/app/DJTooltip.tsx new file mode 100644 index 0000000..f0df0ec --- /dev/null +++ b/app/DJTooltip.tsx @@ -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(null); + const textCarrier = ref(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 () => <> +
{ active.value = true; }} + onMouseleave={() => { active.value = false; }} + > + {slots.default && } +
+
+ {props.tooltip} +
+ ; + }, +}); diff --git a/app/api.ts b/app/api.ts new file mode 100644 index 0000000..10f55be --- /dev/null +++ b/app/api.ts @@ -0,0 +1,14 @@ +export type DJAPIEndpoint = "/rp-articles"; + +export interface DJAPIResultMap extends Record { + "/rp-articles": { slug: string; name: string }[]; +} + +export type DJAPIResult = DJAPIResultMap[DJAPIEndpoint]; + +export default async function getDJAPI( + hostUrl: string, + endpoint: T, +): Promise { + return await (await fetch(`${hostUrl}/api${endpoint}`)).json(); +} diff --git a/app/generative-energy/GECalculator.tsx b/app/generative-energy/GECalculator.tsx index da4d0ed..b5d91b3 100644 --- a/app/generative-energy/GECalculator.tsx +++ b/app/generative-energy/GECalculator.tsx @@ -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 () => <> -
-

Thyroid Calculator

-
-
-

- 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. -

-
-
- - - { inputDefs.map((_, i) => ( - + return () => ( + <> +
+

Thyroid Calculator

+
+
+

+ 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. +

+
+
+
+ + {inputDefs.map((_) => ( + + + + + ))} + + + + + + + + - ))} - - - - - - - - - - -
+ {_.name} + +
+ { + 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" + /> +
+ {_.unit} +
+ Compounded (T3 and T4, "Cynoplus") +
+ Desired Ratio (T3:T4) + +
+ { + t3Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber; + }} + min="0" + step="1" + type="number" + /> +
{" "} + :{" "} +
+ { + t4Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber; + }} + min="0" + step="1" + type="number" + /> +
+
- { _.name } + Synthetic T3/T4 Combo -
- { - 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" /> +
+ { + 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" + /> +
{" "} + :{" "} +
+ { + 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" + />
- { _.unit }
Compounded (T3 and T4, "Cynpolus")
- Desired Ratio (T3:T4) - -
- { - t3Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber; - }} - min="0" - step="1" - type="number" /> -
:
- { - t4Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber; - }} - min="0" - step="1" - type="number" /> -
-
- Synthetic T3/T4 Combo - -
- { - 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" /> -
:
- { - 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" /> -
-
-
-
- Changelog: -

-

    -
  • - November 2024: Migrated to new web framework and fixed some buggy input. -
  • -
  • - 13th March 2024: Removed the synthetic/pure distinction as it was confusing and unnecessary - bloat. -
  • -
-

-
- ; - } + + + +
+ Changelog: +

+

    +
  • + November 2024: Migrated to new web framework and fixed some buggy input. +
  • +
  • + 13th March 2024: Removed the synthetic/pure distinction as it was confusing and + unnecessary bloat. +
  • +
+

+
+ + ); + }, }); diff --git a/app/generative-energy/GEDeutsch.tsx b/app/generative-energy/GEDeutsch.tsx index 6e40854..16b1dfa 100644 --- a/app/generative-energy/GEDeutsch.tsx +++ b/app/generative-energy/GEDeutsch.tsx @@ -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 () => <> -
-

Ray Peat Deutsche Übersetzungen

-
-
-

- Auf dieser Seite befindet sich eine Auswahl der Artikel von Dr. Raymond Peat, die ich in meiner - Freizeit ins Deutsche übersetzt habe. -

-

- 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. -

-

- 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 seiner Website zu finden sind. -

-

- 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. -

-

- 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 eine Mail senden. - Meine Muttersprache ist schließlich Englisch und ich bin kein professioneller Übersetzer! -

-

- 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. -

-
-
-

Artikel auswählen:

-
    -
  • - Indikatoren Schilddrüsenunterfunktion -
  • -
  • - Koffein -
  • -
-
- ; - } + 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 () => ( + <> +
+

Ray Peat Deutsche Übersetzungen

+
+
+

+ Auf dieser Seite befindet sich eine Auswahl der Artikel von Dr. Raymond Peat, die ich in meiner + Freizeit ins Deutsche übersetzt habe. +

+

+ 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. +

+

+ 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{" "} + seiner Website zu finden sind. +

+

+ 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. +

+

+ 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 {" "} + eine Mail senden. Meine Muttersprache ist schließlich Englisch und ich bin kein professioneller + Übersetzer! +

+

+ 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. +

+
+
+

Artikel auswählen:

+
    + {rpArticles.value && rpArticles.value.map((_) => ( +
  • + + {_.name} + +
  • + ))} +
+
+ + ); + }, }); diff --git a/app/generative-energy/GEDeutschArticle.tsx b/app/generative-energy/GEDeutschArticle.tsx index 460ab9b..ecd15df 100644 --- a/app/generative-energy/GEDeutschArticle.tsx +++ b/app/generative-energy/GEDeutschArticle.tsx @@ -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('

')[1].split("

")[0]; + return result; + }, + ); + + useHead({ title }); + + onServerPrefetch(() => + new Promise((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 = {}; - const children = [...node.childNodes].map(_ => transformArticleNode(_)); + const attrs: Record = {}; + 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
Artikel lädt...
; } await stateIsReady; - return () =>
-
- Zur Artikelübersicht - + return () => ( +
+
+ Zur Artikelübersicht + +
+
+ +
-
- -
-
; - } + ); + }, }); diff --git a/app/generative-energy/GEMain.tsx b/app/generative-energy/GEMain.tsx index 4ea4ce5..3dbbcc7 100644 --- a/app/generative-energy/GEMain.tsx +++ b/app/generative-energy/GEMain.tsx @@ -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 () => <> -
-

Generative Energy

-
-
-

- 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. -

-
-
-

Links

-
    -
  • - Thyroid Calculator - -
  • -
  • - Ray Peat Articles in German - -
  • -
-
- + useHead({ title: "Generative Energy" }); + return () => ( + <> +
+

Generative Energy

+
+
+

+ 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. +

+
+
+

Links

+
    +
  • + Thyroid Calculator + +
  • +
  • + Ray Peat Articles in German + +
  • +
+
+ + ); }, }; diff --git a/app/generative-energy/GEPaypal.tsx b/app/generative-energy/GEPaypal.tsx deleted file mode 100644 index b1e5008..0000000 --- a/app/generative-energy/GEPaypal.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { defineComponent } from "vue"; - -export default defineComponent({ - name: "dj-paypal-donate", - setup: () => { - return () => ( -
- - - -
- ); - }, -}); diff --git a/app/generative-energy/GERoot.tsx b/app/generative-energy/GERoot.tsx index 25aa858..988b2b2 100644 --- a/app/generative-energy/GERoot.tsx +++ b/app/generative-energy/GERoot.tsx @@ -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 () => <> -
- - Generative Energy Home - - {{ default: ({ Component }: { Component: VNode }) => (Component && - {{ - default: () => Component, - fallback: () =>
Page loading...
, - }}
- )}}
-
-
-
- djledda.de 2023 - { () => 'Contact' } + return () => ( + <> +
+ + Generative Energy Home + + + {{ + default: ({ Component }: { Component: VNode }) => (Component && + ( + + {{ + default: () => Component, + fallback: () =>
Page loading...
, + }} +
+ )), + }} +
+
+
+
+ djledda.de 2023 - {() => "Contact"} +
+
- -
-
-
- ; + + + + ); }, }); diff --git a/app/generative-energy/client.ts b/app/generative-energy/client.ts index 59cdb6e..75b305f 100644 --- a/app/generative-energy/client.ts +++ b/app/generative-energy/client.ts @@ -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"); diff --git a/app/generative-energy/server.ts b/app/generative-energy/server.ts new file mode 100644 index 0000000..13bb249 --- /dev/null +++ b/app/generative-energy/server.ts @@ -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 }; +} diff --git a/app/home/App.tsx b/app/home/App.tsx index b555b8b..59e0476 100644 --- a/app/home/App.tsx +++ b/app/home/App.tsx @@ -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 () => ( -
- -
Count: {count.value}
-
Count Double: {countDouble.value}
-

- Go to this other site hosted here but a different Vue app! -

+ ); }, diff --git a/app/home/client.ts b/app/home/client.ts new file mode 100644 index 0000000..f5df9e9 --- /dev/null +++ b/app/home/client.ts @@ -0,0 +1,4 @@ +import { createSSRApp } from "vue"; +import App from "@/home/App.tsx"; + +createSSRApp(App).mount("#app-root"); diff --git a/app/home/server.ts b/app/home/server.ts new file mode 100644 index 0000000..6809992 --- /dev/null +++ b/app/home/server.ts @@ -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 }; +} diff --git a/app/meta.ts b/app/meta.ts deleted file mode 100644 index d850b9f..0000000 --- a/app/meta.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { useSSRContext, toValue, type MaybeRefOrGetter } from 'vue'; - -export default function useHead(params: { - title: MaybeRefOrGetter, -}) { - const context = useSSRContext(); - if (context) { - context.meta ??= { - title: toValue(params.title), - meta: {}, - }; - } -} diff --git a/app/tooltip.tsx b/app/tooltip.tsx deleted file mode 100644 index c334650..0000000 --- a/app/tooltip.tsx +++ /dev/null @@ -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(null); - const textCarrier = ref(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 () => ( -
- -
- ); - }, -}); diff --git a/app/useAsyncState.ts b/app/useAsyncState.ts index 9fac3ae..08dfce6 100644 --- a/app/useAsyncState.ts +++ b/app/useAsyncState.ts @@ -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>; } -export default function useAsyncState(key: string, getter: (context: { hostUrl: string }) => Promise, options?: { suspensible: boolean }): { result: ShallowRef, stateIsReady: Promise } { - const ssrContext = useSSRContext<{ registry: Record }>(); - const isClient = typeof ssrContext === 'undefined'; +export default function useAsyncState( + key: string, + getter: (context: { hostUrl: string }) => Promise, + options?: { suspensible: boolean }, +): { result: ShallowRef; stateIsReady: Promise } { + 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(null); diff --git a/app/useDJSSRContext.ts b/app/useDJSSRContext.ts new file mode 100644 index 0000000..0f2928f --- /dev/null +++ b/app/useDJSSRContext.ts @@ -0,0 +1,12 @@ +import { type MaybeRefOrGetter, useSSRContext } from "vue"; + +export type DJSSRContext = { + head: { + title: MaybeRefOrGetter; + }; + registry: Record; +}; + +export default function useDJSSRContext() { + return useSSRContext(); +} diff --git a/app/useHead.ts b/app/useHead.ts new file mode 100644 index 0000000..3970ed8 --- /dev/null +++ b/app/useHead.ts @@ -0,0 +1,19 @@ +import { watchEffect, toValue, type MaybeRefOrGetter } from 'vue'; +import useDJSSRContext from "@/useDJSSRContext.ts"; + +export default function useHead(params: { + title: MaybeRefOrGetter, +}) { + const context = useDJSSRContext(); + + if (context) { + context.head.title = params.title; + } else { + watchEffect(() => { + const newTitle = toValue(params.title); + if (newTitle) { + document.title = newTitle; + } + }); + } +} diff --git a/deno.lock b/deno.lock index 5e22e17..af46a80 100644 --- a/deno.lock +++ b/deno.lock @@ -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", diff --git a/deps.ts b/deps.ts index 9a32cbb..86c6a75 100644 --- a/deps.ts +++ b/deps.ts @@ -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"; diff --git a/main.ts b/main.ts index e5b7b98..034165f 100644 --- a/main.ts +++ b/main.ts @@ -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; entryPath: string }) { + return ` + `; +} + +async function* siteEntries(path: string): AsyncGenerator { + 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(``, rendered) - .replace(``, ""); - 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('', ` - - - `) - .replace(``, 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", () => { diff --git a/public/generative-energy/static/content/caffeine.html b/public/generative-energy/content/caffeine.html similarity index 99% rename from public/generative-energy/static/content/caffeine.html rename to public/generative-energy/content/caffeine.html index f0881e9..7dcedff 100644 --- a/public/generative-energy/static/content/caffeine.html +++ b/public/generative-energy/content/caffeine.html @@ -1,7 +1,5 @@ -

- Caffeine: A vitamin-like nutrient, or adaptogen - Koffein: ein vitamin-ähnlicher Nährstoff, oder Adaptogen -

+

Caffeine: A vitamin-like nutrient, or adaptogen

+

Koffein: ein vitamin-ähnlicher Nährstoff, oder Adaptogen

Questions about tea and coffee, cancer and other degenerative diseases, and the hormones. diff --git a/public/generative-energy/static/content/hypothyroidism.html b/public/generative-energy/content/hypothyroidism.html similarity index 100% rename from public/generative-energy/static/content/hypothyroidism.html rename to public/generative-energy/content/hypothyroidism.html diff --git a/public/generative-energy/static/content/hypothyroidism.md b/public/generative-energy/content/hypothyroidism.md similarity index 100% rename from public/generative-energy/static/content/hypothyroidism.md rename to public/generative-energy/content/hypothyroidism.md diff --git a/public/generative-energy/index.html b/public/generative-energy/index_template.html similarity index 91% rename from public/generative-energy/index.html rename to public/generative-energy/index_template.html index a086da4..0da8780 100644 --- a/public/generative-energy/index.html +++ b/public/generative-energy/index_template.html @@ -5,7 +5,7 @@ %TITLE% - + diff --git a/public/generative-energy/static/styles.css b/public/generative-energy/styles.css similarity index 90% rename from public/generative-energy/static/styles.css rename to public/generative-energy/styles.css index 8b30742..e5368dd 100644 --- a/public/generative-energy/static/styles.css +++ b/public/generative-energy/styles.css @@ -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; -} diff --git a/public/home/static/app.js b/public/home/app.js similarity index 100% rename from public/home/static/app.js rename to public/home/app.js diff --git a/public/home/static/icon.webp b/public/home/icon.webp similarity index 100% rename from public/home/static/icon.webp rename to public/home/icon.webp diff --git a/public/home/static/image.jpeg b/public/home/image.jpeg similarity index 100% rename from public/home/static/image.jpeg rename to public/home/image.jpeg diff --git a/public/home/img/.gitignore b/public/home/img/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/public/home/img/daniel.jpg b/public/home/img/daniel.jpg new file mode 100644 index 0000000..d934649 Binary files /dev/null and b/public/home/img/daniel.jpg differ diff --git a/public/home/img/dj.gif b/public/home/img/dj.gif new file mode 100644 index 0000000..7414d3e Binary files /dev/null and b/public/home/img/dj.gif differ diff --git a/public/home/img/icon.png b/public/home/img/icon.png new file mode 100644 index 0000000..8a42581 Binary files /dev/null and b/public/home/img/icon.png differ diff --git a/public/home/img/preis.jpg b/public/home/img/preis.jpg new file mode 100644 index 0000000..cd00e51 Binary files /dev/null and b/public/home/img/preis.jpg differ diff --git a/public/home/img/tile-wide.png b/public/home/img/tile-wide.png new file mode 100644 index 0000000..ccd739c Binary files /dev/null and b/public/home/img/tile-wide.png differ diff --git a/public/home/img/tile.png b/public/home/img/tile.png new file mode 100644 index 0000000..f820f61 Binary files /dev/null and b/public/home/img/tile.png differ diff --git a/public/home/img/tradies.png b/public/home/img/tradies.png new file mode 100644 index 0000000..a9e6be9 Binary files /dev/null and b/public/home/img/tradies.png differ diff --git a/public/home/img/worksafe.png b/public/home/img/worksafe.png new file mode 100644 index 0000000..a7fd53a Binary files /dev/null and b/public/home/img/worksafe.png differ diff --git a/public/home/index.html b/public/home/index.html deleted file mode 100644 index 5bc67d9..0000000 --- a/public/home/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - G'day - - - - - - -

-

G'day, mate!

-

YOUR SITE GOES HERE

- KANGAROO -
-
- - diff --git a/public/home/js/SexyTooltip.ts b/public/home/js/SexyTooltip.ts new file mode 100644 index 0000000..76002cd --- /dev/null +++ b/public/home/js/SexyTooltip.ts @@ -0,0 +1,130 @@ +interface Point { + x: number; + y: number; +} + +interface Size { + height: string; + width: string; +} + +class SexyTooltip { + private static readonly carrierStyle: Partial = { + 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 = { + 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; diff --git a/public/home/js/main.ts b/public/home/js/main.ts new file mode 100644 index 0000000..3f22ebd --- /dev/null +++ b/public/home/js/main.ts @@ -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; +const shakers = document.querySelectorAll(".shakeable") as NodeListOf; +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; +}); diff --git a/public/home/main.css b/public/home/main.css new file mode 100644 index 0000000..80d095d --- /dev/null +++ b/public/home/main.css @@ -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; +} diff --git a/public/home/muenchen-auf-englisch.html b/public/home/muenchen-auf-englisch.html new file mode 100644 index 0000000..368be6e --- /dev/null +++ b/public/home/muenchen-auf-englisch.html @@ -0,0 +1,32 @@ + +- no title specified

Allach

Axleigh

Ahaloh → Allach, Aha → Axe, Loh → Leigh (“Aha + lohe” → “Flusslichtung”)

Altstadt

Oldstead

 

Am Hart

At the Heart

 

Am Moosfeld

At the Mossfield

 

Am Riesenfeld

At the Entfield

 

Au

Ey

as in “Romsey”, “Athelney”, ey /ai/ as in „Island“ with a fake „s“ from Latin

Aubing

Owing

Kein Beleg für Herkunft, erste Ewähnung 16. April 1010 unter “Ubingun”. Bajuwarischer Name “Ubo” könnte naheliegen.

Berg am Laim

Loam Hill

 

Bogenhausen

Bobehouse

Bajuware Pubo, Pupi

Daglfing

Thighelfing

/daɪəlfɪŋ/, Bajuware Tagolf/Thachulf

Denning

Thenning

Bajuware Tenno

Englschalking

Angelshalking

(en. shalk → a servant; Englschalch → “strenger Knecht”)

Fasangarten

Pheasant Garden

 

Feldmoching

Mockingfield

(Feld der Anhänger des Mocho, imagined as en. Mocko)

Forstenried

Forestleigh

 

Freiham

Freeham

 

Freimann

Freeman

 

Fürstenried

Princeleigh

 

Obergiesing

Upper Kising

/kaɪzɪŋ/, Bajuware Kyso → perhaps en. Kyeso (/kaɪzoʊ/)? K and S stay the same, ahd. y is interchangeable with i, probably would have become /aɪ/)

Untergiesing

Lower Kising

 

Hadern

Hathern

/heiðɜ:n/, Von ahd. Haderun (“bei den Waldleuten”)

Holzapfelkreuth

Crabapple Clearing

Kreuth → das Gereutete, nhd. Gerodete, related to “rid” → a clearing, “rid of trees”

Haidhausen

Heathhouse

 

Harlaching

Heathleighing

Von ahd. Hadaleih + ing →gleich: “Heide + lohe + ing”

Hasenbergl

Hare Hill

 

Isarvorstadt

Isar Forestead

 

Johanneskirchen

Johnchurch

en. -kirk → Church

Laim

Loam

Laim = nhd. Lehm = en. Loam

Langwied

Longwood

Wied → ahd. witu “Wald”, en. “wood”

Lehel

Fief

(von nhd. Lehen, cf. en. Loan)

Lochhausen

Leighhouse

 

Ludwigsvorstadt

Lewis Forestead

 

Maxvorstadt

Max Forestead

 

Milbertshofen

Ilvinghope

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.

Moosach

Moorey

Moor + ach (“Au”). R wird zu S durch Rhotazismus (cf. frieren → Frost, verlieren → Verlust, waren → gewesen)

Neuhausen

Newhouse

 

Nymphenburg

Nymphbury

 

Oberföhring

Upper Ferring

Bajuware Fero

Obermenzing

Upper Menting

Bajuware Manzo (wegen -ing umgelautet)

Pasing

Pasing

/peɪzɪŋ/, Bajuware Paoso, Paso

Perlach

Pearley

 

Ramersdorf

Rammlethorpe

/ræməlθɔ:p/ “Rumoltesdorf” ca. 1006. Kein Fugen-S im Englischen

Riem

Rim

cf. “Pacific Rim”

Schwabing

Swaving

/sweɪvɪŋ/, Bajuware Swapo, https://en.wiktionary.org/wiki/Reconstruction:Proto-West_Germanic/sw%C4%81b

Schwanthalerhöhe

Swandale Heights

 

Sendling

Senthling

Bajuware Sentilo

Solln

Soal

Also closely related: en. soil, slough

Steinhausen

Stanhouse

 

Thalkirchen

Dalechurch

 

Trudering

Druthering

/drvθərɪŋ/, Bajuware Truchtaro/Truhtheri/Drudheri/Tructeri

Untermenzing

Lower Menting

 

Zamdorf

Tamthorpe

/tæmθɔ:p/

   

U1

  

Olympia Einkaufszentrum

Olympia Shopping Centre

 

Georg Brauchle Ring

George Brooke Bypass

 

Westfriedhof

West Cemetery

 

Gern

Gar

 

Rotkreuzplatz

Red Cross Square

 

Maillingerstraße

Miling Street

 

Stiglmaierplatz

Stairmayor Square

 

Hauptbahnhof

Central Station

 

Sendlinger Tor

Senthling Gate

 

Fraunhoferstraße

Frenope Street

 

Kolumbusplatz

Columbus Square

 

Candidplatz

Candid Square

 

Wettersteinplatz

Weatherstone Square

 

St. Quirin Platz

St. Quirin Square

 

Mangfallplatz

Manfole Square

„mannigfaltig“, also „Fluss mit vielen Armen“

   

U2

  

Feldmoching

Mockingfield

 

Hasenbergl

Hare Hill

 

Dülferstraße

Eadwulf Street

 

Harthof

Hearthope

 

Am Hart

At the Heart

 

Frankfurter Ring

Frenchford Bypass

 

Milbertshofen

Ilvinghope

 

Scheidplatz

Sheath Square

 

Hohenzollernplatz

Highteller Square

 

Josephsplatz

Joseph Square

 

Theresienstraße

Theresa Street

 

Königsplatz

King Square

 

Silberhornstraße

Silverhorn Street

 

Untersbergstraße

Underberry Street

(? unters?)

Giesing

Kising

 

Karl-Preis-Platz

Carl Price Square

 

Innsbrucker Ring

Ainbridge Bypass

 

Josephsburg

Josephbury

 

Kreillerstraße

Cralle Street

Name Kreiller, Krailler von „Krail, Kreil, Kräuel, Kralle“ Dunghaken, Herkunft unklar

Trudering

Druthering

 

Moosfeld

Mossfield

 

Messestadt West

Exhibition Centre West

 

Messestadt Ost

Exhibition Centre East

 
   

U3

  

Moosacher St.-Martins-Platz

St. Martin Square - Moorey

 

Oberwiesenfeld

Upper Meadowfield

Wiese ist mit „Rasen“ verwandt… im Englischen fehlt leider was dazu

Olympiazentrum

Olympic Centre

 

Petuelring

Pethuel Bypass

von der Familie Petuel … Name aus der Bibel?

Scheidplatz

Sheath Square

Scheid wie Scheidung

Bonner Platz

Bonn Square

 

Münchner Freiheit

Monks Freedom

 

Gisela Straße

Gisela Street

 

Universität

University

 

Odeonsplatz

Odeon Square

 

Marienplatz

Mary Square

 

Goetheplatz

Godfrey Square

 

Poccistraße

Pocci Street

 

Implerstraße

Imple Street

 

Brudermühlstraße

Brothermill Street

 

Obersendling

Upper Senthling

 

Aidenbachstraße

Hadebeck Street

Some guy called Haito..

Machtlfinger Straße

Maghoulfing Street

PN Mahtolf

Forstenrieder Allee

Forestleigh Alley

 

Basler Straße

Basil Street

„Basel“-Herkunft ungeklärt, Römische Herkunft angenommen, also vllt Basilius

Fürstenried West

West Princeleigh

 
   

U5/4

  

Westendstraße

Westend Street

 

Heimeranplatz

Emmeram Square

Sankt Emmeram wurde verhunzt zu „Heimeran“

Theresienwiese

Theresa Grounds

 

Karlsplatz (Stachus)

Karl Square (Eustace)

 

Odeonsplatz

Odeon Square

 

Max-Weber-Platz

Max Weaver Square

TODO Weaver

Prinzregentenplatz

Prince Regent Place

 

Böhmerwaldplatz

Beamwold Square

Wald gegen Böhmen - Der Name leitet sich von dem keltischen Stamm der Boier (Boiohaemum = Heim der Boier, spätlat.: Bohemia) ab.

Richard-Strauss-Straße

Richard Strout Street

Strauss = Ostrich (über Latein, Italienisch, Französisch, dann Englisch)

Arabellapark

Arabella Park

 

Laimer Platz

Loam Square

 

Friedenheimer Straße

Fritham Street

 

Ostbahnhof

East Station

 

Michaelibad

Michael Swimming Pool

TODO Michaeli

Quiddestraße

Woody Street

 

Neuperlach Zentrum

New Pearley Centre

 
   

U6

  

Garching-Forschungszentrum

Ericking Research Centre

 

Garching

Ericking

Gawi-rich, „auenreich“ bzw. „reich an Auen“, k.A. wo das G herkommt… en. Also „ey“ + „rick“ as in „ricky“ for „richard“

Garching-Hochbrück

Ericking Highbridge

 

Fröttmaning

Frithmering

Fridu – mar – ing, bei den friedfertigen, mar wie „Märchen“, „bereit“ oder so…

Kieferngarten

Conifer Garden

Kienföhrengarten – Kienföhre = Pine, cognate to Fir tree, „conifer“ is from Latin and completely etymologically unrelated but oh my god how cool is the resemblance + identical meaning??ß

Studentenstadt

Student Village

 

Alte Heide

Old Heath

 

Nordfriedhof

Northern Cementery

 

Dietlindenstraße

Theedlithe Street

thede, lithe (gelinde, slithy, snake)

Harras

Herrace

 

Partnachplatz

Portney Square

Portn (porta)+ aha

Westpark

West Park

 

Haderner Stern

Hathern Star

 

Großhadern

Greater Hathern

 

Klinikum Großhadern

Greater Hathern Clinic

 
   

S1

  
   

Freising

Freysing

PN Frigis, keltisch

Pulling

Boleigh

 

Neufahrn

Newfare

Niwiwara → die, die neu dazu sich angesiedelt haben, bei den Neukömmlingen (Neuanfahrern)

Eching

Ecking

Ecco, Eho, Echo

Lohhof

Leighope

Possibly lohe – hof ?

Unterschleißheim

Lower Slyham

ahd sliu „Schleie“ perhaps related to en. „slime“

Oberschleißheim

Upper Slyham

 

Feldmoching

Mockingfield

 

Fasanerie

Pheasantry

 

Moosach

Moorey

 
   

S2

  
   

Erding

Arthing

PN Ardeo

Altenerding

Old Arthing

 

Aufhausen

Uphouse

 

St. Koloman

St. Coleman

 

Ottenhofen

Eadenhope

bei den Höfen des Uto (Otto) laut Wikipedia

Markt Schwaben

Swave Market Town

https://en.wiktionary.org/wiki/Reconstruction:Proto-West_Germanic/sw%C4%81b

Poing

Pewing

Bajuware Piuwo

Grub

Grove

 

Heimstetten

Homestead

 

Feldkirchen

Churchfield

 

Riem

Rim

 
   
   
   

Obermenzing

Upper Menting

 

Untermenzing

Lower Menting

 

Allach

Axleigh

 

Karlsfeld

Carlfield

 

Dachau

Thoughey

Ahd Da_ha zu nhd. „Ton“. ae. „tho_he“, Au wie au

Hebertshausen

Herberthouse

 

Röhrmoos

Rushmore

moos mit Röhricht (rush plant), rushmore has ostensibly the same meaning (Moos im bayrischen Raum heißt so viel wie „Moor“)

Vierkirchen-Esterhofen

Fourchurch-Easthope

 

Petershausen

Peterhouse

 

Dachau Stadt

Thoughey Town Centre

 

Bachern

Brooker

Bei den Bachleuten (Bachern)

Schwabhausen

Swavehouse

 

Niederroth

Netherrid

Roth = roden = Kreuth

Markt Indersdorf

Intherthorpe Market Town

Bajuware Undeo/Undio

Arnbach

Arbeck

vermutlich „Ahornbach“

Erdweg

Earthway

 

Kleinberghofen

Cleanberryhope

Klein = clean

Altomünster

Old Minster

Alto- war mal Alten-

   

S3

  
   

Mammendorf

Memthorpe

Erste Erwähnung „Mammindorf“ https://www.historischer-verein-ffb.de/mammendorf/

Malching

Malking

„Mala“ oder „Malak“

Maisach

Mosey

Meise (Titmouse bird) + ach

Gernlinden

Garlithe

cf. Gern, Dietlindenstraße

Esting

Easting

 

Olching

Elking

Ollicho, vgl. Ölschläger

Gröbenzell

Greentell

 

Lochhausen

Leighhouse

 

Langwied

Longwood

 

St.-Martin-Straße

St. Martin Street

 

Giesing

Kising

 

Fasangarten

Pheasant Garden

 

Fasanenpark

Pheasant Park

 

Unterhaching

Lower Haking

 

Taufkirchen

Dopechurch

 

Furth

Ford

 

Deisenhofen

Thisehope

Hof von Tiso

Sauerlach

Sourley

 

Otterfing

Eadlefing

Ot(w)olf, also Eadwulf

Holzkirchen

Holtchurch

 
   

S4

  
   

Geltendorf

Yelderthorpe

Gildulf bzw. Geltolf als Namensgeber

Türkenfeld

Turkfield

 

Grafrath

Reeverede

Reeve = Graf

Schöngeising

Fairckising

Geising = Giesing

Buchenau

Beechey

 

Fürstenfeldbruck

Princefield Bridge

 

Eichenau

Oakey

 

Puchheim

Beechham

„Buchenheim“

Aubing

Owing

 

Leienfelsstraße

Linestone Street

Stadt „Leienfels“ ← „lewenfels“ == Löwenfels

   

S6

  
   

Tutzing

Dutting

PN Tuzo

Feldafing

Ferdulfing

PN Feldwolf

Possenhofen

Boathope

PN Bozo

Starnberg

Starberry

 

Starnberg Nord

North Starberry

 

Gauting

Godding

PN Gudo

Stockdorf

Stickthorpe

 

Planegg

Plainedge

 

Gräfelfing

Revelfing

Graf, PN Grefolf

Lochham

Lockham

 
   

Trudering

Druthering

 

Gronsdorf

Cramanthorpe

„Cramannesdorf“

Haar

Heart

 

Vaterstetten

Fatherstead

 

Baldham

Boltham

Bald – Bedeutung ungeklärt

Zorneding

Arnlething

Ahorngelting oder Zorngelting als „Ursprünge“

Eglharting

Ilehearting

Egel, hart → en. „ile“ ist verwandt…

Kirchseeon

Churchsea

das „on“ ist von der lokativen Endung. „seu“ hieß „See“ und „seun“ dann mit Ortsnamenendung

Grafing Bahnhof

Reeving Station

 

Grafing Stadt

Reeving Town Centre

 

Ebersberg

Barberry

Eber, Berg. Bar appears for „Boar“ in some place names

   

S7

  
   

Wolfratshausen

Wulfredhouse

 

Icking

Icking

PN Ikko

Ebenhausen-Schäftlarn

Evehouse-Shafter

 

Hohenschäftlarn

Highshafter

 

Baierbrunn

Beighbourne

Beigerbrunn, Pairbrunn, etc. belegt

Buchenhain

Beech Grove

 

Höllriegelskreuth

Hellrows Clearing

Lichtung von Herrn Höllriegel → Scherzname für den Teufel

Pullach

Boleigh

Bühel + lohe, lach

Großhesselohe Isartalbahnhof

Greater Hashleigh Eyser Valley Station

 

Solln

Soal

 

Siemenswerke

Symmonsworks

 

Mittersendling

Central Senthling

 

Harras

Herrace

Har = Flax (as in flaxseed), maybe ras as in Rasen/Wasen? Seens to be the case for some other instances of Harras

Heimeranplatz

Emmeram Square

 
   

Perlach

Pearley

 

Neuperlach Süd

South New Pearley

 

Neubiberg

New Byborough

 

Ottobrunn

Eadbourne

 

Hohenbrunn

Highbourne

 

Wächterhof

Guardhope

 

Höhenkirchen-Siegertsbrunn

Highchurch-Sighardbourne

 

Dürrnhaar

Dryheart

 

Aying

Eying

 

Peiß

Bite

PN Biz

Großhelfendorf

Greater Helthorpe

 

Kreuzstraße

Cross Street

 
   

S8

  
   

Herrsching

Harshing

Horscman, horsc → heiter

Seefeld-Hechendorf

Seefield Heighthorpe

Hechen → höhen

Steinebach

Stanbeck

 

Weßling

Wettling

Bajuware Wezil

Neugilching

New Yelthing

Kiltoahinga, vermutlich vom Namen „Geldiko“

Gilching-Argelsried

Yelthing-Arglerid

 

Geisenbrunn

Goatbourne

 

Germering-Unterpfaffenhofen

Germring - Lower Pasterhope

Germering → Germana (as in spanish hermana, i.e. sister). „Germana vel ad monte“ (das Besitztum der Schwester am Berg)

Harthaus

Hearthouse

 

Freiham

Freeham

 

Neuaubing

New Owing

 
   
   

Daglfing

Thighelfing

 

Englschalking

Angelshalking

 

Johanneskirchen

Johnchurch

 

Unterföhring

Lower Ferring

 

Ismaning

Icemanning

Isamanninga → wie Isar

Hallbergmoos

Hallberry Moore

Adelsgeschlecht Hallberg, von Hohlberg

Flughafen Besucherpark

Airport Visitor‘s Centre

 

Flughafen/Airport

Airport

 
   
   
   

Westkreuz

West Cross

 

Pasing

Pasing

 

Laim

Loam

 

Hirschgarten

Deer Park

 

Donnersbergerbrücke

Thunderberry Bridge

 

Hackerbrücke

Hacker Bridge

hard to translate… the job doesn‘t have a name in English, and the english word is also „hack“, and not borrowed. :(

Hauptbahnhof

Central Station

 

Karlsplatz (Stachus)

Carl Square (Eustace)

 

Marienplatz

Mary Square

 

Isartor

Eyser Gate

 

Rosenheimer Platz

Roseham Square

 

Ostbahnhof

East Station

 

Leuchtenbergring

Lightberry Bypass

 

Berg am Laim

Loam Hill

 
   
   
   

Quellen:

  

http://www.koeblergerhard.de/wikiling/index.php?f=gold

  

https://www.bavarikon.de/

  
   

Notizen:

  

PN = Personennamen

  
   
   
\ No newline at end of file diff --git a/public/home/ortsteile_muc_en.html b/public/home/ortsteile_muc_en.html new file mode 100644 index 0000000..fcef94c --- /dev/null +++ b/public/home/ortsteile_muc_en.html @@ -0,0 +1,740 @@ + + + + + + + + + + + +


+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Allach

+
+

Axleigh +

+
+

Ahaloh → Allach, Aha → Axe, Loh → Leigh (“Aha + lohe” + → “Flusslichtung”)

+
+

Altstadt

+
+

Oldstead

+
+


+ +

+
+

Am Hart

+
+

At the Woods

+
+


+ +

+
+

Am Moosfeld

+
+

At the Mossfield

+
+


+ +

+
+

Am + Riesenfeld

+
+

At the Giantfield

+
+


+ +

+
+

Au

+
+

Ey

+
+

as in “Romsey”, + “Athelney”, “Beverley Hills”

+
+

Aubing

+
+

Eving

+
+

Kein Beleg für Herkunft, + erste Ewähnung 16. April 1010 unter “Ubingun”. Bajuwarischer + Name “Ubo” könnte naheliegen.

+
+

Berg am + Laim

+
+

Loam Hill

+
+


+ +

+
+

Bogenhausen

+
+

Bobehouse

+
+

Bajuware Pubo, Pupi

+
+

Daglfing

+
+

Dighelfing /daɪəlfɪŋ/ + +

+
+

Bajuware Tagolf/Thachulf

+
+

Denning

+
+

Denning +

+
+

Bajuware Tenno

+
+

Englschalking

+
+

Angelshalking

+
+

(en. shalk → a + servant; Englschalch → “strenger Knecht”)

+
+

Fasangarten

+
+

Pheasantgarden

+
+


+ +

+
+

Feldmoching

+
+

Field Mocking +

+
+

Feld der Anhänger des + Mocho, imagined as en. Mocko

+
+

Forstenried

+
+

Forestley

+
+


+

+
+

Freiham

+
+

Freeham

+
+


+ +

+
+

Freimann

+
+

Freeman

+
+


+ +

+
+

Fürstenried

+
+

Princeley

+
+


+ +

+
+

Obergiesing

+
+

Upper Kyesing + /kaɪzɪŋ/ + +

+
+

Bajuware Kyso + → perhaps + en. Kyeso + (/kaɪzoʊ/)? + K and S stay the same, ahd. + y + is interchangeable with i, + probably would have + become /aɪ/)

+
+

Untergiesing

+
+

Lower Kyesing

+
+


+ +

+
+

Hadern

+
+

Hathern /heiðɜ:n/

+
+

Von ahd. Haderun + (“bei den + Waldleuten”)

+
+

Holzapfelkreuth +

+
+

Woodapple Glade

+
+

Kreuth → das + Gereutete, nhd. Gerodete, related to “rid” → a + clearing, “rid of trees”

+
+

Haidhausen

+
+

Heathhouse

+
+


+ +

+
+

Harlaching

+
+

Heathleighing +

+
+

Von ahd. Hadaleih + ing →gleich: + “Heide + lohe + ing”

+
+

Hasenbergl

+
+

Hare Hill

+
+


+ +

+
+

Isarvorstadt

+
+

Isar Forestead

+
+


+ +

+
+

Johanneskirchen

+
+

Johnkirk

+
+

en. -kirk → Church

+
+

Laim

+
+

Loam

+
+

Laim = nhd. Lehm = en. + Loam

+
+

Langwied

+
+

Longwood

+
+

Wied → ahd. witu “Wald”, en. “wood”

+
+

Lehel

+
+

Fief +

+
+

(von nhd. Lehen, + cf. en. Loan)

+
+

Lochhausen

+
+

Lockhouse

+
+


+ +

+
+

Ludwigsvorstadt

+
+

Ludwig Forestead

+
+


+ +

+
+

Maxvorstadt

+
+

Max Forestead

+
+


+ +

+
+

Milbertshofen

+
+

Ilvinghope +

+
+

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. +

+
+

Moosach

+
+

Moorey

+
+

Moor + ach (“Au”). R wird zu S durch Rhotazismus (cf. + frieren → Frost, verlieren → Verlust, waren → gewesen)

+
+

Neuhausen

+
+

Newhouse

+
+


+ +

+
+

Nymphenburg

+
+

Nymphbury

+
+


+ +

+
+

Oberföhring

+
+

Upper Fairing

+
+

Bajuware Fero

+
+

Obermenzing

+
+

Upper Menting

+
+

Bajuware Manzo (wegen -ing umgelautet)

+
+

Pasing

+
+

Paising /peɪzɪŋ/

+
+

Bajuware Paoso, Paso

+
+

Perlach

+
+

Pearley

+
+


+ +

+
+

Ramersdorf

+
+

Rammlethorpe /ræməlθɔ:p/ +

+
+

“Rumoltesdorf” ca. 1006. Kein Fugen-S im Englischen

+
+

Riem

+
+

Rim

+
+

cf. “Pacific Rim”

+
+

Schwabing +

+
+

Swabing /sweɪbɪŋ/

+
+

Bajuware Swapo

+
+

Schwanthalerhöhe

+
+

Swandale Heights

+
+


+ +

+
+

Sendling

+
+

Senthling

+
+

Bajuware Sentilo

+
+

Solln

+
+

Soal

+
+

Also closely related: en. soil, slough

+
+

Steinhausen

+
+

Stonehouse

+
+


+ +

+
+

Thalkirchen

+
+

Dalekirk

+
+


+ +

+
+

Trudering

+
+

Droughthring /drauθrɪŋ/ +

+
+

Bajuware Truchtaro/Truhtheri/Drudheri/Tructeri

+
+

Untermenzing

+
+

Lower Menting

+
+


+ +

+
+

Zamdorf

+
+

Tamthorpe /tæmθɔ:p/

+
+


+ +

+
+


+ +

+

Quellen:

+


+ +

+

Wikipedia Seiten zu +den jeweiligen Orten

+


+ +

+

Wiktionary fuer +einige Rueckbildungen (zB Slough, Rim, Longweed, +Loam, Angelshalking)

+


+ +

+

https://en.wikipedia.org/wiki/List_of_generic_forms_in_place_names_in_Ireland_and_the_United_Kingdom

+


+ +

+

https://de.wikipedia.org/wiki/Kategorie:Ortsnamen-Endung

+


+ +

+

http://www.fam-schweden.de/Muenchen/muenchen.html

+


+ +

+

https://de.wikipedia.org/wiki/Zweite_Lautverschiebung#Phase_1

+


+ +

+

https://www.munichkindl.net/besiedlung

+


+ +

+

https://www.gutenberg.org/files/22636/22636-h/22636-h.htm

+


+ +

+


+ +

+


+ +

+ + diff --git a/public/home/static/styles.css b/public/home/static/styles.css deleted file mode 100644 index ef76dde..0000000 --- a/public/home/static/styles.css +++ /dev/null @@ -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; -} diff --git a/public/index2.html b/public/index2.html new file mode 100644 index 0000000..fe76c66 --- /dev/null +++ b/public/index2.html @@ -0,0 +1,88 @@ + + + + + DJ Ledda's Homepage + + + + + + + + + + + + + + + + +
+
+
+ dj legt krasse Mucke auf + I wonder what he's listening to? + DJ Ledda + Easily the coolest guy out there. + dj laying down some sick beats + I once heard this guy played at revs. +
+
+
+
+ + Drum Slayer + + Small app for designing multitrack looped rhythms with local save and multiple files. + Originally built using just vanilla TypeScript and CSS, now with Vue. + + Somaesque + + 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. + + Generative Energy - Ray Peat Resources + + Thyroid calculator, German translations, and more... + + München auf Englisch - Munich in English + + 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. + + K A D I: Online Yatzy Scoresheets + + 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. + + My git projects + + Check out what I'm coding! + + Click here to get in touch + + You'll see my address when you click here. +
+
+
+
+
+
+ + + + + diff --git a/public/index_template.html b/public/index_template.html new file mode 100644 index 0000000..b02ce13 --- /dev/null +++ b/public/index_template.html @@ -0,0 +1,18 @@ + + + + G'day + + + + + + + + +
+ + diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..d0e5f1b --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,5 @@ +# www.robotstxt.org/ + +# Allow crawling of all content +User-agent: * +Disallow: