accomadesc 0.0.14 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/AccoCard.svelte.d.ts +2 -1
  2. package/dist/AccoDescription.svelte.d.ts +2 -1
  3. package/dist/AmenitiesCore.svelte +2 -2
  4. package/dist/AmenitiesCore.svelte.d.ts +2 -1
  5. package/dist/BookingRequest.svelte +272 -0
  6. package/dist/BookingRequest.svelte.d.ts +5 -0
  7. package/dist/Calendar.svelte +25 -11
  8. package/dist/Calendar.svelte.d.ts +2 -1
  9. package/dist/CalendarAvailable.svelte +11 -18
  10. package/dist/CalendarAvailable.svelte.d.ts +2 -1
  11. package/dist/CalendarGrid.svelte +22 -0
  12. package/dist/CalendarGrid.svelte.d.ts +5 -0
  13. package/dist/CalendarRows.svelte +22 -0
  14. package/dist/CalendarRows.svelte.d.ts +5 -0
  15. package/dist/ContactForm.svelte +180 -0
  16. package/dist/ContactForm.svelte.d.ts +5 -0
  17. package/dist/Photo.svelte.d.ts +2 -1
  18. package/dist/PhotoGallery.svelte +89 -35
  19. package/dist/PhotoGallery.svelte.d.ts +2 -1
  20. package/dist/Pricing.svelte.d.ts +2 -1
  21. package/dist/PricingShort.svelte.d.ts +2 -1
  22. package/dist/Section.svelte.d.ts +2 -1
  23. package/dist/Text.svelte.d.ts +2 -1
  24. package/dist/Weather.svelte +1 -2
  25. package/dist/Weather.svelte.d.ts +2 -1
  26. package/dist/basic/Avatar.svelte.d.ts +3 -2
  27. package/dist/basic/Button.svelte +6 -3
  28. package/dist/basic/Button.svelte.d.ts +4 -3
  29. package/dist/basic/Icon.svelte.d.ts +3 -2
  30. package/dist/basic/Notes.svelte +83 -0
  31. package/dist/basic/Notes.svelte.d.ts +7 -0
  32. package/dist/basic/Spinner.svelte.d.ts +3 -2
  33. package/dist/basic/TextInput.svelte.d.ts +3 -2
  34. package/dist/helpers/debounce.d.ts +7 -0
  35. package/dist/helpers/debounce.js +49 -0
  36. package/dist/helpers/iCSEventExample.ics +14 -0
  37. package/dist/helpers/normalizeDate.d.ts +2 -0
  38. package/dist/helpers/normalizeDate.js +53 -0
  39. package/dist/helpers/readICS.d.ts +7 -0
  40. package/dist/helpers/readICS.js +94 -0
  41. package/dist/names/gen.js +3 -3
  42. package/dist/occuplan/OccuPlanAvailableInfo.svelte +38 -0
  43. package/dist/occuplan/OccuPlanAvailableInfo.svelte.d.ts +12 -0
  44. package/dist/occuplan/OccuPlanGrid.svelte +356 -0
  45. package/dist/occuplan/OccuPlanGrid.svelte.d.ts +13 -0
  46. package/dist/occuplan/OccuPlanPicker.svelte +559 -0
  47. package/dist/occuplan/OccuPlanPicker.svelte.d.ts +16 -0
  48. package/dist/occuplan/OccuPlanRows.svelte +360 -0
  49. package/dist/occuplan/OccuPlanRows.svelte.d.ts +13 -0
  50. package/dist/occuplan/OccuPlanWrapper.svelte +113 -0
  51. package/dist/occuplan/OccuPlanWrapper.svelte.d.ts +5 -0
  52. package/dist/occuplan/state.svelte.d.ts +90 -0
  53. package/dist/occuplan/state.svelte.js +383 -0
  54. package/dist/svg/ExtLinkSVG.svelte.d.ts +3 -2
  55. package/dist/svg/LinkSVG.svelte.d.ts +3 -2
  56. package/dist/types.d.ts +75 -4
  57. package/dist/types.js +20 -0
  58. package/package.json +64 -61
@@ -1,7 +1,9 @@
1
1
  <script lang="ts">
2
+ import Button from './basic/Button.svelte';
2
3
  import PhotoComponent from './Photo.svelte';
3
- import type { I18nFacade, Photo, PhotoGalleryContent, GridPhoto } from './types.js';
4
+ import type { I18nFacade, Photo, PhotoGalleryContent } from './types.js';
4
5
  import { browser } from '$app/environment';
6
+ import { slide } from 'svelte/transition';
5
7
 
6
8
  let { photos, gridPhotoWidth = 300, translateFunc }: PhotoGalleryContent & I18nFacade = $props();
7
9
 
@@ -13,42 +15,48 @@
13
15
  };
14
16
  }
15
17
  let ratio = $derived(landscape ? '16/9' : '9/16');
16
-
17
18
  let galleryContainer: HTMLDivElement | undefined = $state();
18
19
 
19
- let gridPhotos: GridPhoto[] = $state(
20
- photos.map((p: Photo): GridPhoto => {
21
- return {
22
- photo: p,
23
- zoomed: false,
24
- id: crypto.randomUUID(),
25
- };
26
- }),
27
- );
28
-
29
20
  let width = $state(1000);
30
21
  let numberOfCols = $derived(
31
22
  Math.floor(width / (gridPhotoWidth && Number.isInteger(gridPhotoWidth) ? gridPhotoWidth : 300)),
32
23
  );
33
24
 
34
- const zoom = (z: GridPhoto, i: number) => {
35
- let unzoom = false;
36
- if (z.zoomed) {
37
- unzoom = true;
38
- }
25
+ let zoomed: number | null = $state(0);
26
+ let zoomedPhoto: Photo | null = $derived(zoomed != null ? photos[zoomed] : null);
39
27
 
40
- gridPhotos.forEach((p: GridPhoto) => (p.zoomed = false));
41
- gridPhotos.splice(i, 1);
42
-
43
- if (!unzoom) {
44
- z.zoomed = true;
45
- }
46
- gridPhotos = [z, ...gridPhotos];
28
+ const zoom = (i: number) => {
29
+ zoomed = i;
30
+ setTimeout(() => {
31
+ galleryContainer?.scrollIntoView({ behavior: 'smooth' });
32
+ }, 100);
33
+ };
47
34
 
35
+ const unzoom = () => {
36
+ zoomed = null;
48
37
  setTimeout(() => {
49
- galleryContainer?.scrollIntoView();
38
+ galleryContainer?.scrollIntoView({ behavior: 'smooth' });
50
39
  }, 100);
51
40
  };
41
+
42
+ const zoomNext = () => {
43
+ if (zoomed != null) {
44
+ if (zoomed == photos.length - 1) {
45
+ zoomed = 0;
46
+ } else {
47
+ zoomed = zoomed + 1;
48
+ }
49
+ }
50
+ };
51
+ const zoomPrev = () => {
52
+ if (zoomed != null) {
53
+ if (zoomed == 0) {
54
+ zoomed = photos.length - 1;
55
+ } else {
56
+ zoomed = zoomed - 1;
57
+ }
58
+ }
59
+ };
52
60
  </script>
53
61
 
54
62
  <div
@@ -59,27 +67,72 @@
59
67
  class="grid-container"
60
68
  bind:clientWidth={width}
61
69
  >
62
- {#each gridPhotos as p, i (p.id)}
70
+ {#if zoomed != null && zoomedPhoto != null}
63
71
  <div
64
- class:complete-row={p.zoomed}
72
+ transition:slide
65
73
  aria-label="resize"
66
74
  role="button"
67
75
  tabindex="-1"
68
- onclick={() => zoom(p, i)}
69
- onkeyup={() => zoom(p, i)}
76
+ onclick={() => unzoom()}
77
+ onkeyup={() => unzoom()}
78
+ class="photo-container complete-row"
79
+ >
80
+ <PhotoComponent {...zoomedPhoto.content} frame={true} {ratio} {translateFunc} />
81
+
82
+ <div class="next-wrapper" style="">
83
+ <Button
84
+ text=">"
85
+ size={3.3}
86
+ fontSize="3rem"
87
+ clicked={() => zoomNext()}
88
+ stopPropagation={true}
89
+ />
90
+ </div>
91
+ <div class="prev-wrapper">
92
+ <Button
93
+ text="<"
94
+ size={3.3}
95
+ fontSize="3rem"
96
+ clicked={() => zoomPrev()}
97
+ stopPropagation={true}
98
+ />
99
+ </div>
100
+ </div>
101
+ {/if}
102
+
103
+ {#each photos as p, i (p.id)}
104
+ <div
105
+ aria-label="resize"
106
+ role="button"
107
+ tabindex="-1"
108
+ onclick={() => zoom(i)}
109
+ onkeyup={() => zoom(i)}
70
110
  class="photo-container"
71
111
  >
72
- <PhotoComponent
73
- {...p.photo.content}
74
- frame={true}
75
- ratio={p.zoomed ? ratio : '1'}
76
- {translateFunc}
77
- />
112
+ <PhotoComponent {...p.content} frame={true} ratio="1" {translateFunc} />
78
113
  </div>
79
114
  {/each}
80
115
  </div>
81
116
 
82
117
  <style>
118
+ .next-wrapper {
119
+ position: absolute;
120
+ right: 1rem;
121
+ top: calc(50% - 1.65rem);
122
+
123
+ --bg-button-prim-color: rgba(242, 242, 242, 0.3);
124
+ --main-font-color: rgba(15, 14, 15, 0.6);
125
+ }
126
+
127
+ .prev-wrapper {
128
+ position: absolute;
129
+ left: 1rem;
130
+ top: calc(50% - 1.65rem);
131
+
132
+ --bg-button-prim-color: rgba(242, 242, 242, 0.3);
133
+ --main-font-color: rgba(15, 14, 15, 0.6);
134
+ }
135
+
83
136
  .complete-row {
84
137
  grid-column-start: firstLine;
85
138
  grid-column-end: lastLine;
@@ -91,6 +144,7 @@
91
144
  cursor: pointer;
92
145
  width: 100%;
93
146
  height: 100%;
147
+ position: relative;
94
148
  }
95
149
 
96
150
  .grid-container {
@@ -1,4 +1,5 @@
1
1
  import type { I18nFacade, PhotoGalleryContent } from './types.js';
2
- declare const PhotoGallery: import("svelte").Component<PhotoGalleryContent & I18nFacade, {}, "">;
2
+ type $$ComponentProps = PhotoGalleryContent & I18nFacade;
3
+ declare const PhotoGallery: import("svelte").Component<$$ComponentProps, {}, "">;
3
4
  type PhotoGallery = ReturnType<typeof PhotoGallery>;
4
5
  export default PhotoGallery;
@@ -1,4 +1,5 @@
1
1
  import type { PricingContent, I18nFacade } from './types.js';
2
- declare const Pricing: import("svelte").Component<PricingContent & I18nFacade, {}, "">;
2
+ type $$ComponentProps = PricingContent & I18nFacade;
3
+ declare const Pricing: import("svelte").Component<$$ComponentProps, {}, "">;
3
4
  type Pricing = ReturnType<typeof Pricing>;
4
5
  export default Pricing;
@@ -1,4 +1,5 @@
1
1
  import type { I18nFacade, PricingShortContent } from './types.js';
2
- declare const PricingShort: import("svelte").Component<PricingShortContent & I18nFacade, {}, "">;
2
+ type $$ComponentProps = PricingShortContent & I18nFacade;
3
+ declare const PricingShort: import("svelte").Component<$$ComponentProps, {}, "">;
3
4
  type PricingShort = ReturnType<typeof PricingShort>;
4
5
  export default PricingShort;
@@ -1,4 +1,5 @@
1
1
  import { type Section as SectionI, type I18nFacade } from './types.js';
2
- declare const Section: import("svelte").Component<SectionI & I18nFacade, {}, "padding">;
2
+ type $$ComponentProps = SectionI & I18nFacade;
3
+ declare const Section: import("svelte").Component<$$ComponentProps, {}, "padding">;
3
4
  type Section = ReturnType<typeof Section>;
4
5
  export default Section;
@@ -1,4 +1,5 @@
1
1
  import type { TextContent, I18nFacade } from './types.js';
2
- declare const Text: import("svelte").Component<TextContent & I18nFacade, {}, "">;
2
+ type $$ComponentProps = TextContent & I18nFacade;
3
+ declare const Text: import("svelte").Component<$$ComponentProps, {}, "">;
3
4
  type Text = ReturnType<typeof Text>;
4
5
  export default Text;
@@ -8,8 +8,7 @@
8
8
 
9
9
  const callback = () => {
10
10
  //TODO maybe do something here
11
- // probably build something custom, not using weatherwidget.io, yes maptiler might be good option
12
- //
11
+ // probably build something custom, not using weatherwidget.io
13
12
  //console.log("weather script loaded")
14
13
  initialLoadDone = true;
15
14
  };
@@ -1,4 +1,5 @@
1
1
  import type { I18nFacade, WeatherContent } from './types.js';
2
- declare const Weather: import("svelte").Component<WeatherContent & I18nFacade, {}, "">;
2
+ type $$ComponentProps = WeatherContent & I18nFacade;
3
+ declare const Weather: import("svelte").Component<$$ComponentProps, {}, "">;
3
4
  type Weather = ReturnType<typeof Weather>;
4
5
  export default Weather;
@@ -1,7 +1,8 @@
1
- declare const Avatar: import("svelte").Component<{
1
+ type $$ComponentProps = {
2
2
  email: string;
3
3
  size?: number;
4
4
  imageUrl?: string;
5
- }, {}, "imageUrl">;
5
+ };
6
+ declare const Avatar: import("svelte").Component<$$ComponentProps, {}, "imageUrl">;
6
7
  type Avatar = ReturnType<typeof Avatar>;
7
8
  export default Avatar;
@@ -41,7 +41,7 @@
41
41
  clicked?: (event: Event) => void;
42
42
  } = $props();
43
43
 
44
- const disabled = $derived(!enabled);
44
+ let disabled = $derived(!enabled);
45
45
 
46
46
  const onClick = (event: Event) => {
47
47
  if (stopPropagation) {
@@ -50,7 +50,7 @@
50
50
  if (enabled) onclicked(event);
51
51
  };
52
52
 
53
- let focussed = false;
53
+ let focussed = $state(false);
54
54
  const focussing = (event: Event) => {
55
55
  if (stopPropagation) {
56
56
  event.stopPropagation();
@@ -277,7 +277,7 @@
277
277
  align-items: center;
278
278
 
279
279
  background-color: var(--bg-button-prim-color);
280
- color: var(--font-prim-color);
280
+ color: var(--main-font-color);
281
281
 
282
282
  cursor: pointer;
283
283
  overflow: hidden;
@@ -288,6 +288,7 @@
288
288
  }
289
289
  .button:focus {
290
290
  outline-style: none;
291
+ filter: drop-shadow(0 0 0.75rem var(--focussed-border-color));
291
292
  }
292
293
 
293
294
  .button.danger {
@@ -310,6 +311,8 @@
310
311
  vertical-align: middle;
311
312
  font-variant: small-caps;
312
313
 
314
+ color: var(--main-font-color);
315
+
313
316
  margin-left: 0.4rem;
314
317
  margin-right: 0.4rem;
315
318
  }
@@ -1,9 +1,9 @@
1
- declare const Button: import("svelte").Component<{
1
+ type $$ComponentProps = {
2
2
  text?: string | null;
3
3
  iconName?: string;
4
4
  ariaLabel?: string;
5
5
  form?: string | null;
6
- type?: "button" | "submit" | "reset";
6
+ type?: 'button' | 'submit' | 'reset';
7
7
  stopPropagation?: boolean;
8
8
  pressed?: boolean;
9
9
  fontSize?: string;
@@ -15,6 +15,7 @@ declare const Button: import("svelte").Component<{
15
15
  onUp?: boolean;
16
16
  preventDefault?: boolean;
17
17
  clicked?: (event: Event) => void;
18
- }, {}, "enabled">;
18
+ };
19
+ declare const Button: import("svelte").Component<$$ComponentProps, {}, "enabled">;
19
20
  type Button = ReturnType<typeof Button>;
20
21
  export default Button;
@@ -1,8 +1,9 @@
1
- declare const Icon: import("svelte").Component<{
1
+ type $$ComponentProps = {
2
2
  color?: string;
3
3
  iconName: string;
4
4
  width?: string;
5
5
  height?: string;
6
- }, {}, "">;
6
+ };
7
+ declare const Icon: import("svelte").Component<$$ComponentProps, {}, "">;
7
8
  type Icon = ReturnType<typeof Icon>;
8
9
  export default Icon;
@@ -0,0 +1,83 @@
1
+ <script lang="ts">
2
+ import { randomID } from '../names/gen.ts';
3
+
4
+ let {
5
+ disabled,
6
+ changed,
7
+ }: {
8
+ disabled: boolean;
9
+ changed: (value: string) => void;
10
+ } = $props();
11
+
12
+ let divElement: HTMLDivElement;
13
+ let id = randomID();
14
+
15
+ const contentChanged = (e: Event) => {
16
+ const target = e.currentTarget as HTMLDivElement;
17
+ changed(target.getHTML());
18
+ };
19
+
20
+ $effect(() => {
21
+ if (disabled) {
22
+ divElement.innerText = '';
23
+ }
24
+ });
25
+ </script>
26
+
27
+ <div
28
+ bind:this={divElement}
29
+ class:disabled
30
+ id="input-{id}"
31
+ contenteditable={disabled ? null : 'plaintext-only'}
32
+ class="question-input"
33
+ oninput={contentChanged}
34
+ ></div>
35
+
36
+ <style>
37
+ .question-input {
38
+ position: relative;
39
+
40
+ min-width: 400px;
41
+ width: 100%;
42
+ max-width: 800px;
43
+ min-height: 5rem;
44
+ height: 100%;
45
+
46
+ border: var(--main-border);
47
+ border-radius: 1rem;
48
+
49
+ background-color: var(--longinput-bg-color);
50
+
51
+ padding-left: 1.3rem;
52
+ padding-right: 1.3rem;
53
+ padding-top: 0.5rem;
54
+
55
+ line-height: 24px;
56
+
57
+ background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' height='24' width='24'><line x1='0' y1='4' x2='16' y2='4' stroke='rgba(15,15,15,0.5)' stroke-width='0.1' /></svg>");
58
+ background-repeat: repeat;
59
+ }
60
+
61
+ .question-input.disabled {
62
+ border-color: var(--disabled-border-color);
63
+ font: var(--font-disabled-color);
64
+ background-color: var(--longinput-disabled-bg-color);
65
+ }
66
+
67
+ .question-input::after {
68
+ content: '';
69
+ position: absolute;
70
+ border: var(--dashed-border);
71
+ right: 1rem;
72
+ top: 0;
73
+ bottom: 0;
74
+ }
75
+ .question-input::before {
76
+ content: '';
77
+ position: absolute;
78
+ border: var(--dashed-border);
79
+ left: 1rem;
80
+ top: 0;
81
+ bottom: 0;
82
+ }
83
+ </style>
@@ -0,0 +1,7 @@
1
+ type $$ComponentProps = {
2
+ disabled: boolean;
3
+ changed: (value: string) => void;
4
+ };
5
+ declare const Notes: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type Notes = ReturnType<typeof Notes>;
7
+ export default Notes;
@@ -1,8 +1,9 @@
1
- declare const Spinner: import("svelte").Component<{
1
+ type $$ComponentProps = {
2
2
  color?: string;
3
3
  inline?: boolean;
4
4
  height?: string;
5
5
  width?: string;
6
- }, {}, "">;
6
+ };
7
+ declare const Spinner: import("svelte").Component<$$ComponentProps, {}, "">;
7
8
  type Spinner = ReturnType<typeof Spinner>;
8
9
  export default Spinner;
@@ -1,4 +1,4 @@
1
- declare const TextInput: import("svelte").Component<{
1
+ type $$ComponentProps = {
2
2
  placeholder?: string;
3
3
  phonePattern?: string;
4
4
  name?: string;
@@ -28,6 +28,7 @@ declare const TextInput: import("svelte").Component<{
28
28
  focussed?: (name: string, value: string | number) => void;
29
29
  validityChanged?: (valid: boolean, name: string, value: string | number) => void;
30
30
  valueChanged?: (valid: boolean, name: string, value: string | number) => void;
31
- }, {}, "valid" | "value">;
31
+ };
32
+ declare const TextInput: import("svelte").Component<$$ComponentProps, {}, "valid" | "value">;
32
33
  type TextInput = ReturnType<typeof TextInput>;
33
34
  export default TextInput;
@@ -0,0 +1,7 @@
1
+ export type DebounceFunction = () => Promise<boolean>;
2
+ export interface DebounceConfig {
3
+ initialDelay: number;
4
+ debounceDelay: number;
5
+ }
6
+ export declare const DefaultDebounceConfig: DebounceConfig;
7
+ export declare const debounce: (id: string, func: DebounceFunction, config?: DebounceConfig) => Promise<boolean>;
@@ -0,0 +1,49 @@
1
+ import { DateTime as luxon } from 'luxon';
2
+ export const DefaultDebounceConfig = {
3
+ initialDelay: 0,
4
+ debounceDelay: 10000,
5
+ };
6
+ let updateTimestamps = {};
7
+ let nextUpdates = {};
8
+ let scheduled = {};
9
+ export const debounce = async (id, func, config = DefaultDebounceConfig) => {
10
+ //console.log(`DEBOUNCE: ${id}`)
11
+ nextUpdates[id] = func;
12
+ return doDebounce(id, true, config);
13
+ };
14
+ const doDebounce = async (id, firstRun, config) => {
15
+ const now = luxon.utc();
16
+ const priorExecTime = updateTimestamps[id];
17
+ //no prior timestamp, execute and store timestamp
18
+ if (!priorExecTime) {
19
+ if (firstRun && config.initialDelay > 0) {
20
+ setTimeout(() => {
21
+ return doDebounce(id, false, config);
22
+ }, config.initialDelay + 10);
23
+ }
24
+ else {
25
+ const result = await nextUpdates[id]();
26
+ updateTimestamps[id] = now;
27
+ //console.log('INITIAL')
28
+ return result;
29
+ }
30
+ }
31
+ //last execution was more than debounceDelay ms ago
32
+ else if (priorExecTime < now.minus({ milliseconds: config.debounceDelay })) {
33
+ const result = await nextUpdates[id]();
34
+ updateTimestamps[id] = now;
35
+ //console.log('DUE')
36
+ return result;
37
+ }
38
+ //else retry after timeout
39
+ else if (firstRun && !scheduled[id]) {
40
+ //console.log('SCHEDULE')
41
+ scheduled[id] = true;
42
+ setTimeout(() => {
43
+ //console.log('RETRY')
44
+ scheduled[id] = false;
45
+ return doDebounce(id, false, config);
46
+ }, config.debounceDelay + 10);
47
+ }
48
+ return false;
49
+ };
@@ -0,0 +1,14 @@
1
+ BEGIN:VEVENT
2
+ DTSTART;VALUE=DATE:20230207
3
+ DTEND;VALUE=DATE:20230223
4
+ DTSTAMP:20230118T163615Z
5
+ UID:6cfjhjfvp50tldvkbrbgp9m6r0@google.com
6
+ CREATED:20230118T163028Z
7
+ DESCRIPTION:
8
+ LAST-MODIFIED:20230118T163028Z
9
+ LOCATION:
10
+ SEQUENCE:0
11
+ STATUS:CONFIRMED
12
+ SUMMARY:Mertens
13
+ TRANSP:TRANSPARENT
14
+ END:VEVENT
@@ -0,0 +1,2 @@
1
+ import type { DateTime } from 'luxon';
2
+ export declare const normalizeDate: (date?: any) => DateTime;
@@ -0,0 +1,53 @@
1
+ import { DateTime as luxon } from 'luxon';
2
+ export const normalizeDate = (date) => {
3
+ //initialize default return values, for the case the input is not
4
+ //"normalizable"
5
+ const nowUTC = luxon.utc().set({ hour: 12, minute: 0, second: 0, millisecond: 0 });
6
+ //no parameter or falsy
7
+ if (!date) {
8
+ return nowUTC;
9
+ }
10
+ //parameter is an instance of date;
11
+ if (date instanceof Date) {
12
+ // but invalid
13
+ if (Number.isNaN(Number(date))) {
14
+ return nowUTC;
15
+ }
16
+ return luxon.utc(date.getFullYear(), date.getMonth() + 1, date.getDate(), 12);
17
+ }
18
+ //otherwise try different parsing methods
19
+ let pDate;
20
+ //check if it's already a numeric value, assuming millis from start of time
21
+ if (Number.isInteger(date)) {
22
+ pDate = luxon.fromMillis(date);
23
+ if (pDate.isValid) {
24
+ return luxon.utc(pDate.year, pDate.month, pDate.day, 12);
25
+ }
26
+ }
27
+ //check if it has a valueOf function and if that function yields an integer
28
+ if (typeof date.valueOf === 'function' && Number.isInteger(date.valueOf())) {
29
+ pDate = luxon.fromMillis(date.valueOf());
30
+ if (pDate.isValid) {
31
+ return luxon.utc(pDate.year, pDate.month, pDate.day, 12);
32
+ }
33
+ }
34
+ //parse it, as if it is an ISO string
35
+ pDate = luxon.fromISO(date, { setZone: true });
36
+ if (pDate.isValid) {
37
+ return luxon.utc(pDate.year, pDate.month, pDate.day, 12);
38
+ }
39
+ //parse it, as if it is in HTTP format
40
+ //@ts-ignore
41
+ pDate = luxon.fromHTTP(date, { setZone: true });
42
+ if (pDate.isValid) {
43
+ return luxon.utc(pDate.year, pDate.month, pDate.day, 12);
44
+ }
45
+ //parse it, as if it is in SQL format
46
+ //@ts-ignore
47
+ pDate = luxon.fromSQL(date, { setZone: true });
48
+ if (pDate.isValid) {
49
+ return luxon.utc(pDate.year, pDate.month, pDate.day, 12);
50
+ }
51
+ //if nothing helps, return today at noon GMT
52
+ return nowUTC;
53
+ };
@@ -0,0 +1,7 @@
1
+ import type { OccupationCallback } from '../occuplan/state.svelte.js';
2
+ export interface GetEventsResult {
3
+ message: string;
4
+ code: number;
5
+ error: boolean;
6
+ }
7
+ export declare const getEvents: (url: string, eventCallback: OccupationCallback) => Promise<GetEventsResult>;
@@ -0,0 +1,94 @@
1
+ import { DateTime as lx } from 'luxon';
2
+ const extractOccupationType = (line) => {
3
+ try {
4
+ const oTypes = line.split('CATEGORIES:')[1];
5
+ const [first] = oTypes.split(',');
6
+ return first.trim();
7
+ }
8
+ catch (e) {
9
+ console.log('Error occured extracting occupation type from line', line);
10
+ }
11
+ return 'one';
12
+ };
13
+ const readIcsFromResponse = async (resp, eventCallback) => {
14
+ const text = await resp.text();
15
+ //console.log(text)
16
+ let insideEvent = false;
17
+ let arrival = '';
18
+ let leave = '';
19
+ let oType = 'one';
20
+ text.split(/\r?\n/).forEach((line) => {
21
+ switch (true) {
22
+ case /^BEGIN:VEVENT$/.test(line):
23
+ insideEvent = true;
24
+ break;
25
+ case /^DTSTART;VALUE=.*/.test(line) && insideEvent:
26
+ arrival = line;
27
+ break;
28
+ case /^DTEND;VALUE=.*/.test(line) && insideEvent:
29
+ leave = line;
30
+ break;
31
+ case /^CATEGORIES:.*$/.test(line) && insideEvent:
32
+ oType = extractOccupationType(line);
33
+ break;
34
+ case /^END:VEVENT$/.test(line) && insideEvent:
35
+ insideEvent = false;
36
+ eventCallback({
37
+ type: oType,
38
+ arrival: getDate(arrival),
39
+ leave: getDate(leave),
40
+ });
41
+ break;
42
+ }
43
+ });
44
+ };
45
+ export const getEvents = async (url, eventCallback) => {
46
+ let result = {
47
+ message: 'undefined',
48
+ code: 100,
49
+ error: false,
50
+ };
51
+ let resp;
52
+ try {
53
+ resp = await fetch(url, {
54
+ headers: {
55
+ Accept: 'text/calendar',
56
+ },
57
+ //mode: "no-cors"
58
+ });
59
+ result = {
60
+ message: resp.statusText,
61
+ code: resp.status,
62
+ error: resp.status > 299 ? true : false,
63
+ };
64
+ if (resp.status > 299)
65
+ return result;
66
+ await readIcsFromResponse(resp, eventCallback);
67
+ }
68
+ catch (e) {
69
+ console.log('Error occured while fetching events from proxy via calUrl:', e);
70
+ return {
71
+ message: `Fetch threw error: ${e}`,
72
+ code: 500,
73
+ error: true,
74
+ };
75
+ }
76
+ return result;
77
+ };
78
+ const getDate = (icsLine) => {
79
+ //e.g. DTEND;VALUE=DATE:20260818
80
+ const [typePart, valuePart] = icsLine.split('=');
81
+ const dateString = valuePart.split(':')[1];
82
+ let rawDateTime = lx.fromISO(dateString);
83
+ //end date has to be moved back when whole day ending
84
+ if (/^DTEND;VALUE$/.test(typePart)) {
85
+ if (rawDateTime.hour == 0 &&
86
+ rawDateTime.minute == 0 &&
87
+ rawDateTime.second == 0 &&
88
+ rawDateTime.millisecond == 0) {
89
+ rawDateTime = rawDateTime.plus({ hour: 12 });
90
+ }
91
+ }
92
+ //normalize to noon
93
+ return rawDateTime.set({ hour: 12, minute: 0, second: 0, millisecond: 0 });
94
+ };