Kaynağa Gözat

updatge

master
Daniel Ledda 2 ay önce
ebeveyn
işleme
7539e6ed48
50 değiştirilmiş dosya ile 1999 ekleme ve 745 silme
  1. +16
    -0
      app/DJDonate.tsx
  2. +1
    -1
      app/DJEmail.tsx
  3. +80
    -0
      app/DJTooltip.tsx
  4. +14
    -0
      app/api.ts
  5. +132
    -303
      app/generative-energy/GECalculator.tsx
  6. +72
    -57
      app/generative-energy/GEDeutsch.tsx
  7. +57
    -28
      app/generative-energy/GEDeutschArticle.tsx
  8. +34
    -28
      app/generative-energy/GEMain.tsx
  9. +0
    -21
      app/generative-energy/GEPaypal.tsx
  10. +41
    -33
      app/generative-energy/GERoot.tsx
  11. +3
    -3
      app/generative-energy/client.ts
  12. +12
    -0
      app/generative-energy/server.ts
  13. +67
    -11
      app/home/App.tsx
  14. +4
    -0
      app/home/client.ts
  15. +7
    -0
      app/home/server.ts
  16. +0
    -13
      app/meta.ts
  17. +0
    -122
      app/tooltip.tsx
  18. +10
    -5
      app/useAsyncState.ts
  19. +12
    -0
      app/useDJSSRContext.ts
  20. +19
    -0
      app/useHead.ts
  21. +8
    -2
      deno.lock
  22. +3
    -1
      deps.ts
  23. +139
    -55
      main.ts
  24. +2
    -4
      public/generative-energy/content/caffeine.html
  25. +0
    -0
      public/generative-energy/content/hypothyroidism.html
  26. +0
    -0
      public/generative-energy/content/hypothyroidism.md
  27. +1
    -1
      public/generative-energy/index_template.html
  28. +16
    -17
      public/generative-energy/styles.css
  29. +0
    -0
      public/home/app.js
  30. +0
    -0
      public/home/icon.webp
  31. +0
    -0
      public/home/image.jpeg
  32. +0
    -0
      public/home/img/.gitignore
  33. BIN
      public/home/img/daniel.jpg
  34. BIN
      public/home/img/dj.gif
  35. BIN
      public/home/img/icon.png
  36. BIN
      public/home/img/preis.jpg
  37. BIN
      public/home/img/tile-wide.png
  38. BIN
      public/home/img/tile.png
  39. BIN
      public/home/img/tradies.png
  40. BIN
      public/home/img/worksafe.png
  41. +0
    -18
      public/home/index.html
  42. +130
    -0
      public/home/js/SexyTooltip.ts
  43. +47
    -0
      public/home/js/main.ts
  44. +189
    -0
      public/home/main.css
  45. +32
    -0
      public/home/muenchen-auf-englisch.html
  46. +740
    -0
      public/home/ortsteile_muc_en.html
  47. +0
    -22
      public/home/static/styles.css
  48. +88
    -0
      public/index2.html
  49. +18
    -0
      public/index_template.html
  50. +5
    -0
      public/robots.txt

+ 16
- 0
app/DJDonate.tsx Dosyayı Görüntüle

@@ -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>
);
},
});

+ 1
- 1
app/DJEmail.tsx Dosyayı Görüntüle

@@ -14,7 +14,7 @@ export default defineComponent({
}
return () => (
<a href="#" onClick={clickMail}>
{ slots.default ? slots.default() : 'dan.j.ledda [at] gmail [dot] com' }
{slots.default ? slots.default() : "dan.j.ledda [at] gmail [dot] com"}
</a>
);
},


+ 80
- 0
app/DJTooltip.tsx Dosyayı Görüntüle

@@ -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>
</>;
},
});

+ 14
- 0
app/api.ts Dosyayı Görüntüle

@@ -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();
}

+ 132
- 303
app/generative-energy/GECalculator.tsx Dosyayı Görüntüle

@@ -1,194 +1,7 @@
import { defineComponent, computed, ref } from 'vue';

/*
class GrainInput {
static inputs = [];
name;
unit;
mpg;
inputEl;
reference;

* @param {{
* name: string,
* mpg: number,
* reference: GrainInput | 'self',
* unit: string,
* step?: number,
* }} opts
constructor(opts) {
this.name = opts.name;
this.mpg = opts.mpg;
this.unit = opts.unit;
this.reference = opts.reference === "self" ? this : opts.reference;
this.inputEl = h("input", { type: "number", min: 0, step: opts.step ?? 1 });
eventBus.addListener(this);
}

attachInput(insertionPoint) {
insertionPoint.appendChild(this.inputEl);
this.inputEl.valueAsNumber = this.mpg;
this.inputEl.addEventListener("input", (e) => {
const newVal = (e.currentTarget)?.valueAsNumber ?? 0;
if (this !== this.reference) {
this.reference.inputEl.valueAsNumber = newVal / this.mpg;
}
eventBus.sendChange(this);
});
}

onChange() {
if (!this.reference || this === this.reference) return;
this.inputEl.valueAsNumber = this.mpg * this.reference.inputEl.valueAsNumber;
}

getCurrentValue() {
return this.inputEl.valueAsNumber;
}
}

class RatiosController {
t3Ratio = h("input", { type: "number", min: 0, step: 1 });
t4Ratio = h("input", { type: "number", min: 0, step: 1 });
t3Syn = h("input", { type: "number", min: 0 });
t4Syn = h("input", { type: "number", min: 0 });
t3 = 1;
t4 = 4;
reference;

constructor(referenceInput) {
this.reference = referenceInput;
}

attachInputs(inputs) {
inputs.t3Ratio.replaceWith(this.t3Ratio);
inputs.t4Ratio.replaceWith(this.t4Ratio);
inputs.t3Syn.replaceWith(this.t3Syn);
inputs.t4Syn.replaceWith(this.t4Syn);

this.t3Ratio.addEventListener("input", (e) => {
const newVal = (e.currentTarget)?.valueAsNumber ?? 0;
this.t3 = newVal;
this.onChange();
});

this.t4Ratio.addEventListener("input", (e) => {
const newVal = (e.currentTarget)?.valueAsNumber ?? 0;
this.t4 = newVal;
this.onChange();
});

this.t3Syn.addEventListener("input", (e) => {
const newVal = (e.currentTarget)?.valueAsNumber ?? 0;
this.reference.inputEl.valueAsNumber = newVal / MPG_T3_SYN + this.t4Syn.valueAsNumber / MPG_T4_SYN;
this.t3 = newVal;
this.t4 = this.t4Syn.valueAsNumber;
this.updateRatio();
this.updateSyn();
eventBus.sendChange(this);
});

this.t4Syn.addEventListener("input", (e) => {
const newVal = (e.currentTarget)?.valueAsNumber ?? 0;
this.reference.inputEl.valueAsNumber = newVal / MPG_T4_SYN + this.t3Syn.valueAsNumber / MPG_T3_SYN;
this.t3 = this.t3Syn.valueAsNumber;
this.t4 = newVal;
this.updateRatio();
this.updateSyn();
eventBus.sendChange(this);
});

eventBus.addListener(this);
this.onChange();
}

onChange() {
this.updateRatio();
this.updateSyn();
}

updateRatio() {
this.t3Ratio.valueAsNumber = this.t3;
this.t4Ratio.valueAsNumber = this.t4;
}

updateSyn() {
const total = this.t3 + this.t4;
const t3Proportion = this.t3 / total;
const t4Proportion = this.t4 / total;
const grainsSyn = t3Proportion / MPG_T3_SYN + t4Proportion / MPG_T4_SYN;
const multiplierSyn = this.reference.getCurrentValue() / grainsSyn;
this.t3Syn.valueAsNumber = t3Proportion * multiplierSyn;
this.t4Syn.valueAsNumber = t4Proportion * multiplierSyn;
}
}

class ThyroidConverter extends DJElement {
static styles = css``;

static template = html;

constructor() {
super();
const mainGrainInput = new GrainInput({
name: "Grains",
mpg: 1,
reference: "self",
unit: "",
step: 0.1,
});

const ratiosController = new RatiosController(mainGrainInput);

const inputs = [
mainGrainInput,
new GrainInput({
name: "Armour, Natural Dessicated Thyroid",
mpg: 60,
unit: "mg",
reference: mainGrainInput,
}),
new GrainInput({
name: 'Liothyronine (Triiodothyronine, "Cytomel", T3)',
mpg: MPG_T3_SYN,
unit: "mcg",
reference: mainGrainInput,
}),
new GrainInput({
name: 'Levothyroxine (Thyroxine, "Cynoplus", T4)',
mpg: MPG_T4_SYN,
unit: "mcg",
reference: mainGrainInput,
}),
];

const tableBody = qs("#table-body", this.root);
const compoundedStart = qs("#compounded-start", this.root);

for (const field of inputs) {
const newRow = h("tr", {}, [
h("td", { textContent: field.name }),
h("td", {}, [
h("div", { className: "input", style: "display: inline-block;" }),
h("span", { className: "breathe", textContent: field.unit }),
]),
]);
field.attachInput(qs("div.input", newRow));
tableBody.insertBefore(newRow, compoundedStart);
}

ratiosController.attachInputs({
t3Ratio: qs(".ratios .t3", tableBody),
t4Ratio: qs(".ratios .t4", tableBody),
t3Syn: qs(".synthetic .t3", tableBody),
t4Syn: qs(".synthetic .t4", tableBody),
});
}
}
*/
import { computed, defineComponent, ref } from "vue";

export default defineComponent({
name: 'ge-calculator',
name: "ge-calculator",
setup() {
const t3Ratio = ref(1);
const t4Ratio = ref(4);
@@ -209,12 +22,12 @@ export default defineComponent({
unit: "mg",
},
{
name: 'Liothyronine (Triiodothyronine, "Cytomel", T3)',
name: 'Liothyronine (Triiodothyronine, "Cytomel/Cynomel", T3)',
mpg: MPG_T3_SYN,
unit: "mcg",
},
{
name: 'Levothyroxine (Thyroxine, "Cynoplus", T4)',
name: "Levothyroxine (Thyroxine, T4)",
mpg: MPG_T4_SYN,
unit: "mcg",
},
@@ -222,7 +35,6 @@ export default defineComponent({

const numGrains = ref(2);

const ratio = computed(() => t3Ratio.value + t4Ratio.value);
const compounded = computed(() => {
const total = t3Ratio.value + t4Ratio.value;
const t3Proportion = t3Ratio.value / total;
@@ -235,121 +47,138 @@ export default defineComponent({
};
});

return () => <>
<header>
<h1>Thyroid Calculator</h1>
</header>
<div class="text-slab">
<p>
Use this calculator to convert between different forms and units of thyroid hormone. Common
synthetic, pure and natural dessicated forms are listed. The last section of the table provides
input fields for a specified ratio of T3 to T4, and will give the dosages required for both the
synthetic and pure forms. All dosages given are based on the "Grains" field at the beginning of the
table, but editing any field will automatically update the rest to keep them in sync.
</p>
</div>
<div class="text-slab ge-calculator">
<table>
<tbody>
{ inputDefs.map((_, i) => (
<tr key={_.name}>
return () => (
<>
<header>
<h1>Thyroid Calculator</h1>
</header>
<div class="text-slab">
<p>
Use this calculator to convert between different forms and units of thyroid hormone. Common
synthetic, pure and natural dessicated forms are listed. The last section of the table provides
input fields for a specified ratio of T3 to T4, and will give the dosages required for both the
synthetic and pure forms. All dosages given are based on the "Grains" field at the beginning of
the table, but editing any field will automatically update the rest to keep them in sync.
</p>
</div>
<div class="text-slab ge-calculator">
<table>
<tbody>
{inputDefs.map((_) => (
<tr key={_.name}>
<td>
{_.name}
</td>
<td class="right">
<div style="display: inline-block;">
<input
value={_.name === "Grains" ? numGrains.value : _.mpg * numGrains.value}
onInput={(e) => {
const val = (e.target as HTMLInputElement).valueAsNumber;
if (_.name === "Grains") {
numGrains.value = val;
} else {
numGrains.value = _.mpg / val;
}
}}
min="0"
step={_.step ?? 1}
type="number"
/>
</div>
<span class="breathe">{_.unit}</span>
</td>
</tr>
))}
<tr>
<td colspan="2">
<strong>Compounded (T3 and T4, "Cynoplus")</strong>
</td>
</tr>
<tr class="ratios">
<td>
Desired Ratio (T3:T4)
</td>
<td class="right">
<div>
<input
value={t3Ratio.value}
onInput={(e) => {
t3Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber;
}}
min="0"
step="1"
type="number"
/>
</div>{" "}
:{" "}
<div>
<input
value={t4Ratio.value}
onInput={(e) => {
t4Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber;
}}
min="0"
step="1"
type="number"
/>
</div>
</td>
</tr>
<tr class="synthetic">
<td>
{ _.name }
Synthetic T3/T4 Combo
</td>
<td class="right">
<div style="display: inline-block;">
<input
value={_.name === 'Grains' ? numGrains.value : _.mpg * numGrains.value}
onInput={(e) => {
const val = (e.target as HTMLInputElement).valueAsNumber;
if (_.name === 'Grains') {
numGrains.value = val;
} else {
numGrains.value = _.mpg / val;
}
}}
min="0"
step={ _.step ?? 1 }
type="number" />
<div>
<input
value={compounded.value.t3Syn}
onInput={(e) => {
t4Ratio.value = compounded.value.t4Syn;
t3Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber;
numGrains.value = t3Ratio.value / MPG_T3_SYN +
t4Ratio.value / MPG_T4_SYN;
}}
min="0"
step="1"
type="number"
/>
</div>{" "}
:{" "}
<div>
<input
value={compounded.value.t4Syn}
onInput={(e) => {
t3Ratio.value = compounded.value.t3Syn;
t4Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber;
numGrains.value = t3Ratio.value / MPG_T3_SYN +
t4Ratio.value / MPG_T4_SYN;
}}
min="0"
step="1"
type="number"
/>
</div>
<span class="breathe">{ _.unit }</span>
</td>
</tr>
))}
<tr><td colspan="2"><strong>Compounded (T3 and T4, "Cynpolus")</strong></td></tr>
<tr class="ratios">
<td>
Desired Ratio (T3:T4)
</td>
<td class="right">
<div>
<input
value={t3Ratio.value}
onInput={(e) => {
t3Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber;
}}
min="0"
step="1"
type="number" />
</div> : <div>
<input
value={t4Ratio.value}
onInput={(e) => {
t4Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber;
}}
min="0"
step="1"
type="number" />
</div>
</td>
</tr>
<tr class="synthetic">
<td>
Synthetic T3/T4 Combo
</td>
<td class="right">
<div>
<input
value={compounded.value.t3Syn}
onInput={(e) => {
t4Ratio.value = compounded.value.t4Syn;
t3Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber;
numGrains.value = t3Ratio.value / MPG_T3_SYN + t4Ratio.value / MPG_T4_SYN;
}}
min="0"
step="1"
type="number" />
</div> : <div>
<input
value={compounded.value.t4Syn}
onInput={(e) => {
t3Ratio.value = compounded.value.t3Syn;
t4Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber;
numGrains.value = t3Ratio.value / MPG_T3_SYN + t4Ratio.value / MPG_T4_SYN;
}}
min="0"
step="1"
type="number" />
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="text-slab">
<strong>Changelog:</strong>
<p>
<ul>
<li>
November 2024: Migrated to new web framework and fixed some buggy input.
</li>
<li>
13th March 2024: Removed the synthetic/pure distinction as it was confusing and unnecessary
bloat.
</li>
</ul>
</p>
</div>
</>;
}
</tbody>
</table>
</div>
<div class="text-slab">
<strong>Changelog:</strong>
<p>
<ul>
<li>
November 2024: Migrated to new web framework and fixed some buggy input.
</li>
<li>
13th March 2024: Removed the synthetic/pure distinction as it was confusing and
unnecessary bloat.
</li>
</ul>
</p>
</div>
</>
);
},
});

+ 72
- 57
app/generative-energy/GEDeutsch.tsx Dosyayı Görüntüle

@@ -1,61 +1,76 @@
import { defineComponent } from 'vue';
import { RouterLink } from 'vue-router';
import { defineComponent } from "vue";
import { RouterLink } from "vue-router";
import DJEmail from "@/DJEmail.tsx";
import useHead from "@/useHead.ts";
import useAsyncState from "@/useAsyncState.ts";
import getDJAPI from "@/api.ts";

export default defineComponent({
name: 'ge-deutsch',
setup() {
return () => <>
<header>
<h1>Ray Peat Deutsche Übersetzungen</h1>
</header>
<div class="text-slab">
<p>
Auf dieser Seite befindet sich eine Auswahl der Artikel von Dr. Raymond Peat, die ich in meiner
Freizeit ins Deutsche übersetzt habe.
</p>
<p>
Ray Peat war ein US-Amerikaner aus Eugene, Oregon, der neben seinem Studium von Linguistik,
Literatur, und Malerei eine Promotion in der Biologie und Physiologie des Alterns und degenerativer
Krankheiten absolvierte, mit besonderem Fokus auf Zellen-Stoffwechsel, Hormone, und
Ernährungsphysiologie.
</p>
<p>
Nach mehreren Jahren als Professor an diversen Universitäten in den USA und Mexiko begann er Artikel
in Form eines Newsletters herauszugeben, von denen mehrere auf <a href="http://raypeat.com/"
>seiner Website</a> zu finden sind.
</p>
<p>
Da er in meinem Leben zu einem enormen Verständnis meines Körpers und einem unglaublichen
Zurückerlangen meiner Gesundheit und meines Wohlbefindens trotz dem Scheitern mehrerer Ärzte
beigetragen hat, und sein Nachlass in Deutschland kaum Publikum aufgrund schwieriger physiologischer
Texte genießt, habe ich entschieden hauptsächlich für Freunde und Bekannte einige seiner Artikel ins
Deutsche zu übersetzen, damit vielleicht auch ein breiteres Publikum von seinen Ideen profitieren
könnte.
</p>
<p>
Falls was bei der Übersetzung auffällt oder besonders unidiomatisch klingt, bzw. der deutschen
Fachsprache der Medizin nicht gerecht sein sollte, kannst du mir unter <DJEmail /> eine Mail senden.
Meine Muttersprache ist schließlich Englisch und ich bin kein professioneller Übersetzer!
</p>
<p>
Zusätzlich zu der Funktion, den Artikel mit dem Button oben in der Originalversion anzusehen, hat
jeder Absatz oben rechts beim drüberhovern mit der Maus einen zusätzlichen Button über den man den
jeweiligen Absatz in die andere Sprache wechseln kann, um die Versionen beim Lesen schnell zu
vergleichen zu können.
</p>
</div>
<div class="text-slab">
<h3>Artikel auswählen:</h3>
<ul id="article">
<li>
<RouterLink to={{ name: 'GEDeutschArticle', params: { articleName: 'hypothyroidism'}}}>Indikatoren Schilddrüsenunterfunktion</RouterLink>
</li>
<li>
<RouterLink to={{ name: 'GEDeutschArticle', params: { articleName: 'caffeine'}}}>Koffein</RouterLink>
</li>
</ul>
</div>
</>;
}
name: "ge-deutsch",
async setup() {
useHead({ title: "Ray Peat Artikel auf Deutsch" });

const {
result: rpArticles,
stateIsReady,
} = useAsyncState("rp-articles", ({ hostUrl }) => getDJAPI(hostUrl, "/rp-articles"));

await stateIsReady;
return () => (
<>
<header>
<h1>Ray Peat Deutsche Übersetzungen</h1>
</header>
<div class="text-slab">
<p>
Auf dieser Seite befindet sich eine Auswahl der Artikel von Dr. Raymond Peat, die ich in meiner
Freizeit ins Deutsche übersetzt habe.
</p>
<p>
Ray Peat war ein US-Amerikaner aus Eugene, Oregon, der neben seinem Studium von Linguistik,
Literatur, und Malerei eine Promotion in der Biologie und Physiologie des Alterns und
degenerativer Krankheiten absolvierte, mit besonderem Fokus auf Zellen-Stoffwechsel, Hormone,
und Ernährungsphysiologie.
</p>
<p>
Nach mehreren Jahren als Professor an diversen Universitäten in den USA und Mexiko begann er
Artikel in Form eines Newsletters herauszugeben, von denen mehrere auf{" "}
<a href="http://raypeat.com/">seiner Website</a> zu finden sind.
</p>
<p>
Da er in meinem Leben zu einem enormen Verständnis meines Körpers und einem unglaublichen
Zurückerlangen meiner Gesundheit und meines Wohlbefindens trotz dem Scheitern mehrerer Ärzte
beigetragen hat, und sein Nachlass in Deutschland kaum Publikum aufgrund schwieriger
physiologischer Texte genießt, habe ich entschieden hauptsächlich für Freunde und Bekannte
einige seiner Artikel ins Deutsche zu übersetzen, damit vielleicht auch ein breiteres Publikum
von seinen Ideen profitieren könnte.
</p>
<p>
Falls was bei der Übersetzung auffällt oder besonders unidiomatisch klingt, bzw. der deutschen
Fachsprache der Medizin nicht gerecht sein sollte, kannst du mir unter <DJEmail />{" "}
eine Mail senden. Meine Muttersprache ist schließlich Englisch und ich bin kein professioneller
Übersetzer!
</p>
<p>
Zusätzlich zu der Funktion, den Artikel mit dem Button oben in der Originalversion anzusehen,
hat jeder Absatz oben rechts beim drüberhovern mit der Maus einen zusätzlichen Button über den
man den jeweiligen Absatz in die andere Sprache wechseln kann, um die Versionen beim Lesen
schnell zu vergleichen zu können.
</p>
</div>
<div class="text-slab">
<h3>Artikel auswählen:</h3>
<ul id="article">
{rpArticles.value && rpArticles.value.map((_) => (
<li>
<RouterLink to={{ name: "GEDeutschArticle", params: { articleName: _.slug } }}>
{_.name}
</RouterLink>
</li>
))}
</ul>
</div>
</>
);
},
});

+ 57
- 28
app/generative-energy/GEDeutschArticle.tsx Dosyayı Görüntüle

@@ -1,9 +1,10 @@
import { h, inject, defineComponent, ref, createTextVNode, type VNode } from 'vue';
import { RouterLink } from 'vue-router';
import { createTextVNode, defineComponent, h, inject, onServerPrefetch, ref, type VNode, watchEffect } from "vue";
import { RouterLink } from "vue-router";
import useAsyncState from "@/useAsyncState.ts";
import useHead from "@/useHead.ts";

export default defineComponent({
name: 'ge-deutsch-article',
name: "ge-deutsch-article",
props: {
articleName: {
type: String,
@@ -17,24 +18,50 @@ export default defineComponent({
currentLang.value = currentLang.value === "en" ? "de" : "en";
}

const parseDom = inject('dom-parse', (innerHTML: string) => Object.assign(document.createElement('div'), { innerHTML }));
const parseDom = inject(
"dom-parse",
(innerHTML: string) => Object.assign(document.createElement("div"), { innerHTML }),
);

const { result: articleContent, stateIsReady } = useAsyncState('ge-deutsch-article-data', async ({ hostUrl }) => {
const articleResponse = await fetch(`${ hostUrl }/generative-energy/static/content/${ props.articleName }.html`);
return await articleResponse.text();
});
const title = ref("");

const { result: articleContent, stateIsReady } = useAsyncState(
"ge-deutsch-article-data",
async ({ hostUrl }) => {
const articleResponse = await fetch(`${hostUrl}/generative-energy/content/${props.articleName}.html`);
const result = await articleResponse.text();
title.value = result.split('<h1 lang="de">')[1].split("</h1>")[0];
return result;
},
);

useHead({ title });

onServerPrefetch(() =>
new Promise<void>((res) => {
watchEffect(() => {
if (title.value !== "") {
console.log("resolve", title.value);
res();
}
});
})
);

function transformArticleNode(node: Node): VNode | string {
if (node.nodeType === node.ELEMENT_NODE) {
const el = node as Element;
const attrs: Record<string, string> = {};
const children = [...node.childNodes].map(_ => transformArticleNode(_));
const attrs: Record<string, string> = {};
const children = [...node.childNodes].map((_) => transformArticleNode(_));

if (el.tagName === 'P') {
el.classList.add('text-slab');
children.unshift(h('button', { class: 'swap', onClick: (e) => {
e.target.parentElement.classList.toggle('swap');
} }, '↻'));
if (el.tagName === "P") {
el.classList.add("text-slab");
children.unshift(h("button", {
class: "swap",
onClick: (e) => {
e.target.parentElement.classList.toggle("swap");
},
}, "↻"));
}

for (let i = 0; i < el.attributes.length; i++) {
@@ -46,30 +73,32 @@ export default defineComponent({

return h((node as Element).tagName, attrs, children);
} else {
return createTextVNode(node.textContent ?? '');
return createTextVNode(node.textContent ?? "");
}
}

function ArticleContentTransformed() {
if (articleContent.value) {
const dom = parseDom(articleContent.value);
return h('div', {}, [...dom.children].map(_ => transformArticleNode(_)));
return h("div", {}, [...dom.children].map((_) => transformArticleNode(_)));
}
return <div>Artikel lädt...</div>;
}

await stateIsReady;

return () => <div class="ge-article">
<div class="header">
<RouterLink to={{ name: 'GEDeutsch' }}>Zur Artikelübersicht</RouterLink>
<button onClick={clickBtn}>
Sprache auf <span>{currentLang.value === "en" ? "Deutsch" : "Englisch"}</span> umschalten
</button>
return () => (
<div class="ge-article">
<div class="header">
<RouterLink to={{ name: "GEDeutsch" }}>Zur Artikelübersicht</RouterLink>
<button onClick={clickBtn}>
Sprache auf <span>{currentLang.value === "en" ? "Deutsch" : "Englisch"}</span> umschalten
</button>
</div>
<article class={`lang-${currentLang.value}`}>
<ArticleContentTransformed />
</article>
</div>
<article class={`lang-${ currentLang.value }`}>
<ArticleContentTransformed />
</article>
</div>;
}
);
},
});

+ 34
- 28
app/generative-energy/GEMain.tsx Dosyayı Görüntüle

@@ -1,33 +1,39 @@
import { RouterLink } from 'vue-router';
import { RouterLink } from "vue-router";
import useHead from "@/useHead.ts";

export default {
name: 'ge-main',
name: "ge-main",
setup() {
return () => <>
<header>
<h1>Generative Energy</h1>
</header>
<div class="text-slab">
<p>
This page is dedicated to Dr. Raymond Peat († 2022), who has had a profound impact on my health and
my life in general. Hover over the links below for more detail.
</p>
</div>
<div class="text-slab">
<h2>Links</h2>
<ul>
<li>
<RouterLink to={{ name: 'GECalculator' }}>Thyroid Calculator</RouterLink>
<span style="display: none" class="tooltip">Convert to and from grains, set ratios, etc.</span>
</li>
<li>
<RouterLink to={{ name: 'GEDeutsch' }}>Ray Peat Articles in German</RouterLink>
<span style="display: none" class="tooltip">
A selection of articles by Ray that I have translated in my spare time into German.
</span>
</li>
</ul>
</div>
</>
useHead({ title: "Generative Energy" });
return () => (
<>
<header>
<h1>Generative Energy</h1>
</header>
<div class="text-slab">
<p>
This page is dedicated to Dr. Raymond Peat († 2022), who has had a profound impact on my health
and my life in general. Hover over the links below for more detail.
</p>
</div>
<div class="text-slab">
<h2>Links</h2>
<ul>
<li>
<RouterLink to={{ name: "GECalculator" }}>Thyroid Calculator</RouterLink>
<span style="display: none" class="tooltip">
Convert to and from grains, set ratios, etc.
</span>
</li>
<li>
<RouterLink to={{ name: "GEDeutsch" }}>Ray Peat Articles in German</RouterLink>
<span style="display: none" class="tooltip">
A selection of articles by Ray that I have translated in my spare time into German.
</span>
</li>
</ul>
</div>
</>
);
},
};

+ 0
- 21
app/generative-energy/GEPaypal.tsx Dosyayı Görüntüle

@@ -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>
);
},
});

+ 41
- 33
app/generative-energy/GERoot.tsx Dosyayı Görüntüle

@@ -1,34 +1,34 @@
import { defineComponent, type VNode, Suspense } from "vue";
import { useRoute, RouterLink, RouterView, type RouteRecordRaw } from 'vue-router';
import { defineComponent, Suspense, type VNode } from "vue";
import { type RouteRecordRaw, RouterLink, RouterView, useRoute } from "vue-router";
import GEMain from "@/generative-energy/GEMain.tsx";
import DJEmail from "@/DJEmail.tsx";
import GEPaypal from "@/generative-energy/GEPaypal.tsx";
import GEDeutsch from "@/generative-energy/GEDeutsch.tsx";
import GEDeutschArticle from "@/generative-energy/GEDeutschArticle.tsx";
import GECalculator from "@/generative-energy/GECalculator.tsx";
import DJDonate from "@/DJDonate.tsx";

export const routes: RouteRecordRaw[] = [
{
path: '/',
name: 'GEMain',
path: "/",
name: "GEMain",
component: GEMain,
},
{
path: '/calculator',
name: 'GECalculator',
path: "/calculator",
name: "GECalculator",
component: GECalculator,
},
{
path: '/raypeat-deutsch',
name: 'GEDeutsch',
path: "/raypeat-deutsch",
name: "GEDeutsch",
component: GEDeutsch,
},
{
path: '/raypeat-deutsch/article/:articleName',
name: 'GEDeutschArticle',
path: "/raypeat-deutsch/article/:articleName",
name: "GEDeutschArticle",
component: GEDeutschArticle,
props: ({ params }) => {
if ('articleName' in params) {
if ("articleName" in params) {
return { articleName: params.articleName };
} else {
return false;
@@ -41,27 +41,35 @@ export default defineComponent({
name: "ge-root",
setup() {
const route = useRoute();
return () => <>
<main>
<RouterLink class={"home-btn" + (route.name === 'GEMain' ? ' hide' : '') }
to={{ name: 'GEMain' }}>
Generative Energy Home
</RouterLink>
<RouterView>{{ default: ({ Component }: { Component: VNode }) => (Component &&
<Suspense>{{
default: () => Component,
fallback: () => <div>Page loading...</div>,
}}</Suspense>
)}}</RouterView>
<footer>
<div class="bottom">
<div>
<a href="https://djledda.de/">djledda.de</a> 2023 - <DJEmail>{ () => 'Contact' }</DJEmail>
return () => (
<>
<main>
<RouterLink class={"home-btn" + (route.name === "GEMain" ? " hide" : "")} to={{ name: "GEMain" }}>
Generative Energy Home
</RouterLink>
<RouterView>
{{
default: ({ Component }: { Component: VNode }) => (Component &&
(
<Suspense>
{{
default: () => Component,
fallback: () => <div>Page loading...</div>,
}}
</Suspense>
)),
}}
</RouterView>
<footer>
<div class="bottom">
<div>
<a href="/">djledda.de</a> 2023 - <DJEmail>{() => "Contact"}</DJEmail>
</div>
<DJDonate />
</div>
<GEPaypal />
</div>
</footer>
</main>
</>;
</footer>
</main>
</>
);
},
});

+ 3
- 3
app/generative-energy/client.ts Dosyayı Görüntüle

@@ -4,7 +4,7 @@ import GERoot, { routes } from "@/generative-energy/GERoot.tsx";

createSSRApp(GERoot)
.use(createRouter({
routes,
history: createWebHistory('/generative-energy')
routes,
history: createWebHistory("/generative-energy"),
}))
.mount('#app-root');
.mount("#app-root");

+ 12
- 0
app/generative-energy/server.ts Dosyayı Görüntüle

@@ -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 };
}

+ 67
- 11
app/home/App.tsx Dosyayı Görüntüle

@@ -1,19 +1,75 @@
import { computed, defineComponent, ref } from "vue";
import { defineComponent } from "vue";
import useHead from "@/useHead.ts";
import DJTooltip from "@/DJTooltip.tsx";
import DJEmail from "@/DJEmail.tsx";

export default defineComponent({
name: "app-root",
setup() {
const count = ref(0);
const countDouble = computed(() => count.value * 2);
count.value++;
useHead({ title: "DJ Ledda's Homepage" });
return () => (
<div class="app-main">
<button class="test" onClick={() => count.value++}>Click me!</button>
<div>Count: {count.value}</div>
<div>Count Double: {countDouble.value}</div>
<p>
<a href="/generative-energy/">Go to this other site hosted here but a different Vue app!</a>
</p>
<div class="supercontainer">
<div class="shakeable">
<div class="title_name">
<DJTooltip tooltip="I wonder what he's listening to?">
<img src="/home/img/dj.gif" alt="dj legt krasse Mucke auf" class="dude" />
</DJTooltip>
<DJTooltip tooltip="Easily the coolest guy out there.">
<span>DJ Ledda</span>
</DJTooltip>
<DJTooltip tooltip="I once heard this guy played at revs.">
<img src="/home/img/dj.gif" alt="dj laying down some sick beats" class="dude" />
</DJTooltip>
</div>
<div class="main">
<div class="subject">
<div class="resourcelist">
<a href="https://drum-slayer.com">
<DJTooltip class="resource" tooltip="Small app for designing multitrack looped rhythms with local save and multiple files. Originally built using just vanilla TypeScript and CSS, now with Vue.">
Drum Slayer
</DJTooltip>
</a>
<a href="/somaesque">
<DJTooltip class="resource" tooltip="Puzzle solver app for puzzle cubes resembling the original Soma Cube puzzle. Save and edit your own puzzles! Built with Svelte, THREE.js and AssemblyScript.">
Somaesque
</DJTooltip>
</a>
<a href="/generative-energy">
<DJTooltip class="resource" tooltip="Thyroid calculator, German translations, and more...">
Generative Energy - Ray Peat Resources
</DJTooltip>
</a>
<a href="/home/muenchen-auf-englisch.html">
<DJTooltip class="resource" tooltip="
Authentic historically accurate translations of all of Munich's S-Bahn and U-Bahn
stations, as well as the main municipalities, into English. You live in Allach? It's
Axleigh now. Giesing? Nope! Kyesing! This is a WIP.
">
München auf Englisch - Munich in English
</DJTooltip>
</a>
<a href="/kadi/">
<DJTooltip class="resource" tooltip="Make an account and start saving paper and tracking your Yatzy stats with your
friends! Make your own rulesets, and more. Built with React, express.js, and
MongoDB. Currently inactive.">
K A D I: Online Yatzy Scoresheets
</DJTooltip>
</a>
<a href="http://git.djledda.de/Ledda">
<DJTooltip class="resource" tooltip="Check out what I'm coding!">
My git projects
</DJTooltip>
</a>
<DJEmail>
<DJTooltip class="resource" tooltip="You'll see my address when you click here.">
Click here to get in touch
</DJTooltip>
</DJEmail>
</div>
</div>
</div>
<div id="tooltipCarrier"></div>
</div>
</div>
);
},


+ 4
- 0
app/home/client.ts Dosyayı Görüntüle

@@ -0,0 +1,4 @@
import { createSSRApp } from "vue";
import App from "@/home/App.tsx";

createSSRApp(App).mount("#app-root");

+ 7
- 0
app/home/server.ts Dosyayı Görüntüle

@@ -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 };
}

+ 0
- 13
app/meta.ts Dosyayı Görüntüle

@@ -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: {},
};
}
}

+ 0
- 122
app/tooltip.tsx Dosyayı Görüntüle

@@ -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>
);
},
});

+ 10
- 5
app/useAsyncState.ts Dosyayı Görüntüle

@@ -1,17 +1,22 @@
import { onMounted, onServerPrefetch, useSSRContext, shallowRef, type ShallowRef } from 'vue';
import { onMounted, onServerPrefetch, type ShallowRef, shallowRef } from "vue";
import useDJSSRContext from "@/useDJSSRContext.ts";

declare global {
// deno-lint-ignore no-var
var appstate: Partial<Record<string, unknown>>;
}

export default function useAsyncState<T>(key: string, getter: (context: { hostUrl: string }) => Promise<T | null>, options?: { suspensible: boolean }): { result: ShallowRef<T | null>, stateIsReady: Promise<unknown> } {
const ssrContext = useSSRContext<{ registry: Record<string, unknown> }>();
const isClient = typeof ssrContext === 'undefined';
export default function useAsyncState<T>(
key: string,
getter: (context: { hostUrl: string }) => Promise<T | null>,
options?: { suspensible: boolean },
): { result: ShallowRef<T | null>; stateIsReady: Promise<unknown> } {
const ssrContext = useDJSSRContext();
const isClient = typeof ssrContext === "undefined";

const registry = ssrContext?.registry ?? globalThis?.appstate;

const hostUrl = isClient ? globalThis.location.origin : 'http://localhost:8080';
const hostUrl = isClient ? globalThis.location.origin : "http://localhost:8080";

const state = shallowRef<T | null>(null);



+ 12
- 0
app/useDJSSRContext.ts Dosyayı Görüntüle

@@ -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>();
}

+ 19
- 0
app/useHead.ts Dosyayı Görüntüle

@@ -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;
}
});
}
}

+ 8
- 2
deno.lock Dosyayı Görüntüle

@@ -11,11 +11,13 @@
"jsr:@std/encoding@^1.0.5": "1.0.5",
"jsr:@std/fmt@0.223": "0.223.0",
"jsr:@std/fmt@^1.0.3": "1.0.3",
"jsr:@std/fs@*": "0.223.0",
"jsr:@std/fs@0.223": "0.223.0",
"jsr:@std/http@*": "1.0.9",
"jsr:@std/io@0.223": "0.223.0",
"jsr:@std/media-types@^1.0.3": "1.0.3",
"jsr:@std/net@^1.0.4": "1.0.4",
"jsr:@std/path@*": "1.0.7",
"jsr:@std/path@0.223": "0.223.0",
"jsr:@std/path@^1.0.7": "1.0.7",
"jsr:@std/streams@^1.0.7": "1.0.7",
@@ -34,7 +36,7 @@
"dependencies": [
"jsr:@deno/graph",
"jsr:@std/fmt@0.223",
"jsr:@std/fs",
"jsr:@std/fs@0.223",
"jsr:@std/io",
"jsr:@std/path@0.223"
]
@@ -68,7 +70,11 @@
"integrity": "97765c16aa32245ff4e2204ecf7d8562496a3cb8592340a80e7e554e0bb9149f"
},
"@std/fs@0.223.0": {
"integrity": "3b4b0550b2c524cbaaa5a9170c90e96cbb7354e837ad1bdaf15fc9df1ae9c31c"
"integrity": "3b4b0550b2c524cbaaa5a9170c90e96cbb7354e837ad1bdaf15fc9df1ae9c31c",
"dependencies": [
"jsr:@std/assert",
"jsr:@std/path@0.223"
]
},
"@std/http@1.0.9": {
"integrity": "d409fc319a5e8d4a154e576c758752e9700282d74f31357a12fec6420f9ecb6c",


+ 3
- 1
deps.ts Dosyayı Görüntüle

@@ -1,4 +1,6 @@
import "jsr:@deno/emit";
import "jsr:@std/http";
import "vue";
import 'jsr:@b-fuze/deno-dom';
import "jsr:@b-fuze/deno-dom";
import "jsr:@std/fs";
import "jsr:@std/path";

+ 139
- 55
main.ts Dosyayı Görüntüle

@@ -1,16 +1,85 @@
import { serveFile } from "jsr:@std/http/file-server";
import { createSSRApp } from "vue";
import { type App, toValue } from "vue";
import { type Router } from "vue-router";
import { renderToString } from "vue/server-renderer";
import { createRouter, createMemoryHistory } from 'vue-router';
import Home from "@/home/App.tsx";
import GERoot, { routes as geRoutes } from "@/generative-energy/GERoot.tsx";
import transpileResponse from "./transpile.ts";
import { DOMParser } from 'jsr:@b-fuze/deno-dom'
import { DOMParser } from "jsr:@b-fuze/deno-dom";
import { join } from "jsr:@std/path/join";
import { exists } from "jsr:@std/fs";
import { type DJSSRContext } from "@/useDJSSRContext.ts";
import { type DJAPIResult, type DJAPIResultMap } from "@/api.ts";

const utf8Decoder = new TextDecoder("utf-8");

const parser = new DOMParser();

function appHeaderScript(params: { appstate: Record<string, unknown>; entryPath: string }) {
return `<script type="importmap">
{
"imports": {
"vue": "/deps/vue/dist/vue.esm-browser.prod.js",
"vue-router": "/deps/vue-router/dist/vue-router.esm-browser.js",
"vue/jsx-runtime": "/deps/vue/jsx-runtime/index.mjs",
"@vue/devtools-api": "/deps/@vue/devtools-api/lib/esm/index.js",
"@/": "/app/"
}
}
</script>
<script type="module">
window.appstate = ${JSON.stringify(params.appstate)};
import('${params.entryPath}');
</script>`;
}

async function* siteEntries(path: string): AsyncGenerator<string> {
for await (const dirEnt of Deno.readDir(path)) {
if (dirEnt.isDirectory) {
yield* siteEntries(join(path, dirEnt.name));
} else if (dirEnt.name === "index_template.html") {
yield path.split("/")[1] ?? "";
}
}
}

const publicFiles = siteEntries("public");
const sites: string[] = [];
for await (const path of publicFiles) {
sites.push(path);
}

function getAPIResponse(apiReq: Request): Response {
let jsonResponse: DJAPIResult | { error: string } | null = null;
let status = 200;

const pathname = URL.parse(apiReq.url)?.pathname;

if (!pathname) {
jsonResponse = { error: "Invalid Route" };
status = 400;
}

if (!jsonResponse && pathname) {
const apiPath = pathname.split("/api")[1];

if (apiPath === "/rp-articles") {
jsonResponse = [
{ name: "Koffein: ein vitamin-ähnlicher Nährstoff, oder Adaptogen", slug: "caffeine" },
{
name: "TSH, Temperatur, Puls, und andere Indikatoren bei einer Schilddrüsenunterfunktion",
slug: "hypothyroidism",
},
] satisfies DJAPIResultMap["/rp-articles"];
}
}

const headers = new Headers();
headers.set("Content-Type", "application/json");
return new Response(JSON.stringify(jsonResponse), {
status,
headers,
});
}

Deno.serve({
port: 8080,
hostname: "0.0.0.0",
@@ -18,63 +87,78 @@ Deno.serve({
console.log(`Listening on port http://${hostname}:${port}/`);
},
}, async (req, _conn) => {
let response: Response | null = null;

if (req.method === "GET") {
const pathname = URL.parse(req.url)?.pathname ?? "/";
if (pathname.startsWith('/static/') ||
pathname.startsWith('/generative-energy/static/')) {
if (pathname.startsWith('/static/')) {
return serveFile(req, `public/home/${ pathname }`);
} else {
return serveFile(req, `public${pathname}`);

if (pathname.startsWith("/api/")) {
response = getAPIResponse(req);
}

// Public/static files
if (!response) {
let filepath = join(".", "public", pathname);
if (filepath.endsWith("/")) {
filepath = join(filepath, "index.html");
}
} else if (pathname === "/") {
const rendered = await renderToString(createSSRApp(Home));
const content = utf8Decoder.decode(await Deno.readFile("./public/home/index.html"))
.replace(`<!-- SSR OUTLET -->`, rendered)
.replace(`<!-- SSR HEAD OUTLET -->`, "");
return new Response(content, { headers: { "Content-Type": "text/html" } });
} else if (pathname.startsWith('/generative-energy')) {
const app = createSSRApp(GERoot);
const router = createRouter({
routes: geRoutes,
history: createMemoryHistory('/generative-energy'),
});
app.use(router);
app.provide('dom-parse', (innerHTML: string) => {
return parser.parseFromString(innerHTML, 'text/html').documentElement;
});
const ssrContext = { registry: {}};
await router.replace(pathname.split('/generative-energy')[1]);
await router.isReady();
const rendered = await renderToString(app, ssrContext);
const content = utf8Decoder.decode(await Deno.readFile("./public/generative-energy/index.html"))
.replace('%TITLE%', 'Generative Energy')
.replace('<!-- SSR HEAD OUTLET -->', `
<script type="importmap">
{
"imports": {
"vue": "/deps/vue/dist/vue.esm-browser.prod.js",
"vue-router": "/deps/vue-router/dist/vue-router.esm-browser.js",
"vue/jsx-runtime": "/deps/vue/jsx-runtime/index.mjs",
"@vue/devtools-api": "/deps/@vue/devtools-api/lib/esm/index.js",
"@/": "/app/"
if (await exists(filepath, { isFile: true })) {
response = await serveFile(req, filepath);
}
}

// NPM Vendor deps
if (response === null && pathname.startsWith("/deps")) {
response = await serveFile(req, `node_modules/${pathname.split("/deps")[1]}`);
}

// Transpile Code
if (
response === null &&
pathname.startsWith("/app") &&
(pathname.endsWith(".ts") || pathname.endsWith(".tsx")) &&
!pathname.endsWith("server.ts")
) {
response = await serveFile(req, "./" + pathname);
response = await transpileResponse(response, req.url, pathname);
}

// SSR
if (response === null) {
const baseDirectoryName = pathname.split("/")[1] ?? "";
if (sites.includes(baseDirectoryName)) {
const appLocation = baseDirectoryName === "" ? "home" : baseDirectoryName;
const siteTemplate = join("public", baseDirectoryName, "index_template.html");
const siteEntry = join("app", appLocation, "server.ts");
const clientEntry = join("@", appLocation, "client.ts");
const { app, router } = (await import("./" + siteEntry)).default() as {
app: App;
router: Router | null;
};
app.provide("dom-parse", (innerHTML: string) => {
return parser.parseFromString(innerHTML, "text/html").documentElement;
});
const ssrContext: DJSSRContext = { registry: {}, head: { title: "" } };
if (router) {
await router.replace(pathname.split("/generative-energy")[1]);
await router.isReady();
}
const rendered = await renderToString(app, ssrContext);
const content = utf8Decoder.decode(await Deno.readFile(siteTemplate))
.replace(`<!-- SSR OUTLET -->`, rendered)
.replaceAll("%TITLE%", toValue(ssrContext.head?.title) ?? "Site")
.replace(
`<!-- SSR HEAD OUTLET -->`,
appHeaderScript({ appstate: ssrContext.registry, entryPath: clientEntry }),
);
response = new Response(content, { headers: { "Content-Type": "text/html" } });
}
</script>
<script> window.appstate = ${ JSON.stringify(ssrContext.registry) }; </script>
`)
.replace(`<!-- SSR OUTLET -->`, rendered);
return new Response(content, { headers: { "Content-Type": "text/html" } });
} else if (pathname.startsWith("/app") && (pathname.endsWith(".ts") || pathname.endsWith(".tsx"))) {
const response = await serveFile(req, "./" + pathname);
return await transpileResponse(response, req.url, pathname);
} else if (pathname.startsWith("/deps")) {
return serveFile(req, `node_modules/${pathname.split("/deps")[1]}`);
}
return new Response("Not found.", { status: 404 });
}
} else {
return new Response("Only GET allowed.", { status: 500 });
response = new Response("Only GET allowed.", { status: 500 });
}

return response ?? new Response("Not found.", { status: 404 });
});

Deno.addSignalListener("SIGINT", () => {


public/generative-energy/static/content/caffeine.html → public/generative-energy/content/caffeine.html Dosyayı Görüntüle

@@ -1,7 +1,5 @@
<h1>
<span lang="en">Caffeine: A vitamin-like nutrient, or adaptogen</span>
<span lang="de">Koffein: ein vitamin-ähnlicher Nährstoff, oder Adaptogen</span>
</h1>
<h1 lang="en">Caffeine: A vitamin-like nutrient, or adaptogen</h1>
<h1 lang="de">Koffein: ein vitamin-ähnlicher Nährstoff, oder Adaptogen</h1>

<p>
<span lang="en"><strong>Questions about tea and coffee, cancer and other degenerative diseases, and the hormones.</strong></span>

public/generative-energy/static/content/hypothyroidism.html → public/generative-energy/content/hypothyroidism.html Dosyayı Görüntüle


public/generative-energy/static/content/hypothyroidism.md → public/generative-energy/content/hypothyroidism.md Dosyayı Görüntüle


public/generative-energy/index.html → public/generative-energy/index_template.html Dosyayı Görüntüle

@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>%TITLE%</title>

<link rel="stylesheet" href="/generative-energy/static/styles.css">
<link rel="stylesheet" href="/generative-energy/styles.css">
<link
href="https://fonts.googleapis.com/css2?family=Roboto&amp;family=Roboto+Slab:wght@600&amp;display=swap"
rel="stylesheet">

public/generative-energy/static/styles.css → public/generative-energy/styles.css Dosyayı Görüntüle

@@ -139,17 +139,26 @@ hr {
background: #999;
}

footer .bottom {
display: flex;
flex-direction: row;
justify-content: space-between;
}

footer {
margin-bottom: 20px;
}

.bottom {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;

> * {
flex: 1;
}
.dj-donate {
text-align: right;
}
img {
display: inline-block;
}
}
}

.ge-calculator {
table {
@@ -186,13 +195,3 @@ footer {
}
}
}

.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}

.fade-enter-from,
.fade-leave-to {
opacity: 0;
}

public/home/static/app.js → public/home/app.js Dosyayı Görüntüle


public/home/static/icon.webp → public/home/icon.webp Dosyayı Görüntüle


public/home/static/image.jpeg → public/home/image.jpeg Dosyayı Görüntüle


+ 0
- 0
public/home/img/.gitignore Dosyayı Görüntüle


BIN
public/home/img/daniel.jpg Dosyayı Görüntüle

Önce Sonra
Genişlik: 184  |  Yükseklik: 184  |  Boyut: 6.3 KiB

BIN
public/home/img/dj.gif Dosyayı Görüntüle

Önce Sonra
Genişlik: 320  |  Yükseklik: 267  |  Boyut: 42 KiB

BIN
public/home/img/icon.png Dosyayı Görüntüle

Önce Sonra
Genişlik: 192  |  Yükseklik: 192  |  Boyut: 3.9 KiB

BIN
public/home/img/preis.jpg Dosyayı Görüntüle

Önce Sonra
Genişlik: 3480  |  Yükseklik: 4640  |  Boyut: 384 KiB

BIN
public/home/img/tile-wide.png Dosyayı Görüntüle

Önce Sonra
Genişlik: 558  |  Yükseklik: 270  |  Boyut: 1.8 KiB

BIN
public/home/img/tile.png Dosyayı Görüntüle

Önce Sonra
Genişlik: 558  |  Yükseklik: 558  |  Boyut: 3.4 KiB

BIN
public/home/img/tradies.png Dosyayı Görüntüle

Önce Sonra
Genişlik: 298  |  Yükseklik: 165  |  Boyut: 92 KiB

BIN
public/home/img/worksafe.png Dosyayı Görüntüle

Önce Sonra
Genişlik: 400  |  Yükseklik: 400  |  Boyut: 17 KiB

+ 0
- 18
public/home/index.html Dosyayı Görüntüle

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

+ 130
- 0
public/home/js/SexyTooltip.ts Dosyayı Görüntüle

@@ -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;

+ 47
- 0
public/home/js/main.ts Dosyayı Görüntüle

@@ -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;
});

+ 189
- 0
public/home/main.css Dosyayı Görüntüle

@@ -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;
}

+ 32
- 0
public/home/muenchen-auf-englisch.html
Dosya farkı çok büyük olduğundan ihmal edildi
Dosyayı Görüntüle


+ 740
- 0
public/home/ortsteile_muc_en.html Dosyayı Görüntüle

@@ -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&amp;action=edit&amp;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>

+ 0
- 22
public/home/static/styles.css Dosyayı Görüntüle

@@ -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;
}

+ 88
- 0
public/index2.html Dosyayı Görüntüle

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

+ 18
- 0
public/index_template.html Dosyayı Görüntüle

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

+ 5
- 0
public/robots.txt Dosyayı Görüntüle

@@ -0,0 +1,5 @@
# www.robotstxt.org/

# Allow crawling of all content
User-agent: *
Disallow:

Yükleniyor…
İptal
Kaydet