| @@ -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 () => ( | return () => ( | ||||
| <a href="#" onClick={clickMail}> | <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> | </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({ | export default defineComponent({ | ||||
| name: 'ge-calculator', | |||||
| name: "ge-calculator", | |||||
| setup() { | setup() { | ||||
| const t3Ratio = ref(1); | const t3Ratio = ref(1); | ||||
| const t4Ratio = ref(4); | const t4Ratio = ref(4); | ||||
| @@ -209,12 +22,12 @@ export default defineComponent({ | |||||
| unit: "mg", | unit: "mg", | ||||
| }, | }, | ||||
| { | { | ||||
| name: 'Liothyronine (Triiodothyronine, "Cytomel", T3)', | |||||
| name: 'Liothyronine (Triiodothyronine, "Cytomel/Cynomel", T3)', | |||||
| mpg: MPG_T3_SYN, | mpg: MPG_T3_SYN, | ||||
| unit: "mcg", | unit: "mcg", | ||||
| }, | }, | ||||
| { | { | ||||
| name: 'Levothyroxine (Thyroxine, "Cynoplus", T4)', | |||||
| name: "Levothyroxine (Thyroxine, T4)", | |||||
| mpg: MPG_T4_SYN, | mpg: MPG_T4_SYN, | ||||
| unit: "mcg", | unit: "mcg", | ||||
| }, | }, | ||||
| @@ -222,7 +35,6 @@ export default defineComponent({ | |||||
| const numGrains = ref(2); | const numGrains = ref(2); | ||||
| const ratio = computed(() => t3Ratio.value + t4Ratio.value); | |||||
| const compounded = computed(() => { | const compounded = computed(() => { | ||||
| const total = t3Ratio.value + t4Ratio.value; | const total = t3Ratio.value + t4Ratio.value; | ||||
| const t3Proportion = t3Ratio.value / total; | 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> | <td> | ||||
| { _.name } | |||||
| Synthetic T3/T4 Combo | |||||
| </td> | </td> | ||||
| <td class="right"> | <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> | </div> | ||||
| <span class="breathe">{ _.unit }</span> | |||||
| </td> | </td> | ||||
| </tr> | </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 DJEmail from "@/DJEmail.tsx"; | ||||
| import useHead from "@/useHead.ts"; | |||||
| import useAsyncState from "@/useAsyncState.ts"; | |||||
| import getDJAPI from "@/api.ts"; | |||||
| export default defineComponent({ | 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 useAsyncState from "@/useAsyncState.ts"; | ||||
| import useHead from "@/useHead.ts"; | |||||
| export default defineComponent({ | export default defineComponent({ | ||||
| name: 'ge-deutsch-article', | |||||
| name: "ge-deutsch-article", | |||||
| props: { | props: { | ||||
| articleName: { | articleName: { | ||||
| type: String, | type: String, | ||||
| @@ -17,24 +18,50 @@ export default defineComponent({ | |||||
| currentLang.value = currentLang.value === "en" ? "de" : "en"; | 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 { | function transformArticleNode(node: Node): VNode | string { | ||||
| if (node.nodeType === node.ELEMENT_NODE) { | if (node.nodeType === node.ELEMENT_NODE) { | ||||
| const el = node as Element; | 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++) { | for (let i = 0; i < el.attributes.length; i++) { | ||||
| @@ -46,30 +73,32 @@ export default defineComponent({ | |||||
| return h((node as Element).tagName, attrs, children); | return h((node as Element).tagName, attrs, children); | ||||
| } else { | } else { | ||||
| return createTextVNode(node.textContent ?? ''); | |||||
| return createTextVNode(node.textContent ?? ""); | |||||
| } | } | ||||
| } | } | ||||
| function ArticleContentTransformed() { | function ArticleContentTransformed() { | ||||
| if (articleContent.value) { | if (articleContent.value) { | ||||
| const dom = parseDom(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>; | return <div>Artikel lädt...</div>; | ||||
| } | } | ||||
| await stateIsReady; | 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> | </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 { | export default { | ||||
| name: 'ge-main', | |||||
| name: "ge-main", | |||||
| setup() { | 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 GEMain from "@/generative-energy/GEMain.tsx"; | ||||
| import DJEmail from "@/DJEmail.tsx"; | import DJEmail from "@/DJEmail.tsx"; | ||||
| import GEPaypal from "@/generative-energy/GEPaypal.tsx"; | |||||
| import GEDeutsch from "@/generative-energy/GEDeutsch.tsx"; | import GEDeutsch from "@/generative-energy/GEDeutsch.tsx"; | ||||
| import GEDeutschArticle from "@/generative-energy/GEDeutschArticle.tsx"; | import GEDeutschArticle from "@/generative-energy/GEDeutschArticle.tsx"; | ||||
| import GECalculator from "@/generative-energy/GECalculator.tsx"; | import GECalculator from "@/generative-energy/GECalculator.tsx"; | ||||
| import DJDonate from "@/DJDonate.tsx"; | |||||
| export const routes: RouteRecordRaw[] = [ | export const routes: RouteRecordRaw[] = [ | ||||
| { | { | ||||
| path: '/', | |||||
| name: 'GEMain', | |||||
| path: "/", | |||||
| name: "GEMain", | |||||
| component: GEMain, | component: GEMain, | ||||
| }, | }, | ||||
| { | { | ||||
| path: '/calculator', | |||||
| name: 'GECalculator', | |||||
| path: "/calculator", | |||||
| name: "GECalculator", | |||||
| component: GECalculator, | component: GECalculator, | ||||
| }, | }, | ||||
| { | { | ||||
| path: '/raypeat-deutsch', | |||||
| name: 'GEDeutsch', | |||||
| path: "/raypeat-deutsch", | |||||
| name: "GEDeutsch", | |||||
| component: GEDeutsch, | component: GEDeutsch, | ||||
| }, | }, | ||||
| { | { | ||||
| path: '/raypeat-deutsch/article/:articleName', | |||||
| name: 'GEDeutschArticle', | |||||
| path: "/raypeat-deutsch/article/:articleName", | |||||
| name: "GEDeutschArticle", | |||||
| component: GEDeutschArticle, | component: GEDeutschArticle, | ||||
| props: ({ params }) => { | props: ({ params }) => { | ||||
| if ('articleName' in params) { | |||||
| if ("articleName" in params) { | |||||
| return { articleName: params.articleName }; | return { articleName: params.articleName }; | ||||
| } else { | } else { | ||||
| return false; | return false; | ||||
| @@ -41,27 +41,35 @@ export default defineComponent({ | |||||
| name: "ge-root", | name: "ge-root", | ||||
| setup() { | setup() { | ||||
| const route = useRoute(); | 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> | </div> | ||||
| <GEPaypal /> | |||||
| </div> | |||||
| </footer> | |||||
| </main> | |||||
| </>; | |||||
| </footer> | |||||
| </main> | |||||
| </> | |||||
| ); | |||||
| }, | }, | ||||
| }); | }); | ||||
| @@ -4,7 +4,7 @@ import GERoot, { routes } from "@/generative-energy/GERoot.tsx"; | |||||
| createSSRApp(GERoot) | createSSRApp(GERoot) | ||||
| .use(createRouter({ | .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({ | export default defineComponent({ | ||||
| name: "app-root", | name: "app-root", | ||||
| setup() { | setup() { | ||||
| const count = ref(0); | |||||
| const countDouble = computed(() => count.value * 2); | |||||
| count.value++; | |||||
| useHead({ title: "DJ Ledda's Homepage" }); | |||||
| return () => ( | 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> | </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 { | declare global { | ||||
| // deno-lint-ignore no-var | // deno-lint-ignore no-var | ||||
| var appstate: Partial<Record<string, unknown>>; | 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 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); | 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/encoding@^1.0.5": "1.0.5", | ||||
| "jsr:@std/fmt@0.223": "0.223.0", | "jsr:@std/fmt@0.223": "0.223.0", | ||||
| "jsr:@std/fmt@^1.0.3": "1.0.3", | "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/fs@0.223": "0.223.0", | ||||
| "jsr:@std/http@*": "1.0.9", | "jsr:@std/http@*": "1.0.9", | ||||
| "jsr:@std/io@0.223": "0.223.0", | "jsr:@std/io@0.223": "0.223.0", | ||||
| "jsr:@std/media-types@^1.0.3": "1.0.3", | "jsr:@std/media-types@^1.0.3": "1.0.3", | ||||
| "jsr:@std/net@^1.0.4": "1.0.4", | "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@0.223": "0.223.0", | ||||
| "jsr:@std/path@^1.0.7": "1.0.7", | "jsr:@std/path@^1.0.7": "1.0.7", | ||||
| "jsr:@std/streams@^1.0.7": "1.0.7", | "jsr:@std/streams@^1.0.7": "1.0.7", | ||||
| @@ -34,7 +36,7 @@ | |||||
| "dependencies": [ | "dependencies": [ | ||||
| "jsr:@deno/graph", | "jsr:@deno/graph", | ||||
| "jsr:@std/fmt@0.223", | "jsr:@std/fmt@0.223", | ||||
| "jsr:@std/fs", | |||||
| "jsr:@std/fs@0.223", | |||||
| "jsr:@std/io", | "jsr:@std/io", | ||||
| "jsr:@std/path@0.223" | "jsr:@std/path@0.223" | ||||
| ] | ] | ||||
| @@ -68,7 +70,11 @@ | |||||
| "integrity": "97765c16aa32245ff4e2204ecf7d8562496a3cb8592340a80e7e554e0bb9149f" | "integrity": "97765c16aa32245ff4e2204ecf7d8562496a3cb8592340a80e7e554e0bb9149f" | ||||
| }, | }, | ||||
| "@std/fs@0.223.0": { | "@std/fs@0.223.0": { | ||||
| "integrity": "3b4b0550b2c524cbaaa5a9170c90e96cbb7354e837ad1bdaf15fc9df1ae9c31c" | |||||
| "integrity": "3b4b0550b2c524cbaaa5a9170c90e96cbb7354e837ad1bdaf15fc9df1ae9c31c", | |||||
| "dependencies": [ | |||||
| "jsr:@std/assert", | |||||
| "jsr:@std/path@0.223" | |||||
| ] | |||||
| }, | }, | ||||
| "@std/http@1.0.9": { | "@std/http@1.0.9": { | ||||
| "integrity": "d409fc319a5e8d4a154e576c758752e9700282d74f31357a12fec6420f9ecb6c", | "integrity": "d409fc319a5e8d4a154e576c758752e9700282d74f31357a12fec6420f9ecb6c", | ||||
| @@ -1,4 +1,6 @@ | |||||
| import "jsr:@deno/emit"; | import "jsr:@deno/emit"; | ||||
| import "jsr:@std/http"; | import "jsr:@std/http"; | ||||
| import "vue"; | 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 { 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 { 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 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 utf8Decoder = new TextDecoder("utf-8"); | ||||
| const parser = new DOMParser(); | 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({ | Deno.serve({ | ||||
| port: 8080, | port: 8080, | ||||
| hostname: "0.0.0.0", | hostname: "0.0.0.0", | ||||
| @@ -18,63 +87,78 @@ Deno.serve({ | |||||
| console.log(`Listening on port http://${hostname}:${port}/`); | console.log(`Listening on port http://${hostname}:${port}/`); | ||||
| }, | }, | ||||
| }, async (req, _conn) => { | }, async (req, _conn) => { | ||||
| let response: Response | null = null; | |||||
| if (req.method === "GET") { | if (req.method === "GET") { | ||||
| const pathname = URL.parse(req.url)?.pathname ?? "/"; | 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 { | } 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", () => { | 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> | <p> | ||||
| <span lang="en"><strong>Questions about tea and coffee, cancer and other degenerative diseases, and the hormones.</strong></span> | <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"> | <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
| <title>%TITLE%</title> | <title>%TITLE%</title> | ||||
| <link rel="stylesheet" href="/generative-energy/static/styles.css"> | |||||
| <link rel="stylesheet" href="/generative-energy/styles.css"> | |||||
| <link | <link | ||||
| href="https://fonts.googleapis.com/css2?family=Roboto&family=Roboto+Slab:wght@600&display=swap" | href="https://fonts.googleapis.com/css2?family=Roboto&family=Roboto+Slab:wght@600&display=swap" | ||||
| rel="stylesheet"> | rel="stylesheet"> | ||||
| @@ -139,17 +139,26 @@ hr { | |||||
| background: #999; | background: #999; | ||||
| } | } | ||||
| footer .bottom { | |||||
| display: flex; | |||||
| flex-direction: row; | |||||
| justify-content: space-between; | |||||
| } | |||||
| footer { | footer { | ||||
| margin-bottom: 20px; | 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 { | .ge-calculator { | ||||
| table { | 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: | |||||