+
+
`
+ },
+ generate_icon: function (item, fcolor = "", styling = "", classes = "") {
+ return `
`;
+ },
+
+
+ import_data: function () {
+ this.journey.data = Object.assign(
+ {},
+ JSON.parse(this.impexp.toDecoded()),
+ );
+ this.journey.data.main.forEach((e) => {
+ if (e.date_range) {
+ e.date_range[0] = new Date(e.date_range[0]);
+ e.date_range[1] = new Date(e.date_range[1]);
+ }
+ });
+ },
+ export_data: function () {
+ this.impexp = JSON.stringify(this.journey.data).toEncoded();
+ },
+ filter_selected: function (list, step) {
+ return list.filter((e) =>
+ step ? e.step == this.journey.sel_day : e.step >= 0,
+ );
+ },
+ filter_unselected: function (list) {
+ return list.filter((e) => e.step == undefined || e.step < 0);
+ },
+ remove_item: function (list, idx) {
+ list[idx].step = -1;
+ list.splice(idx, 1);
+ },
+ log: function (e) {
+ console.log(e);
+ },
+ place_delete: function (f, idx) {
+ switch (f) {
+ case "hotel": return this.journey.leg_get().hotel = null;
+ case "restaurant": return this.journey.leg_get().places.restaurants.splice(idx, 1);
+ case "activities": return this.journey.leg_get().places.activities.splice(idx, 1);
+ case "other": return;
+ case "flight": return this.journey.leg_get().travel.splice(idx, 1);
+ default: return true;
+ }
+ },
+ get_filter: function (f) {
+ switch (f) {
+ case "hotel": return api.is_hotel_type;
+ case "restaurant": return api.is_restauration_type;
+ case "place": return api.is_attraction_type;
+ case "other":
+ default: return () => true;
+ }
+ },
+
+ search_nominatim: function (f) {
+ return (q) => api.query_nominatim(q, this.compute_bb(), this.get_filter(f)).catch((_err) => []).then((r) => {
+ r.forEach((rr) => {
+ rr.latlon = [parseFloat(rr.lat), parseFloat(rr.lon)];
+ rr.sname = rr.display_name.split(",")[0];
+ });
+ r = r.filter(e => {
+ if (this.journey.leg_get().hotel && this.journey.leg_get().hotel.osm_id == e.osm_id) return false;
+ if (this.journey.leg_get().places.restaurants.find(i => i.osm_id == e.osm_id)) return false;
+ if (this.journey.leg_get().places.activities.find(i => i.osm_id == e.osm_id)) return false;
+ return true
+ })
+ this.query.load = false;
+ this.query.res = r;
+ return r
+ });
+ },
+ search_travel: function (f) {
+ return (q) => api.query_flight(q).then((r) => {
+ r.forEach(el => {
+ el.path = getGeoLine(
+ { lat: el.from_geo.lat, lng: el.from_geo.lon },
+ { lat: el.to_geo.lat, lng: el.to_geo.lon }, { dist: 2_500_000 }).map(v => [v.lat, v.lng])
+ el.type = "flight";
+ });
+ r = r.filter(e => {
+ if (this.journey.leg_get().travel.find(i => `${i.from}->${i.to}` == `${e.from}->${e.to}`)) return false;
+ return true
+ })
+ this.query.load = false;
+ this.query.res = r;
+ return r;
+ });
+ },
+
+ drawer_hover_item: function (item) {
+ if (item) {
+ this.map_override.active = true
+ if (item.type == 'flight') {
+ this.map_override.elements = [[item.from_geo.lat, item.from_geo.lon], [item.to_geo.lat, item.to_geo.lon]]
+ } else {
+ this.map_override.elements = [[item.lat, item.lon]]
+ }
+ } else {
+ this.map_override.active = false
+ }
+ },
+
+ drawer_click_item: function (item) {
+ const tpe = this.query.type;
+ this.query.res = [];
+ this.query.note = false;
+ this.query.type = null;
+ this.query.drawer = item ? true : false;
+ setTimeout(() => this.$refs.map.mapObject.invalidateSize(), 500);
+ this.query.sub = false;
+ this.drawer_hover_item()
+ if (item) {
+ item.step = -1;
+ switch (tpe) {
+ case 'hotel': return this.journey.leg_get().hotel = item;
+ case 'restaurant': return this.journey.leg_get().places.restaurants.push(item);
+ case 'place': return this.journey.leg_get().places.activities.push(item);
+ case 'other': return;
+ case 'flight': return this.journey.leg_get().travel.push(item);
+ }
+ }
+ },
+
+ search_active: function (q) {
+ const txt = q.target.value
+ this.query.load = true;
+ switch (this.query.type) {
+ case 'hotel': return this.search_hotel(txt);
+ case 'restaurant': return this.search_restaurant(txt);
+ case 'place': return this.search_place(txt);
+ case 'other': return this.search_other(txt);
+ case 'flight': return this.search_flight(txt);
+ }
+ },
+
+ search_enable: function (f) {
+ this.query.drawer = true;
+ setTimeout(() => this.$refs.map.mapObject.invalidateSize(), 500);
+ if (f == "notes") {
+ this.query.note = true;
+ this.query.type = null;
+ const query_in = document.getElementById('query_note')
+ setTimeout(() => query_in.focus(), 500);
+ return;
+ }
+ this.query.note = false;
+ this.query.type = f;
+ const query_in = document.getElementById('query_input')
+ setTimeout(() => query_in.focus(), 500);
+ this.search_active({ target: query_in })
+ },
+
+
+ sideScroll: function (element, direction, speed, step) {
+ this.leg_nav.scrollDir = direction
+ if (direction == 'none') return;
+ this.leg_nav.scrollInterval = setInterval(() => {
+ element.scrollLeft += (direction == 'left') ? -step : step;
+ }, speed);
+ },
+
+ keyboardEvent(e) {
+ if (e.which === 13) {
+ }
+ },
+ nav_mousemove(e) {
+ const c = document.querySelector('.scroll-content')
+ const left = e.pageX - c.getBoundingClientRect().left;
+ const newDir =
+ left < c.offsetWidth * 0.1 ? 'left' :
+ (left > c.offsetWidth * 0.9 ? 'right' : 'none')
+ if (!this.leg_nav.scrollInterval || this.leg_nav.scrollDir != newDir) {
+ if (this.leg_nav.scrollInterval) clearInterval(this.leg_nav.scrollInterval)
+ this.sideScroll(c, newDir, 25, 10);
+ }
+ },
+ nav_mouseleave(e) {
+ clearInterval(this.leg_nav.scrollInterval);
+ this.leg_nav.scrollDir = 'none'
+ this.leg_nav.scrollInterval = null
+ },
+
+ refreshTextAreaHeight(event) {
+ console.log("AAA", event.target.scrollHeight, event.target)
+ event.target.style['height'] = 'auto';
+ event.target.style['height'] = event.target.scrollHeight + 'px';
+ event.target.style['max-height'] = "100%";
+ },
+ },
+ created: function () {
+ this.search_hotel = api.throttle(this.search_nominatim("hotel"), 1000)
+ this.search_restaurant = api.throttle(this.search_nominatim("restaurant"), 1000)
+ this.search_place = api.throttle(this.search_nominatim("place"), 1000)
+ this.save_data = api.throttle(() => {
+ this.impexp = JSON.stringify(this.journey.data).toEncoded();
+ api.save(this.journey.id, this.journey.data);
+ }, 1000);
+ this.search_flight = api.throttle(this.search_travel("flight"), 2000)
+
+ window.addEventListener("keydown", (e) => {
+ switch (e.key) {
+ case "ArrowLeft":
+ this.journey.day_prev();
+ break;
+ case "ArrowRight":
+ this.journey.day_next();
+ break;
+ default:
+ console.log(e.key);
+ }
+ });
+
+ api.load(this.journey.id).then((r) => {
+ app.journey.data = migrator(r)
+ });
+ },
+ watch: {
+ journey: {
+ handler: function (ndata, odata) {
+ if (this.edit_active)
+ this.save_data();
+ },
+ deep: true,
+ },
+ },
+});
+
+
diff --git a/src/types/ext.ts b/src/client/types/ext.ts
similarity index 69%
rename from src/types/ext.ts
rename to src/client/types/ext.ts
index cddb2b8..0344b70 100644
--- a/src/types/ext.ts
+++ b/src/client/types/ext.ts
@@ -2,8 +2,10 @@
declare global {
interface Date {
toJSONLocal: () => string;
+ toLocal: () => string;
}
}
+
Date.prototype.toJSONLocal = function () {
function addZ(n: number): string {
return n <= 9 ? `0${n}` : `${n}`;
@@ -13,7 +15,27 @@ Date.prototype.toJSONLocal = function () {
addZ(this.getMonth() + 1),
addZ(this.getDate()),
].join("-");
-};
+}
+
+Date.prototype.toLocal = function () {
+ return [["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][this.getDay()],
+ this.getDate(),
+ [
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec",
+ ][this.getMonth()]].join(" ")
+ ;
+}
// ARRAY EXTENTION
declare global {
@@ -34,27 +56,20 @@ Array.prototype.foldl = function
(f: (x: T, acc: B) => B, acc: B): B {
// STRING EXTENTION
declare global {
interface String {
- btoa: () => String;
toEncoded: () => String;
toDecoded: () => String;
}
}
-String.prototype.btoa = function () {
- return window.btoa(this);
-};
-
String.prototype.toEncoded = function () {
- return window.btoa(
- Array.from(this as string, (c) => c.charCodeAt(0)).foldl(
- (e, v) => v + String.fromCharCode(e),
- "",
- ),
- );
+ return window.btoa(encodeURIComponent(Array.from(this as string, (c) => c.charCodeAt(0)).foldl(
+ (e, v) => v + String.fromCharCode(e),
+ "",
+ )))
};
String.prototype.toDecoded = function () {
- return Array.from(window.atob(this), (c) => c.charCodeAt(0)).foldl(
+ return Array.from(decodeURIComponent(window.atob(this as string)), (c) => c.charCodeAt(0)).foldl(
(e, v) => v + String.fromCharCode(e),
"",
);
@@ -76,4 +91,4 @@ String.gen_id = function (length) {
.join("");
};
-export {};
+export { };
diff --git a/src/client/types/format.ts b/src/client/types/format.ts
new file mode 100644
index 0000000..a0f1ff9
--- /dev/null
+++ b/src/client/types/format.ts
@@ -0,0 +1,59 @@
+
+
+declare global {
+ interface LatLng {
+ lat: number
+ lng: number
+ }
+ interface geoloc {
+ latlon: [number, number]
+ notes: string
+ step: -1
+ }
+
+ interface map {
+ zoom: number
+ center: LatLng
+ }
+
+
+ interface leg {
+ title: string
+ day_title: string[]
+ date_range: [Date, Date] | null
+ map: map
+ travel: unknown[]
+ hotel: geoloc | null
+ places: {
+ restaurants: geoloc[]
+ activities: geoloc[]
+ }
+ notes: string
+ }
+
+ interface journey {
+ fmt_ver: number
+ title: string
+ main: leg[]
+ }
+}
+
+const leg_template: leg = {
+ title: "",
+ day_title: [],
+ map: { zoom: 2, center: { lng: 0, lat: 0 } },
+ travel: [],
+ hotel: null,
+ places: { restaurants: [], activities: [] },
+ notes: "",
+ date_range: null
+}
+const journey_template: journey = {
+ fmt_ver: 1,
+ title: "New Journey",
+ main: [leg_template],
+}
+
+
+export { map, geoloc, leg, journey }
+export { journey_template, leg_template }
\ No newline at end of file
diff --git a/src/client/types/geom.ts b/src/client/types/geom.ts
new file mode 100644
index 0000000..23a864a
--- /dev/null
+++ b/src/client/types/geom.ts
@@ -0,0 +1,165 @@
+const ellipsoid = {
+ a: 6378137,
+ b: 6356752.3142,
+ f: 1 / 298.257223563
+};
+
+function mod(n: number, p: number): number {
+ const r = n % p;
+ return r < 0 ? r + p : r;
+}
+
+function wrap(degrees: number, max = 360) {
+ if (-max <= degrees && degrees <= max) {
+ return degrees;
+ } else {
+ return mod(degrees + max, 2 * max) - max;
+ }
+}
+
+function dist(src: LatLng, dst: LatLng, itr = 100, mit = true): number {
+ const p1 = src,
+ p2 = dst;
+ const φ1 = toRadians(p1.lat),
+ λ1 = toRadians(p1.lng);
+ const φ2 = toRadians(p2.lat),
+ λ2 = toRadians(p2.lng);
+ const π = Math.PI;
+ const ε = Number.EPSILON;
+
+ // allow alternative ellipsoid to be specified
+ const { a, b, f } = ellipsoid;
+
+ const dL = λ2 - λ1; // L = difference in longitude, U = reduced latitude, defined by tan U = (1-f)·tanφ.
+ const tanU1 = (1 - f) * Math.tan(φ1),
+ cosU1 = 1 / Math.sqrt(1 + tanU1 * tanU1),
+ sinU1 = tanU1 * cosU1;
+ const tanU2 = (1 - f) * Math.tan(φ2),
+ cosU2 = 1 / Math.sqrt(1 + tanU2 * tanU2),
+ sinU2 = tanU2 * cosU2;
+
+ const antipodal = Math.abs(dL) > π / 2 || Math.abs(φ2 - φ1) > π / 2;
+
+ let λ = dL,
+ sinλ: number | null = null,
+ cosλ: number | null = null; // λ = difference in longitude on an auxiliary sphere
+ let σ = antipodal ? π : 0,
+ sinσ = 0,
+ cosσ = antipodal ? -1 : 1,
+ sinSqσ: number | null = null; // σ = angular distance P₁ P₂ on the sphere
+ let cos2σₘ = 1; // σₘ = angular distance on the sphere from the equator to the midpoint of the line
+ let sinα: number | null = null,
+ cosSqα = 1; // α = azimuth of the geodesic at the equator
+ let C: number | null = null;
+
+ let λʹ: number | null = null,
+ iterations = 0;
+ do {
+ sinλ = Math.sin(λ);
+ cosλ = Math.cos(λ);
+ sinSqσ =
+ cosU2 * sinλ * (cosU2 * sinλ) +
+ (cosU1 * sinU2 - sinU1 * cosU2 * cosλ) * (cosU1 * sinU2 - sinU1 * cosU2 * cosλ);
+ if (Math.abs(sinSqσ) < ε) {
+ break; // co-incident/antipodal points (falls back on λ/σ = L)
+ }
+ sinσ = Math.sqrt(sinSqσ);
+ cosσ = sinU1 * sinU2 + cosU1 * cosU2 * cosλ;
+ σ = Math.atan2(sinσ, cosσ);
+ sinα = (cosU1 * cosU2 * sinλ) / sinσ;
+ cosSqα = 1 - sinα * sinα;
+ cos2σₘ = cosSqα !== 0 ? cosσ - (2 * sinU1 * sinU2) / cosSqα : 0; // on equatorial line cos²α = 0 (§6)
+ C = (f / 16) * cosSqα * (4 + f * (4 - 3 * cosSqα));
+ λʹ = λ;
+ λ = dL + (1 - C) * f * sinα * (σ + C * sinσ * (cos2σₘ + C * cosσ * (-1 + 2 * cos2σₘ * cos2σₘ)));
+ const iterationCheck = antipodal ? Math.abs(λ) - π : Math.abs(λ);
+ if (iterationCheck > π) {
+ throw new EvalError("λ > π");
+ }
+ } while (Math.abs(λ - λʹ) > 1e-12 && ++iterations < itr);
+
+ if (iterations >= itr) {
+ if (mit) {
+ return dist(
+ src,
+ { lat: dst.lat, lng: dst.lng - 0.01 },
+ itr,
+ mit
+ );
+ } else {
+ throw new EvalError(`Inverse vincenty formula failed to converge after ${itr} iterations
+ (start=${src.lat}/${src.lng}; dest=${dst.lat}/${dst.lng})`);
+ }
+ }
+ const uSq = (cosSqα * (a * a - b * b)) / (b * b);
+ const A = 1 + (uSq / 16384) * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
+ const B = (uSq / 1024) * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
+ const Δσ =
+ B *
+ sinσ *
+ (cos2σₘ +
+ (B / 4) *
+ (cosσ * (-1 + 2 * cos2σₘ * cos2σₘ) -
+ (B / 6) * cos2σₘ * (-3 + 4 * sinσ * sinσ) * (-3 + 4 * cos2σₘ * cos2σₘ)));
+
+ const s = b * A * (σ - Δσ); // s = length of the geodesic
+ return s
+
+}
+
+function pointDistance(src: LatLng, dst: LatLng): number {
+ return dist(
+ { lat: src.lat, lng: wrap(src.lng, 180) },
+ { lat: dst.lat, lng: wrap(dst.lng, 180) }
+ );
+}
+
+function toRadians(degree: number): number {
+ return (degree * Math.PI) / 180;
+}
+
+function toDegrees(radians: number): number {
+ return (radians * 180) / Math.PI;
+}
+
+function midpoint(src: LatLng, dst: LatLng): LatLng {
+ // φm = atan2( sinφ1 + sinφ2, √( (cosφ1 + cosφ2⋅cosΔλ)² + cos²φ2⋅sin²Δλ ) )
+ // λm = λ1 + atan2(cosφ2⋅sinΔλ, cosφ1 + cosφ2⋅cosΔλ)
+ // midpoint is sum of vectors to two points: mathforum.org/library/drmath/view/51822.html
+
+ const φ1 = toRadians(src.lat);
+ const λ1 = toRadians(src.lng);
+ const φ2 = toRadians(dst.lat);
+ const Δλ = toRadians(dst.lng - src.lng);
+
+ // get cartesian coordinates for the two points
+ const A = { x: Math.cos(φ1), y: 0, z: Math.sin(φ1) }; // place point A on prime meridian y=0
+ const B = { x: Math.cos(φ2) * Math.cos(Δλ), y: Math.cos(φ2) * Math.sin(Δλ), z: Math.sin(φ2) };
+
+ // vector to midpoint is sum of vectors to two points (no need to normalise)
+ const C = { x: A.x + B.x, y: A.y + B.y, z: A.z + B.z };
+
+ const φm = Math.atan2(C.z, Math.sqrt(C.x * C.x + C.y * C.y));
+ const λm = λ1 + Math.atan2(C.y, C.x);
+
+ return { lat: toDegrees(φm), lng: toDegrees(λm) };
+}
+
+function recursiveMidPoint(src: LatLng, dst: LatLng, opt: { step?: number, dist?: number } = {}, curr = 0): LatLng[] {
+ const geom: LatLng[] = [src, dst];
+ const mp = midpoint(src, dst);
+ const split_step = (opt.step != undefined && (opt.step > 0 || curr == 0))
+ const split_dist = (opt.dist != undefined && (pointDistance(src, dst) > opt.dist || curr == 0))
+ const next_opt = split_step ? { step: (opt.step || 0) - 1 } : { dist: opt.dist };
+ if (split_step || split_dist) {
+ geom.splice(0, 1, ...recursiveMidPoint(src, mp, next_opt, curr + 1));
+ geom.splice(geom.length - 2, 2, ...recursiveMidPoint(mp, dst, next_opt, curr + 1));
+ } else {
+ geom.splice(1, 0, mp);
+ }
+ return geom;
+}
+
+export function getGeoLine(src: LatLng, dst: LatLng, opt: { step?: number, dist?: number }) {
+ return recursiveMidPoint(src, dst, opt, 1)
+}
\ No newline at end of file
diff --git a/src/client/types/migration.ts b/src/client/types/migration.ts
new file mode 100644
index 0000000..2ccf0c0
--- /dev/null
+++ b/src/client/types/migration.ts
@@ -0,0 +1,24 @@
+const FMT_VER_0 = 0
+const FMT_VER_LATEST = FMT_VER_0
+
+function migrate_A_to_0(e: journey): journey {
+ e.title = (e as any).name;
+ e.main.forEach((v) => {
+ v.date_range = v.date_range || (v as any).dateRange;
+ v.day_title = v.day_title || (v as any).step_title;
+ v.places.activities = v.places.activities || (v as any).places.places;
+ v.travel = v.travel || [];
+ })
+ console.log(e)
+ return e;
+}
+
+export const migrator = (e: journey): journey => {
+ if (e.fmt_ver == FMT_VER_LATEST) return e;
+ switch (e.fmt_ver) {
+ case FMT_VER_0: break; // Update when FMT_VER_1 releases
+ default:
+ return migrate_A_to_0(e)
+ }
+ return e;
+}
\ No newline at end of file
diff --git a/src/client/types/wrapper.ts b/src/client/types/wrapper.ts
new file mode 100644
index 0000000..ade7429
--- /dev/null
+++ b/src/client/types/wrapper.ts
@@ -0,0 +1,144 @@
+import { journey_template, leg_template } from "./format"
+
+const date_day_diff = (d0: Date, d1: Date): number =>
+ (d1.getTime() - d0.getTime()) / (1000 * 60 * 60 * 24)
+
+class journey_wrapper {
+ id: String
+ data: journey = journey_template;
+ sel_leg: number = 0;
+ sel_day: number = 0;
+
+ constructor(id: String) {
+ this.id = id;
+ }
+
+ leg_first = () => this.data.main[0]
+ leg_last = () => this.data.main[this.leg_count() - 1]
+ leg_count(): number {
+ return this.data.main.length;
+ }
+ leg_len(idx?: number): number {
+ let d = this.leg_get(idx == undefined ? this.sel_leg : idx).date_range;
+ return d ? date_day_diff(d[0], d[1]) + 1 : 1;
+ }
+ add_leg(): void {
+ if (this.data.main == undefined) this.data.main = [];
+ this.data.main.push(leg_template);
+ }
+ rm_leg(idx: number): void {
+ this.data.main.splice(idx, 1);
+ if (this.sel_leg == idx) this.leg_prev();
+ if (this.sel_leg > this.data.main.length - 1) this.leg_next();
+ }
+ tot_len(): number | "?" {
+ if (this.leg_count() == 0) return 0;
+ let lf = this.leg_first(), ll = this.leg_last();
+ if (lf.date_range && ll.date_range) {
+ let d0 = lf.date_range[0]
+ let d1 = ll.date_range[1]
+ return date_day_diff(d0, d1);
+ }
+ return "?";
+ }
+ leg_sel(idx: number): void {
+ this.sel_leg = idx;
+ this.sel_day = 0;
+ }
+ leg_get(idx?: number): leg {
+ return this.data.main[idx != undefined ? idx : this.sel_leg]
+ }
+ leg_next(): void {
+ this.sel_leg = Math.min(this.sel_leg + 1, this.leg_count() - 1);
+ this.sel_day = 0;
+ }
+ leg_prev(): void {
+ this.sel_leg = Math.max(this.sel_leg - 1, 0);
+ this.sel_day = 0;
+ }
+ day_next() {
+ this.sel_day += 1
+ if (this.sel_day > this.leg_len() - 1) {
+ if (this.sel_leg < this.leg_count() - 1) {
+ this.leg_next()
+ this.sel_day = 0;
+ } else {
+ this.sel_day = this.leg_len() - 1;
+ }
+ }
+ }
+ day_prev() {
+ this.sel_day -= 1
+ if (this.sel_day < 0) {
+ if (this.sel_leg > 0) {
+ this.leg_prev()
+ this.sel_day = this.leg_len() - 1;
+ } else {
+ this.sel_day = 0;
+ }
+ }
+ }
+ date_sel(): string {
+ if (this.sel_day < 0) return "?";
+ let leg = this.leg_get()
+ if (!leg.date_range)
+ return "?";
+ var date = new Date(leg.date_range[0]);
+ date.setDate(date.getDate() + this.sel_day);
+ return date.toLocal();
+ }
+ date_tot() {
+ if (this.leg_count() == 0) return "";
+ let lf = this.leg_first(), ll = this.leg_last();
+ if (lf.date_range && ll.date_range)
+ return `${lf.date_range[0].toLocal()} - ${ll.date_range[1].toLocal()}`;
+ return "?";
+ }
+
+ date_update(idx: number) {
+ let date_range = this.leg_get(idx).date_range;
+ if (!date_range) return;
+ let start_end = [0, 0];
+ let step_len = 0;
+
+ let last_start = date_range[0];
+ for (let i = idx - 1; i >= 0; --i) {
+ step_len = this.leg_len(i) - 1;
+ if (this.leg_get(i).date_range) {
+ start_end = [last_start.getDate() - step_len, last_start.getDate()];
+ } else {
+ this.leg_get(i).date_range = [new Date(), new Date()];
+ start_end = [last_start.getDate() - step_len, last_start.getDate()];
+ }
+ let leg = this.leg_get(i)
+ if (leg.date_range) {
+ leg.date_range[0].setTime(last_start.getTime());
+ leg.date_range[0].setDate(start_end[0]);
+ leg.date_range[1].setTime(last_start.getTime());
+ leg.date_range[1].setDate(start_end[1]);
+ last_start = leg.date_range[0];
+ }
+ }
+
+ let last_end = date_range[1];
+ for (let i = idx + 1; i < this.leg_count(); ++i) {
+ step_len = this.leg_len(i) - 1;
+ if (this.leg_get(i).date_range) {
+ start_end = [last_end.getDate(), last_end.getDate() + step_len];
+ } else {
+ this.leg_get(i).date_range = [new Date(), new Date()];
+ start_end = [last_end.getDate(), last_end.getDate() + step_len];
+ }
+ let leg = this.leg_get(i)
+ if (leg.date_range) {
+ leg.date_range[0].setTime(last_end.getTime());
+ leg.date_range[0].setDate(start_end[0]);
+ leg.date_range[1].setTime(last_end.getTime());
+ leg.date_range[1].setDate(start_end[1]);
+ last_end = leg.date_range[1];
+ }
+ }
+ }
+}
+
+export default journey_wrapper;
\ No newline at end of file
diff --git a/src/old.js b/src/old.js
deleted file mode 100644
index 07049a7..0000000
--- a/src/old.js
+++ /dev/null
@@ -1,344 +0,0 @@
-import * as api from "./api";
-
-Vue.component("l-map", window.Vue2Leaflet.LMap);
-Vue.component("l-tile-layer", window.Vue2Leaflet.LTileLayer);
-Vue.component("l-marker", window.Vue2Leaflet.LMarker);
-Vue.component("l-icon", window.Vue2Leaflet.LIcon);
-Vue.component("l-popup", window.Vue2Leaflet.LPopup);
-Vue.component("l-tooltip", window.Vue2Leaflet.LTooltip);
-Vue.component("l-control-scale", window.Vue2Leaflet.LControlScale);
-Vue.component("multiselect", window.VueMultiselect.default);
-Vue.use(window.VueTextareaAutosize);
-
-const app = new Vue({
- el: "#app",
- data: {
- journey_edit:
- ["view", "short"].indexOf(window.location.pathname.split("/")[1]) == -1,
- journey_id: window.location.pathname.split("/").pop() || String.gen_id(16),
-
- journey_step_data: { day: 1, section: 0 },
- journey_data: {
- name: "New Journey",
- main: [],
- },
-
- query: { hotel: [], flight: [], nominatim: [] },
- querying: { hotel: false, flight: false, place: false, food: false },
- impexp: "",
- lang: {
- format: "ddd D MMM",
- formatLocale: {
- firstDayOfWeek: 1,
- },
- monthBeforeYear: true,
- },
- },
- methods: {
- start_journey: function (event) {
- window.location.href = "/" + this.journey_id;
- },
- add_section: function (event) {
- if (this.journey_data.main == undefined) this.journey_data.main = [];
- this.journey_data.main.push({
- title: "?",
- step_title: [],
- map: { zoom: 2 },
- hotel: { latlon: [0, 0] },
- places: { restaurants: [], places: [] },
- });
- },
- step_len: function (idx) {
- return this.journey_data.main[idx].dateRange
- ? (this.journey_data.main[idx].dateRange[1] -
- this.journey_data.main[idx].dateRange[0]) /
- (1000 * 60 * 60 * 24) +
- 1
- : 1;
- },
- next_step: function () {
- this.journey_step_data.day += 1;
- let s = this.journey_step_data.section;
- let cd = this.step_len(s);
-
- if (this.journey_step_data.day > cd) {
- this.journey_step_data.section += 1;
- if (this.journey_step_data.section >= this.journey_data.main.length) {
- this.journey_step_data.section = this.journey_data.main.length - 1;
- this.journey_step_data.day = cd;
- } else {
- this.journey_step_data.day = 1;
- }
- }
- },
- prev_step: function () {
- this.journey_step_data.day -= 1;
- if (this.journey_step_data.day <= 0) {
- this.journey_step_data.section -= 1;
- if (this.journey_step_data.section < 0) {
- this.first_step();
- } else {
- let s = this.journey_step_data.section;
-
- let cd = this.step_len(s);
- this.journey_step_data.day = cd;
- }
- }
- },
- nextnext_step: function () {
- this.journey_step_data.section += 1;
- this.journey_step_data.day = 1;
- if (this.journey_step_data.section >= this.journey_data.main.length)
- this.first_step();
- },
- prevprev_step: function () {
- this.journey_step_data.section -= 1;
- this.journey_step_data.day = 1;
- if (this.journey_step_data.section < 0) this.first_step();
- },
- first_step: function () {
- this.journey_step_data.section = 0;
- this.journey_step_data.day = 1;
- },
-
- active_date: function () {
- if (this.journey_step_data.day < 0) return "?";
- if (!this.journey_data.main[this.journey_step_data.section].dateRange)
- return "?";
- var date = new Date(
- this.journey_data.main[this.journey_step_data.section].dateRange[0],
- );
- date.setDate(date.getDate() + this.journey_step_data.day - 1);
- return this.format_date(date);
- },
- format_date: function (d) {
- return (
- ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][d.getDay()] +
- " " +
- d.getDate() +
- " " +
- [
- "Jan",
- "Feb",
- "Mar",
- "Apr",
- "May",
- "Jun",
- "Jul",
- "Aug",
- "Sep",
- "Oct",
- "Nov",
- "Dec",
- ][d.getMonth()]
- );
- },
-
- total_days: function () {
- if (this.journey_data.main.length == 0) return 0;
- try {
- return (
- (this.journey_data.main[this.journey_data.main.length - 1]
- .dateRange[1] -
- this.journey_data.main[0].dateRange[0]) /
- (1000 * 60 * 60 * 24)
- );
- } catch {
- return "?";
- }
- },
- total_date: function () {
- if (this.journey_data.main.length == 0) return "";
- try {
- return `${this.format_date(
- this.journey_data.main[0].dateRange[0],
- )} - ${this.format_date(
- this.journey_data.main[this.journey_data.main.length - 1]
- .dateRange[1],
- )}`;
- } catch {
- return "?";
- }
- },
- update_date: function (idx) {
- let dateRange = this.journey_data.main[idx].dateRange;
- let start_end = [0, 0];
- let step_len = 0;
-
- let last_start = dateRange[0];
- for (let i = idx - 1; i >= 0; --i) {
- step_len = this.step_len(i) - 1;
- if (this.journey_data.main[i].dateRange) {
- start_end = [last_start.getDate() - step_len, last_start.getDate()];
- } else {
- this.journey_data.main[i].dateRange = [new Date(), new Date()];
- start_end = [last_start.getDate() - step_len, last_start.getDate()];
- }
- this.journey_data.main[i].dateRange[0].setTime(last_start.getTime());
- this.journey_data.main[i].dateRange[0].setDate(start_end[0]);
- this.journey_data.main[i].dateRange[1].setTime(last_start.getTime());
- this.journey_data.main[i].dateRange[1].setDate(start_end[1]);
- last_start = this.journey_data.main[i].dateRange[0];
- }
-
- let last_end = dateRange[1];
- for (let i = idx + 1; i < this.journey_data.main.length; ++i) {
- step_len = this.step_len(i) - 1;
- if (this.journey_data.main[i].dateRange) {
- start_end = [last_end.getDate(), last_end.getDate() + step_len];
- } else {
- this.journey_data.main[i].dateRange = [new Date(), new Date()];
- start_end = [last_end.getDate(), last_end.getDate() + step_len];
- }
- this.journey_data.main[i].dateRange[0].setTime(last_end.getTime());
- this.journey_data.main[i].dateRange[0].setDate(start_end[0]);
- this.journey_data.main[i].dateRange[1].setTime(last_end.getTime());
- this.journey_data.main[i].dateRange[1].setDate(start_end[1]);
- last_end = this.journey_data.main[i].dateRange[1];
- }
- },
-
- rm_section: function (idx) {
- this.journey_data.main.splice(idx, 1);
- if (this.journey_step_data.section == idx) {
- this.prevprev_step();
- }
- },
- sel_section: function (idx) {
- this.journey_step_data.section = idx;
- this.journey_step_data.day = 1;
- },
- search_nominatim: function (txt, f) {
- if (txt == "") {
- this.query.nominatim = [];
- return Promise.resolve([]);
- }
- return query_nominatim(txt, f).then((results) => {
- results.forEach((r) => {
- r.latlon = [parseFloat(r.lat), parseFloat(r.lon)];
- r.sname = r.display_name.split(",")[0];
- });
- this.query.nominatim = results;
- });
- },
- search_flight: function (txt) {
- if (txt == "") return;
- this.querying.flight = true;
- query_flight(txt.replace(" ", "")).then((results) => {
- if (results.results == "") {
- this.query.flight = [];
- this.querying.flight = false;
- return;
- }
- this.query.flight = results.results;
- this.querying.flight = false;
- });
- },
- generate_icon: function (item, fcolor) {
- return L.AwesomeMarkers.icon({
- icon: api.icon_type(item) || "star",
- prefix: "fa",
- markerColor: fcolor || item.color || "blue",
- }).createIcon().outerHTML;
- },
-
- save_data: function () {
- this.impexp = JSON.stringify(this.journey_data).toEncoded();
- api.save(this.journey_id, this.journey_data);
- },
- import_data: function () {
- this.journey_data = Object.assign(
- {},
- JSON.parse(this.impexp.toDecoded()),
- );
- this.journey_data.main.forEach((e) => {
- if (e.dateRange) {
- e.dateRange[0] = new Date(e.dateRange[0]);
- e.dateRange[1] = new Date(e.dateRange[1]);
- }
- });
- },
- export_data: function () {
- this.impexp = JSON.stringify(this.journey_data).toEncoded();
- },
- filter_selected: function (list, step) {
- return list.filter((e) =>
- step ? e.step == this.journey_step_data.day : e.step >= 0,
- );
- },
- filter_unselected: function (list) {
- return list.filter((e) => e.step == undefined || e.step < 0);
- },
- remove_item: function (list, idx) {
- list[idx].step = -1;
- list.splice(idx, 1);
- },
- log: function (e) {
- console.log(e);
- },
-
- keyboardEvent(e) {
- if (e.which === 13) {
- }
- },
- },
- created: function () {
- window.addEventListener("keydown", (e) => {
- switch (e.key) {
- case "ArrowLeft":
- this.prev_step();
- break;
- case "ArrowRight":
- this.next_step();
- break;
- default:
- console.log(e.key);
- }
- });
-
- api.load(this.journey_id).then((r) => (app.journey_data = r));
-
- this.debounceSave = _.debounce(this.save_data, 500);
- this.debounceSearch = {
- hotel: _.debounce((q) => {
- this.querying.hotel = true;
- this.search_nominatim(
- q,
- (r) =>
- r.type == "hotel" || r.type == "hostel" || r.type == "guest_house",
- ).then((r) => {
- this.querying.hotel = false;
- });
- }, 500),
- restaurants: _.debounce((q) => {
- this.querying.food = true;
- this.search_nominatim(q, (r) => api.is_restauration_type(r)).then(
- (r) => {
- this.querying.food = false;
- },
- );
- }, 500),
- places: _.debounce((q) => {
- this.querying.place = true;
- this.search_nominatim(q, (r) => api.is_attraction_type(r)).then((r) => {
- this.querying.place = false;
- });
- }, 500),
- other: _.debounce((q) => {
- this.querying.any = true;
- this.search_nominatim(q, (r) => true).then((r) => {
- this.querying.any = false;
- });
- }, 500),
- flight: _.debounce((q) => this.search_flight(q), 500),
- };
- },
- watch: {
- journey_data: {
- handler: function (ndata, odata) {
- this.debounceSave();
- },
- deep: true,
- },
- },
-});
diff --git a/src/server/api.ts b/src/server/api.ts
new file mode 100644
index 0000000..9b8b531
--- /dev/null
+++ b/src/server/api.ts
@@ -0,0 +1,90 @@
+//import { ProxyAgent, setGlobalDispatcher } from 'undici';
+
+import { flight_get_data } from './api_flight'
+import { nominatim_get_data } from './api_nominatim';
+
+//setGlobalDispatcher(new ProxyAgent(process.env.HTTPS_PROXY as string));
+
+
+export default function (server, opts, done) {
+ server.get("/flight/:id", async (req, reply) =>
+ flight_get_data(req.params.id)
+ .then(res => {
+ let wait_for_all: Promise[] = []
+ res.forEach(r => {
+ wait_for_all.push(nominatim_get_data(r.from).then(geo => (r as any).from_geo = geo[0]));
+ wait_for_all.push(nominatim_get_data(r.to).then(geo => (r as any).to_geo = geo[0]));
+ });
+ return Promise.all(wait_for_all).then(_ => res)
+ })
+ .then(res => reply.send(res))
+ );
+
+ server.get("/place/:id", async (req, reply) =>
+ nominatim_get_data(req.params.id, JSON.parse(req.query.bb))
+ .then(res => reply.send(res))
+ );
+
+ server.get("/gpx/:id", async (req, reply) => {
+ if (req.params.id == undefined)
+ return reply.code(400).send({ error: "No ID query parameter" });
+
+ server.level.db.get(req.params.id, (err, val) => {
+ if (err) {
+ console.warn(err);
+ reply.code(500).send();
+ } else {
+ let file = ''
+ const data = JSON.parse(val);
+ const gen_wpt = (name, desc, latlon, icon = "Flag") => `0${name}-${desc}${icon}`
+ const esc_str = (str) => (str || "Undefined").replace('"', """).replace("'", "'").replace("<", "<").replace(">", ">").replace("&", "&").replace("\n", "...")
+ data.main.forEach(a => {
+ file += gen_wpt(esc_str(a.hotel.name), esc_str(a.hotel.notes), a.hotel.latlon, "Hotel");
+ a.places.restaurants.forEach(b => {
+ file += gen_wpt(esc_str(b.name), esc_str(b.notes), b.latlon, "Restaurant");
+ });
+ a.places.activities.forEach(b => {
+ file += gen_wpt(esc_str(b.name), esc_str(b.notes), b.latlon, "Tree");
+ });
+ });
+ file += "";
+ reply.header('Content-Type', 'application/gpx+xml');
+ reply.header('Content-Disposition', `attachment; filename=${req.params.id}.gpx`);
+ reply.send(file);
+ }
+ });
+ return reply;
+ });
+
+ server.get("/:id", async (req, reply) => {
+ if (req.params.id == undefined)
+ return reply.code(400).send({ error: "No ID query parameter" });
+
+ server.level.db.get(req.params.id, (err, val) => {
+ if (err) {
+ console.warn(err);
+ reply.code(500).send();
+ } else {
+ reply.send(JSON.parse(val));
+ }
+ });
+ return reply;
+ });
+
+ server.post("/:id", async (req, reply) => {
+ if (req.params.id == undefined)
+ return reply.code(400).send({ error: "No ID query parameter" });
+
+ server.level.db.put(req.params.id, req.body, (err) => {
+ if (err) {
+ console.warn(err);
+ reply.code(500).send({ error: "Error with DB" });
+ } else {
+ reply.send({ content: "ok" });
+ }
+ });
+ return reply;
+ });
+
+ done();
+};
diff --git a/src/server/api_flight.ts b/src/server/api_flight.ts
new file mode 100644
index 0000000..14f5977
--- /dev/null
+++ b/src/server/api_flight.ts
@@ -0,0 +1,86 @@
+import { JSDOM } from 'jsdom';
+
+interface FlightData {
+ id: string;
+ date: string;
+ from: string;
+ to: string;
+ std: string;
+ atd: string | string[] | null;
+ sta: string;
+ ata: string | string[] | null;
+}
+
+function clean_times(s: string): string | null {
+ if (s == "—" || s == "Scheduled") return null
+ if (s.indexOf("Estimated departure") == 0) return null
+ if (s.indexOf("Landed") == 0) return s.replace("Landed ", "")
+ return s
+}
+
+export function flight_get_data(flightId: string): Promise {
+ const url = new URL(`https://www.flightradar24.com/data/flights/${flightId}`);
+ return fetch(url).then(res => res.text()).then(res => new JSDOM(res)).then(dom => {
+ const rows = dom.window.document.querySelectorAll('table tbody tr');
+ const flightData: FlightData[] = [];
+ rows.forEach(row => {
+ const columns = row.querySelectorAll('td');
+ /*
+ * 2/6 = Date/Duration
+ * 3/4 = From/To
+ * 7/8 = STD/ATD
+ * 9/11 = STA/ATA
+ */
+ const flight: FlightData = {
+ id: flightId,
+ date: columns[2].textContent.trim(),
+ from: columns[3].textContent.trim(),
+ to: columns[4].textContent.trim(),
+ std: columns[7].textContent.trim(),
+ atd: clean_times(columns[8].textContent.trim()),
+ sta: columns[9].textContent.trim(),
+ ata: clean_times(columns[11].textContent.trim())
+ };
+ flightData.push(flight);
+ });
+
+ return groupByPair(flightData).map(v => groupByFrequency(v));
+ })
+}
+
+function groupByPair(flightData: FlightData[]): FlightData[][] {
+ const flightMap: { [key: string]: FlightData[] } = {};
+ flightData.forEach(flight => {
+ const key = `${flight.from}-${flight.to}-${flight.std}`;
+ if (!flightMap[key])
+ flightMap[key] = [];
+ flightMap[key].push(flight);
+ });
+ return Object.values(flightMap);
+}
+
+function groupByFrequency(flightData: FlightData[]): FlightData {
+ let data = 'OOOOOOO'; // Initialize with no flights
+ const atdArray: string[] = [];
+ const ataArray: string[] = [];
+
+ flightData.forEach(flight => {
+ const dayOfWeek = (new Date(flight.date).getDay() + 6) % 7;
+ data = data.substring(0, dayOfWeek) + 'X' + data.substring(dayOfWeek + 1);
+ if (flight.atd)
+ atdArray.push(flight.atd as string);
+ if (flight.ata)
+ ataArray.push(flight.ata as string);
+ });
+
+ return {
+ id: flightData[0].id,
+ date: data,
+ from: flightData[0].from,
+ to: flightData[0].to,
+ std: flightData[0].std,
+ sta: flightData[0].sta,
+ atd: atdArray,
+ ata: ataArray
+ };
+};
diff --git a/src/server/api_nominatim.ts b/src/server/api_nominatim.ts
new file mode 100644
index 0000000..874b77c
--- /dev/null
+++ b/src/server/api_nominatim.ts
@@ -0,0 +1,27 @@
+
+function drop_fields(results) {
+ results.forEach(e => {
+ delete e.licence;
+ delete e.place_rank;
+ delete e.importance;
+ delete e.boundingbox;
+ });
+ return results
+}
+
+export function nominatim_get_data(id: string, bb: string[][] | null = null): Promise {
+ if (!id) return Promise.resolve([])
+
+ const url = new URL("https://nominatim.openstreetmap.org/search");
+ url.searchParams.append('format', 'jsonv2')
+ url.searchParams.append('q', id)
+ url.searchParams.append('limit', '20')
+ if (bb) {
+ url.searchParams.append('viewbox', `${bb[0][0]},${bb[0][1]},${bb[1][0]},${bb[1][1]}`)
+ url.searchParams.append('bounded', `1`)
+ }
+ return fetch(url).then((res) => {
+ if (!res.ok) throw new Error("Nominatim Error")
+ return res.json().then(r => drop_fields(r))
+ })
+}
diff --git a/src/server/main.ts b/src/server/main.ts
new file mode 100644
index 0000000..599b401
--- /dev/null
+++ b/src/server/main.ts
@@ -0,0 +1,36 @@
+import fastify from 'fastify'
+import fastify_static from '@fastify/static'
+import fastify_db from '@fastify/leveldb'
+import fastify_view from '@fastify/view';
+import pug from 'pug'
+import { join as pathJoin } from "path";
+
+import api from "./api"
+
+const server = fastify(); //{ logger: true });
+
+server.register(fastify_static, {
+ root: pathJoin(__dirname, "../public"),
+ prefix: "/public/",
+});
+
+server.register(
+ fastify_db as any,
+ { name: "db" }
+);
+
+server.register(fastify_view, {
+ engine: { pug: pug },
+});
+
+server.register(api, { prefix: "/api" });
+
+server.get("/", (req, reply) => reply.view("/src/template/home.pug"));
+server.get("/:id", (req, reply) => reply.view("/src/template/journey.pug"));
+server.get("/view/:id", (req, reply) => reply.view("/src/template/view.pug"));
+server.get("/short/:id", (req, reply) => reply.view("/src/template/short.pug"));
+
+server.listen({ port: 8080, host: "0.0.0.0" }, (err, address) => {
+ if (err) throw err;
+ console.log("Listening on", address);
+});
diff --git a/src/style/custom.css b/src/style/custom.css
new file mode 100644
index 0000000..e9b5b85
--- /dev/null
+++ b/src/style/custom.css
@@ -0,0 +1,207 @@
+.leaflet-popup-close-button {
+ visibility: hidden;
+}
+
+.p-abs {
+ position: absolute;
+}
+
+.list-group {
+ overflow: auto;
+ white-space: nowrap;
+ scrollbar-width: none;
+ padding: 1rem 0rem;
+}
+
+.list-group-item {
+ border: 1px solid var(--darkdark);
+ display: inline-block;
+ position: relative;
+ cursor: pointer;
+ text-align: center;
+ padding: 0.5rem 0.8rem;
+}
+
+.list-group-item:hover {
+ filter: brightness(85%);
+}
+
+.leaflet-control-attribution {
+ display: none;
+}
+
+.display-none {
+ display: none;
+}
+
+.col-0 {
+ flex: 0 0 0%;
+ max-width: 0%;
+ overflow: hidden;
+}
+
+.map-container,
+.drawer-container {
+ transition: flex 0.5s ease-in-out, max-width 0.5s ease-in-out;
+}
+
+.map-container {
+ min-width: 52px;
+ height: 100%;
+}
+
+.drawer-container {
+ height: 100%;
+ right: 0;
+ top: 0;
+ overflow: scroll;
+}
+
+.travel-path-icon {
+ margin-left: -12px;
+ margin-top: -32px;
+}
+
+
+.ml-auto {
+ margin-left: auto;
+}
+
+.mr-auto {
+ margin-right: auto;
+}
+
+.input .mx-input {
+ height: 40px;
+}
+
+.h-100 {
+ height: 100%;
+}
+
+.map-menu {
+ position: absolute;
+ display: flex;
+ z-index: 1100;
+ right: 0;
+ flex-direction: column;
+ gap: 10px;
+ margin: 5px;
+}
+
+.map-menu-top {
+ top: 0;
+}
+
+.map-menu-bottom {
+ bottom: 0;
+}
+
+.map-menu-center {
+ top: 50%;
+}
+
+.padding-1 {
+ padding: 5px;
+}
+
+.map-menu-item {
+ background-color: var(--darkdark);
+ padding: 5px;
+ border-radius: 50%;
+ cursor: pointer;
+}
+
+.map-menu-item:hover {
+ filter: brightness(150%);
+}
+
+.map-menu-sub {
+ display: flex;
+ flex-direction: row-reverse;
+ gap: 5px;
+}
+
+.map-menu-item,
+.map-menu-sub-item {
+ background-color: var(--darkdark);
+ padding: 5px;
+ border-radius: 50%;
+ cursor: pointer;
+ float: right;
+ width: 42px;
+ height: 42px;
+ align-content: center;
+ text-align: center;
+}
+
+.vue2leaflet-map {
+ border-radius: var(--border-radius);
+}
+
+.leaflet-popup-content {
+ margin: 10px 20px !important;
+ /* display: flex;
+ align-items: center;
+ justify-content: center;
+ width: auto !important;
+ max-width: 100%; */
+}
+
+
+.leaflet-popup>.leaflet-popup-content-wrapper {
+ border-radius: var(--border-radius);
+ width: 350px;
+}
+
+.leaflet-popup-button-group {
+ position: absolute;
+ top: 0;
+ right: 0;
+ display: flex;
+ flex-direction: row;
+ gap: 5px;
+ margin: 2px 6px;
+}
+
+.leaflet-popup-button-group>a {
+ cursor: pointer;
+}
+
+.leaflet-popup-button-group>a:hover {
+ cursor: pointer;
+ filter: brightness(150%);
+}
+
+.leaflet-popup-button-group>a>i {
+ font-size: 1em;
+}
+
+.query-result {
+ display: flex;
+ align-items: center;
+ border-radius: var(--border-radius);
+ cursor: pointer;
+}
+
+.query-result:hover {
+ filter: brightness(85%);
+}
+
+.scroll-content>div:first-child {
+ border-top-left-radius: var(--border-radius);
+ border-bottom-left-radius: var(--border-radius);
+}
+
+.scroll-content>div:nth-last-child(3) {
+ border-top-right-radius: var(--border-radius);
+ border-bottom-right-radius: var(--border-radius);
+}
+
+.scroll-content>div:last-child {
+ border-radius: var(--border-radius);
+}
+
+.mx-datepicker {
+ width: 100% !important;
+}
\ No newline at end of file
diff --git a/src/style/define.css b/src/style/define.css
new file mode 100644
index 0000000..e7bbaed
--- /dev/null
+++ b/src/style/define.css
@@ -0,0 +1,19 @@
+:root {
+ --black: #030B12;
+ --darkdark: #0C1D2E;
+ --dark: #203A53;
+ --lightdark: #425F7C;
+ --light: #93A9BE;
+ --lightlight: #B6C5D5;
+ --white: #F0F3F7;
+ --orange: ##F5B97D;
+ --yellow: #F5F57D;
+ --green: #B9F57D;
+ --turquoise: #7DF5B9;
+ --blue: #7DB9F5;
+ --purple: #B97DF5;
+ --pink: #F57DB9;
+ --red: #F57D7D;
+
+ --border-radius: 3px;
+}
\ No newline at end of file
diff --git a/src/style/index.css b/src/style/index.css
new file mode 100644
index 0000000..018e967
--- /dev/null
+++ b/src/style/index.css
@@ -0,0 +1,13 @@
+@import './define.css';
+
+@import './module/input.css';
+@import './module/load_n_spin.css';
+@import './module/typography.css';
+@import './module/layout.css';
+@import './module/general.css';
+
+@import './custom.css';
+
+[v-cloak] {
+ display: none;
+}
\ No newline at end of file
diff --git a/src/style/module/general.css b/src/style/module/general.css
new file mode 100644
index 0000000..88a2419
--- /dev/null
+++ b/src/style/module/general.css
@@ -0,0 +1,202 @@
+html,
+body,
+body,
+div,
+span,
+object,
+iframe,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+p,
+blockquote,
+pre,
+abbr,
+address,
+cite,
+code,
+del,
+dfn,
+em,
+ins,
+kbd,
+q,
+samp,
+small,
+strong,
+sub,
+sup,
+var,
+b,
+i,
+dl,
+dt,
+dd,
+ol,
+ul,
+li,
+fieldset,
+form,
+label,
+legend,
+table,
+caption,
+tbody,
+tfoot,
+thead,
+tr,
+th,
+td,
+article,
+aside,
+figure,
+footer,
+header,
+hgroup,
+menu,
+nav,
+section,
+time,
+mark,
+audio,
+video {
+ background: transparent;
+ border: 0;
+ font-size: 100%;
+ margin: 0;
+ outline: 0;
+ padding: 0;
+ vertical-align: baseline;
+}
+
+article,
+aside,
+figure,
+footer,
+header,
+main,
+nav,
+section {
+ display: block;
+}
+
+*,
+*:before,
+*:after {
+ box-sizing: border-box;
+}
+
+
+*,
+*::after,
+*::before {
+ box-sizing: border-box;
+ outline: none;
+}
+
+body {
+ background-color: #fff;
+ min-height: 100%;
+ overflow-x: hidden;
+ position: relative;
+}
+
+p {
+ font-weight: normal;
+ margin-bottom: 1.5em;
+}
+
+img {
+ max-width: 100%;
+}
+
+strong {
+ font-weight: 600;
+}
+
+ul {
+ margin-bottom: 1em;
+}
+
+li {
+ list-style: none;
+ margin-bottom: 0.5em;
+}
+
+/**
+ * BACKGROUND
+ */
+.bg-primary {
+ background-color: var(--blue);
+}
+
+.bg-dark {
+ background-color: var(--darkdark);
+}
+
+.bg-secondary {
+ background-color: var(--pink);
+}
+
+.bg-white {
+ background-color: var(--white);
+}
+
+.bg-success {
+ background-color: var(--green);
+}
+
+.bg-info {
+ background-color: var(--yellow);
+}
+
+.bg-warning {
+ background-color: var(--orange);
+}
+
+.bg-error {
+ background-color: var(--red);
+}
+
+.bg-gray {
+ background-color: var(--lightdark);
+}
+
+.bg-gray-light {
+ background-color: var(--lightlight);
+}
+
+
+.align {
+ align-items: center;
+ justify-content: center;
+}
+
+.fleft {
+ float: left;
+}
+
+.fright {
+ float: right;
+}
+
+.clearfix ::after {
+ clear: both;
+ content: "";
+ display: table;
+}
+
+.no-wrap {
+ white-space: nowrap;
+}
+
+.overflow-hidden {
+ overflow: hidden;
+}
+
+.rounded {
+ border-radius: var(--border-radius);
+}
\ No newline at end of file
diff --git a/src/style/module/input.css b/src/style/module/input.css
new file mode 100644
index 0000000..5ebba66
--- /dev/null
+++ b/src/style/module/input.css
@@ -0,0 +1,362 @@
+input,
+textarea {
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ resize: none;
+}
+
+label {
+ display: block;
+ font-weight: normal;
+}
+
+input:-webkit-autofill {
+ box-shadow: 0 0 0 1000px #eceff1 inset;
+}
+
+.textarea,
+.input,
+.select {
+ border: 1px solid var(--white);
+ border-radius: var(--border-radius);
+ box-shadow: none;
+ display: inline-block;
+ font-weight: normal;
+ overflow: hidden;
+}
+
+.textarea :focus,
+.input :focus,
+.select :focus {
+ outline: none;
+}
+
+.textarea.has-error,
+.input.has-error,
+.select.has-error {
+ background: #eceff1;
+ border: 1px solid #e74c3c;
+ margin-bottom: 0;
+}
+
+.select {
+ background-color: #eceff1;
+ display: inline-block;
+ margin-right: 16px;
+ position: relative;
+}
+
+.select:last-child {
+ margin-right: 0;
+}
+
+.select-fullWidth {
+ display: block;
+ margin-left: 0;
+ margin-right: 0;
+ width: 100%;
+}
+
+.select select {
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ background-color: transparent;
+ border: 0;
+ border-radius: 0;
+ color: #272727;
+ display: block;
+ font-size: 16px;
+ line-height: 1.5em;
+ margin: 0;
+ padding: 8px 16px;
+ padding-right: 30px;
+ transition: background-color 0.2s ease-in-out;
+ width: 100%;
+}
+
+.select select:active,
+.select select:focus {
+ background-color: #fbfbfc;
+ border: 0;
+ outline: none;
+}
+
+.select select::-ms-expand {
+ display: none;
+}
+
+.select::after,
+.select::before {
+ background: #03a9f4;
+ content: "";
+ display: block;
+ height: 2px;
+ margin-top: 2px;
+ position: absolute;
+ right: 5px;
+ top: 50%;
+ -webkit-transform-origin: 1px;
+ transform-origin: 1px;
+ width: 10px;
+}
+
+.select::after {
+ -webkit-transform: rotate(-135deg);
+ transform: rotate(-135deg);
+}
+
+.select::before {
+ -webkit-transform: rotate(-45deg);
+ transform: rotate(-45deg);
+}
+
+.textarea {
+ background-color: #eceff1;
+ padding: 0;
+}
+
+.textarea-fullWidth {
+ display: block;
+ margin-left: 0;
+ margin-right: 0;
+ width: 100%;
+}
+
+.textarea textarea {
+ background: transparent;
+ border: 0;
+ color: #272727;
+ display: block;
+ font-family: "Lato", sans-serif;
+ font-size: 16px;
+ line-height: 1.5em;
+ margin: 0;
+ min-height: 120px;
+ padding: 8px 16px;
+ transition: background-color 0.2s ease-in-out;
+ width: 100%;
+}
+
+.textarea textarea::-webkit-input-placeholder {
+ color: #969da6;
+}
+
+.textarea textarea::-ms-input-placeholder {
+ color: #969da6;
+}
+
+.textarea textarea::placeholder {
+ color: #969da6;
+}
+
+.textarea textarea:focus,
+.textarea textarea:active {
+ background-color: #fbfbfc;
+ border: 0;
+ outline: none;
+}
+
+.checkbox {
+ margin-bottom: 8px;
+ position: relative;
+}
+
+.checkbox input[type="checkbox"] {
+ display: none;
+}
+
+.checkbox input[type="checkbox"]:checked+label::after {
+ -webkit-animation: checkboxAndRadioAnimation 0.25s;
+ animation: checkboxAndRadioAnimation 0.25s;
+ content: "";
+ -webkit-transform: scale(1) rotate(45deg);
+ transform: scale(1) rotate(45deg);
+}
+
+.checkbox input[type="checkbox"]+label {
+ display: block;
+ overflow: hidden;
+ padding-left: 30px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.checkbox input[type="checkbox"]+label::before {
+ background-color: #eceff1;
+ border: 1px solid var(--white);
+ border-radius: var(--border-radius);
+ content: "";
+ display: inline-block;
+ height: 20px;
+ left: 0;
+ margin-top: -10px;
+ position: absolute;
+ top: 50%;
+ width: 20px;
+}
+
+.checkbox input[type="checkbox"]+label::after {
+ border-bottom: 3px solid #03a9f4;
+ border-right: 3px solid #03a9f4;
+ display: block;
+ height: 12px;
+ left: 11px;
+ margin-left: -4px;
+ margin-top: -7px;
+ position: absolute;
+ top: 50%;
+ width: 7px;
+ z-index: 1;
+}
+
+.radio {
+ margin-bottom: 8px;
+ position: relative;
+}
+
+.radio input[type="radio"] {
+ display: none;
+}
+
+.radio input[type="radio"]:checked+label::after {
+ -webkit-animation: checkboxAndRadioAnimation 0.25s;
+ animation: checkboxAndRadioAnimation 0.25s;
+ content: "";
+ -webkit-transform: scale(1) rotate(45deg);
+ transform: scale(1) rotate(45deg);
+}
+
+.radio input[type="radio"]+label {
+ display: block;
+ overflow: hidden;
+ padding-left: 30px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.radio input[type="radio"]+label::before {
+ background-color: #eceff1;
+ border: 1px solid var(--white);
+ border-radius: 20px;
+ content: "";
+ display: inline-block;
+ height: 20px;
+ left: 0;
+ margin-top: -10px;
+ position: absolute;
+ top: 50%;
+ width: 20px;
+}
+
+.radio input[type="radio"]+label::after {
+ background-color: #03a9f4;
+ border-radius: 20px;
+ display: block;
+ height: 10px;
+ left: 11px;
+ margin-left: -6px;
+ margin-top: -6px;
+ position: absolute;
+ top: 13px;
+ width: 10px;
+ z-index: 1;
+}
+
+@-webkit-keyframes checkboxAndRadioAnimation {
+ 0% {
+ -webkit-transform: scale(0) rotate(45deg);
+ transform: scale(0) rotate(45deg);
+ }
+
+ 50% {
+ -webkit-transform: scale(1.5) rotate(45deg);
+ transform: scale(1.5) rotate(45deg);
+ }
+
+ 100% {
+ -webkit-transform: scale(1) rotate(45deg);
+ transform: scale(1) rotate(45deg);
+ }
+}
+
+@keyframes checkboxAndRadioAnimation {
+ 0% {
+ -webkit-transform: scale(0) rotate(45deg);
+ transform: scale(0) rotate(45deg);
+ }
+
+ 50% {
+ -webkit-transform: scale(1.5) rotate(45deg);
+ transform: scale(1.5) rotate(45deg);
+ }
+
+ 100% {
+ -webkit-transform: scale(1) rotate(45deg);
+ transform: scale(1) rotate(45deg);
+ }
+}
+
+.input-invis {
+ background-color: transparent !important;
+ margin: auto !important;
+ border: 0 !important;
+}
+
+.input {
+ background-color: var(--white);
+ padding: 0;
+ position: relative;
+}
+
+.input :focus,
+.input :active {
+ background-color: var(--white);
+ border-radius: var(--border-radius);
+}
+
+.input input,
+.input textarea {
+ background: transparent;
+ border: 0;
+ box-shadow: none;
+ color: #272727;
+ font-size: 16px;
+ line-height: 1.5em;
+ margin: 0;
+ outline: none;
+ padding: 8px 16px;
+ width: 100%;
+}
+
+.input input::-webkit-input-placeholder {
+ color: #969da6;
+}
+
+.input input::-ms-input-placeholder {
+ color: #969da6;
+}
+
+.input input::placeholder {
+ color: #969da6;
+}
+
+.input input.small {
+ line-height: 1em;
+ padding: 0;
+}
+
+.input-withIcon input {
+ padding-right: 32px;
+}
+
+.input-icon {
+ fill: #969da6;
+ height: 16px;
+ margin-top: -8px;
+ position: absolute;
+ right: 16px;
+ top: 50%;
+ width: 16px;
+}
\ No newline at end of file
diff --git a/src/style/module/layout.css b/src/style/module/layout.css
new file mode 100644
index 0000000..58ec6d9
--- /dev/null
+++ b/src/style/module/layout.css
@@ -0,0 +1,354 @@
+/**
+ * LAYOUT
+ */
+.section {
+ padding-bottom: 36px;
+ padding-top: 36px;
+}
+
+@media (min-width: 768px) {
+ .section {
+ padding-bottom: 72px;
+ padding-top: 72px;
+ }
+}
+
+.section+.section {
+ padding-top: 0;
+}
+
+.container {
+ margin: 0 auto;
+ max-width: 1380px;
+ padding-left: 12px;
+ padding-right: 12px;
+ width: 100%;
+}
+
+@media (min-width: 576px) {
+ .container {
+ max-width: 540px;
+ }
+}
+
+@media (min-width: 992px) {
+ .container {
+ max-width: 960px;
+ }
+}
+
+@media (min-width: 1200px) {
+ .container {
+ max-width: 1140px;
+ }
+}
+
+@media (min-width: 768px) {
+ .container {
+ padding-left: 24px;
+ padding-right: 24px;
+ max-width: 720px;
+ }
+}
+
+.container-medium {
+ margin: 0 auto;
+ max-width: 944px;
+ padding-left: 12px;
+ padding-right: 12px;
+}
+
+@media (min-width: 768px) {
+ .container-medium {
+ padding-left: 24px;
+ padding-right: 24px;
+ }
+}
+
+.container {
+ width: 100%;
+ padding-right: 12px;
+ padding-left: 12px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.container-fluid {
+ width: 100%;
+ padding-right: 12px;
+ padding-left: 12px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.row {
+ display: flex;
+ flex-wrap: wrap;
+ margin-right: -12px;
+ margin-left: -12px;
+}
+
+.col-1,
+.col-2,
+.col-3,
+.col-4,
+.col-5,
+.col-6,
+.col-7,
+.col-8,
+.col-9,
+.col-10,
+.col-11,
+.col-12,
+.col,
+.col-auto,
+.col-sm-1,
+.col-sm-2,
+.col-sm-3,
+.col-sm-4,
+.col-sm-5,
+.col-sm-6,
+.col-sm-7,
+.col-sm-8,
+.col-sm-9,
+.col-sm-10,
+.col-sm-11,
+.col-sm-12,
+.col-md-1,
+.col-md-2,
+.col-md-3,
+.col-md-4,
+.col-md-5,
+.col-md-6,
+.col-md-7,
+.col-md-8,
+.col-md-9,
+.col-md-10,
+.col-md-11,
+.col-md-12 {
+ position: relative;
+ width: 100%;
+ min-height: 1px;
+ padding-right: 12px;
+ padding-left: 12px;
+}
+
+.col {
+ flex-basis: 0;
+ flex-grow: 1;
+ max-width: 100%;
+}
+
+.col-auto {
+ flex: 0 0 auto;
+ width: auto;
+ max-width: none;
+}
+
+.col-1 {
+ flex: 0 0 8.33333%;
+ max-width: 8.33333%;
+}
+
+.col-2 {
+ flex: 0 0 16.66667%;
+ max-width: 16.66667%;
+}
+
+.col-3 {
+ flex: 0 0 25%;
+ max-width: 25%;
+}
+
+.col-4 {
+ flex: 0 0 33.33333%;
+ max-width: 33.33333%;
+}
+
+.col-5 {
+ flex: 0 0 41.66667%;
+ max-width: 41.66667%;
+}
+
+.col-6 {
+ flex: 0 0 50%;
+ max-width: 50%;
+}
+
+.col-7 {
+ flex: 0 0 58.33333%;
+ max-width: 58.33333%;
+}
+
+.col-8 {
+ flex: 0 0 66.66667%;
+ max-width: 66.66667%;
+}
+
+.col-9 {
+ flex: 0 0 75%;
+ max-width: 75%;
+}
+
+.col-10 {
+ flex: 0 0 83.33333%;
+ max-width: 83.33333%;
+}
+
+.col-11 {
+ flex: 0 0 91.66667%;
+ max-width: 91.66667%;
+}
+
+.col-12 {
+ flex: 0 0 100%;
+ max-width: 100%;
+}
+
+@media (min-width: 576px) {
+ .col-sm {
+ flex-basis: 0;
+ flex-grow: 1;
+ max-width: 100%;
+ }
+
+ .col-sm-auto {
+ flex: 0 0 auto;
+ width: auto;
+ max-width: none;
+ }
+
+ .col-sm-1 {
+ flex: 0 0 8.33333%;
+ max-width: 8.33333%;
+ }
+
+ .col-sm-2 {
+ flex: 0 0 16.66667%;
+ max-width: 16.66667%;
+ }
+
+ .col-sm-3 {
+ flex: 0 0 25%;
+ max-width: 25%;
+ }
+
+ .col-sm-4 {
+ flex: 0 0 33.33333%;
+ max-width: 33.33333%;
+ }
+
+ .col-sm-5 {
+ flex: 0 0 41.66667%;
+ max-width: 41.66667%;
+ }
+
+ .col-sm-6 {
+ flex: 0 0 50%;
+ max-width: 50%;
+ }
+
+ .col-sm-7 {
+ flex: 0 0 58.33333%;
+ max-width: 58.33333%;
+ }
+
+ .col-sm-8 {
+ flex: 0 0 66.66667%;
+ max-width: 66.66667%;
+ }
+
+ .col-sm-9 {
+ flex: 0 0 75%;
+ max-width: 75%;
+ }
+
+ .col-sm-10 {
+ flex: 0 0 83.33333%;
+ max-width: 83.33333%;
+ }
+
+ .col-sm-11 {
+ flex: 0 0 91.66667%;
+ max-width: 91.66667%;
+ }
+
+ .col-sm-12 {
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+}
+
+@media (min-width: 768px) {
+ .col-md {
+ flex-basis: 0;
+ flex-grow: 1;
+ max-width: 100%;
+ }
+
+ .col-md-auto {
+ flex: 0 0 auto;
+ width: auto;
+ max-width: none;
+ }
+
+ .col-md-1 {
+ flex: 0 0 8.33333%;
+ max-width: 8.33333%;
+ }
+
+ .col-md-2 {
+ flex: 0 0 16.66667%;
+ max-width: 16.66667%;
+ }
+
+ .col-md-3 {
+ flex: 0 0 25%;
+ max-width: 25%;
+ }
+
+ .col-md-4 {
+ flex: 0 0 33.33333%;
+ max-width: 33.33333%;
+ }
+
+ .col-md-5 {
+ flex: 0 0 41.66667%;
+ max-width: 41.66667%;
+ }
+
+ .col-md-6 {
+ flex: 0 0 50%;
+ max-width: 50%;
+ }
+
+ .col-md-7 {
+ flex: 0 0 58.33333%;
+ max-width: 58.33333%;
+ }
+
+ .col-md-8 {
+ flex: 0 0 66.66667%;
+ max-width: 66.66667%;
+ }
+
+ .col-md-9 {
+ flex: 0 0 75%;
+ max-width: 75%;
+ }
+
+ .col-md-10 {
+ flex: 0 0 83.33333%;
+ max-width: 83.33333%;
+ }
+
+ .col-md-11 {
+ flex: 0 0 91.66667%;
+ max-width: 91.66667%;
+ }
+
+ .col-md-12 {
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+}
\ No newline at end of file
diff --git a/src/style/module/load_n_spin.css b/src/style/module/load_n_spin.css
new file mode 100644
index 0000000..17fe58c
--- /dev/null
+++ b/src/style/module/load_n_spin.css
@@ -0,0 +1,166 @@
+/**
+ * LOADING BAR
+ *
+ * Markup:
+ * ---------
+ *
+ *
+ */
+.loadingBar {
+ height: 6px;
+ left: 0;
+ overflow: hidden;
+ position: fixed;
+ right: 0;
+ top: 0;
+ width: 100%;
+ z-index: 1000;
+}
+
+.loadingBar::before {
+ -webkit-animation: loading 2s linear infinite;
+ animation: loading 2s linear infinite;
+ background-color: #03a9f4;
+ content: "";
+ display: block;
+ height: 6px;
+ left: -300px;
+ position: absolute;
+ width: 300px;
+}
+
+@-webkit-keyframes loading {
+ from {
+ left: -300px;
+ width: 30%;
+ }
+
+ 50% {
+ width: 30%;
+ }
+
+ 70% {
+ width: 70%;
+ }
+
+ 80% {
+ left: 50%;
+ }
+
+ 95% {
+ left: 120%;
+ }
+
+ to {
+ left: 100%;
+ }
+}
+
+@keyframes loading {
+ from {
+ left: -300px;
+ width: 30%;
+ }
+
+ 50% {
+ width: 30%;
+ }
+
+ 70% {
+ width: 70%;
+ }
+
+ 80% {
+ left: 50%;
+ }
+
+ 95% {
+ left: 120%;
+ }
+
+ to {
+ left: 100%;
+ }
+}
+
+.spinner {
+ position: absolute;
+ right: 0;
+ top: 0;
+ width: 40px;
+ height: 40px;
+ display: block
+}
+
+.spinner:after,
+.spinner:before {
+ position: absolute;
+ content: "";
+ top: 50%;
+ left: 50%;
+ margin: -12px 0 0 -12px;
+ width: 24px;
+ height: 24px;
+ border-radius: 100%;
+ border: 3px solid transparent;
+ border-top-color: var(--blue);
+}
+
+.spinner:before {
+ -webkit-animation: spinning 2.4s cubic-bezier(.41, .26, .2, .62);
+ animation: spinning 2.4s cubic-bezier(.41, .26, .2, .62);
+ -webkit-animation-iteration-count: infinite;
+ animation-iteration-count: infinite
+}
+
+.spinner:after {
+ -webkit-animation: spinning 2.4s cubic-bezier(.51, .09, .21, .8);
+ animation: spinning 2.4s cubic-bezier(.51, .09, .21, .8);
+ -webkit-animation-iteration-count: infinite;
+ animation-iteration-count: infinite
+}
+
+@-webkit-keyframes spinning {
+ 0% {
+ -webkit-transform: rotate(0);
+ transform: rotate(0);
+ }
+
+ 25% {
+ -webkit-transform: rotate(90deg);
+ transform: rotate(90deg);
+ }
+
+ 50% {
+ -webkit-transform: rotate(180deg);
+ transform: rotate(180deg);
+ }
+
+ 75% {
+ -webkit-transform: rotate(270deg);
+ transform: rotate(270deg);
+ }
+
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+
+@keyframes spinning {
+ 0% {
+ -webkit-transform: rotate(0);
+ transform: rotate(0);
+ }
+
+ 50% {
+ -webkit-transform: rotate(180deg);
+ transform: rotate(180deg);
+ }
+
+
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
\ No newline at end of file
diff --git a/src/style/module/typography.css b/src/style/module/typography.css
new file mode 100644
index 0000000..96a1fa2
--- /dev/null
+++ b/src/style/module/typography.css
@@ -0,0 +1,148 @@
+/**
+ * TYPOGRAPHY
+ */
+body {
+ color: #272727;
+ font-family: "Lato", sans-serif;
+ font-size: 16px;
+ font-weight: 400;
+ line-height: 1.5em;
+}
+
+a {
+ color: var(--blue);
+ text-decoration: none;
+}
+
+a:hover {
+ color: color-mix(in srgb, var(--color-primary), #FFF 15%);
+}
+
+a:focus {
+ color: var(--blue);
+}
+
+.text-huge,
+.text-big,
+.text-medium {
+ margin-bottom: 1em;
+}
+
+.text-huge {
+ font-size: 36px;
+ line-height: 1.3em;
+}
+
+.text-big {
+ font-size: 24px;
+ line-height: 1.3em;
+}
+
+.text-medium {
+ font-size: 16px;
+ line-height: 1.5em;
+}
+
+.text-small {
+ font-size: 12px;
+ line-height: 1.3em;
+}
+
+.text-body {
+ font-size: 16px;
+ line-height: 1.5em;
+}
+
+.text-primary {
+ color: #03a9f4;
+}
+
+.text-dark {
+ color: #18232f;
+}
+
+.text-secondary {
+ color: #e91e63;
+}
+
+.text-white {
+ color: #fff;
+}
+
+.text-success {
+ color: #4caf50;
+}
+
+.text-info {
+ color: #5bc0de;
+}
+
+.text-warning {
+ color: #f0ad4e;
+}
+
+.text-error {
+ color: #e74c3c;
+}
+
+.text-gray {
+ color: #969da6;
+}
+
+.text-gray-light {
+ color: #eceff1;
+}
+
+.text-light {
+ font-weight: 300;
+}
+
+.text-normal {
+ font-weight: 400;
+}
+
+.text-lineThrough {
+ text-decoration: line-through;
+}
+
+.text-italic {
+ font-style: italic;
+}
+
+.text-underline {
+ text-decoration: underline;
+}
+
+.text-uppercase {
+ text-transform: uppercase;
+}
+
+.text-withSubtitle {
+ margin-bottom: 0 !important;
+}
+
+.text-withSubtitle+.text-huge,
+.text-withSubtitle+.text-big,
+.text-withSubtitle+.text-medium,
+.text-withSubtitle+.text-small {
+ margin-top: 0.5em;
+}
+
+h1,
+h2,
+h3,
+h4 {
+ font-weight: 300;
+}
+
+.text-center {
+ text-align: center;
+}
+
+.text-right {
+ text-align: right;
+}
+
+.text-left {
+ text-align: left;
+}
\ No newline at end of file
diff --git a/src/template/home.pug b/src/template/home.pug
new file mode 100644
index 0000000..2a3d520
--- /dev/null
+++ b/src/template/home.pug
@@ -0,0 +1,32 @@
+doctype html
+
+include module/head.pug
+main#app
+ .bg-white.section.text-dark
+ .container.mb-big
+ .text-center
+ div
+ h1.text-huge.text-withSubtitle Open Tourism Map
+ h2.text-big.text-gray Collaborative Holiday Planner
+ .spacer
+ .section.bg-dark.text-white
+ .container-medium
+ .row
+ .col-8
+ h2.text-big Your journey
+ p.text-gray
+ | Browse hotels, restaurants and attractions,....
+ br
+ |
+ | Select and plan the varying elements of your journey
+ .col-4
+ .row.align
+ .input
+ input#journey.id(v-model="journey.id", placeholder="ID", type="text")
+ p
+ .row.align
+ button.button.button--primary.button--mobileFull(
+ v-on:click="start_journey"
+ ) Start the journey
+
+include module/foot.pug
diff --git a/src/template/journey.pug b/src/template/journey.pug
new file mode 100644
index 0000000..3b3e81b
--- /dev/null
+++ b/src/template/journey.pug
@@ -0,0 +1,7 @@
+doctype html
+
+include module/head.pug
+
+main#app(v-cloak)
+ include module/journey/main.pug
+include module/foot.pug
diff --git a/src/template/module/foot.pug b/src/template/module/foot.pug
new file mode 100644
index 0000000..6ea8792
--- /dev/null
+++ b/src/template/module/foot.pug
@@ -0,0 +1,13 @@
+script(src="https://unpkg.com/leaflet")
+
+script(src="https://unpkg.com/vue@2")
+script(src="https://unpkg.com/vue2-datepicker")
+script(src="https://unpkg.com/vue2-leaflet")
+script(src="https://unpkg.com/sortablejs")
+script(src="https://unpkg.com/vuedraggable")
+script(src="/public/main.js", type="text/javascript", charset="utf-8")
+footer.bg-dark.section
+ .container.text-center.text-small.text-white
+ | Built with ❤ by Helcel
+ br
+ span.text-small.text-gray v0.0.1
diff --git a/template/module/head.pug b/src/template/module/head.pug
similarity index 67%
rename from template/module/head.pug
rename to src/template/module/head.pug
index 6252ff0..3274cfa 100644
--- a/template/module/head.pug
+++ b/src/template/module/head.pug
@@ -8,18 +8,10 @@ head
href="https://fonts.googleapis.com/css?family=IBM+Plex+Sans:400,700,300",
type="text/css"
)
- link(rel="stylesheet", href="/public/css/index.css")
+ link(rel="stylesheet", href="/public/index.css")
link(rel="stylesheet", href="https://unpkg.com/vue2-datepicker/index.css")
- link(
- rel="stylesheet",
- href="https://unpkg.com/vue-multiselect@2/dist/vue-multiselect.min.css"
- )
link(rel="stylesheet", href="https://unpkg.com/leaflet/dist/leaflet.css")
- link(
- rel="stylesheet",
- href="https://unpkg.com/leaflet.awesome-markers/dist/leaflet.awesome-markers.css"
- )
link(
rel="stylesheet",
href="https://unpkg.com/@fortawesome/fontawesome-free/css/all.min.css"
diff --git a/template/module/importexport.pug b/src/template/module/journey/impexp.pug
similarity index 93%
rename from template/module/importexport.pug
rename to src/template/module/journey/impexp.pug
index b66f61e..6e0c3a5 100644
--- a/template/module/importexport.pug
+++ b/src/template/module/journey/impexp.pug
@@ -1,4 +1,4 @@
-div
+.impexp
.container-medium.section
.aligner
.input.col-sm-4
@@ -10,4 +10,4 @@ div
.col-sm-2
button.button.button--primary.button--mobileFull(
v-on:click="export_data"
- ) Export
+ ) Export
\ No newline at end of file
diff --git a/src/template/module/journey/leg/drawer-notes.pug b/src/template/module/journey/leg/drawer-notes.pug
new file mode 100644
index 0000000..0f4a1c7
--- /dev/null
+++ b/src/template/module/journey/leg/drawer-notes.pug
@@ -0,0 +1,8 @@
+.col-12.input.text-dark()
+ textarea.text-small#query_note(
+ v-model="journey.leg_get().notes"
+ @input="refreshTextAreaHeight"
+ @focus="refreshTextAreaHeight"
+ rows="1"
+ placeholder="...",
+ )
\ No newline at end of file
diff --git a/src/template/module/journey/leg/drawer.pug b/src/template/module/journey/leg/drawer.pug
new file mode 100644
index 0000000..cc30f3e
--- /dev/null
+++ b/src/template/module/journey/leg/drawer.pug
@@ -0,0 +1,48 @@
+
+.col-12.input.text-dark
+ input#query_input(
+ type="search"
+ @input="search_active"
+ @focus="search_active"
+ placeholder="Search ... "
+ style="width:85%;"
+ :disabled="query.note"
+ )
+ .spinner(v-if="query.load")
+
+div(v-if="['hotel', 'restaurant', 'place','other', 'travel'].indexOf(query.type)>=0")
+ template(v-for="(item, idx) in query.res" )
+ .query-result.col-12.bg-white.text-dark(
+ :key="'q'+idx"
+ @mouseover="drawer_hover_item(item)"
+ @mouseleave="drawer_hover_item()"
+ @click="drawer_click_item(item)" )
+ div( v-html="generate_icon(item, 'var(--dark)')")
+ .col-10()
+ | {{ item.name }}
+ .bg-dark.divider(
+ :key="'qdiv'+idx" style="height:1px" )
+ .query-result.col-12.bg-white.text-dark(
+ v-if="query.load==false && query.res.length==0" )
+ div( v-html="generate_icon('star', 'var(--dark)')")
+ .col-10()
+ | Add custom
+
+ .col-12.text-white.text-center(
+ ) {{query.load? `Loading ...` : `Found ${query.res.length} results`}}
+div(v-else-if="['flight'].indexOf(query.type)>=0")
+ template(v-for="(item, idx) in query.res" )
+ .query-result.col-12.bg-white.text-dark(
+ :key="'q'+idx"
+ @mouseover="drawer_hover_item(item)"
+ @mouseleave="drawer_hover_item()"
+ @click="drawer_click_item(item)" )
+ div( v-html="generate_icon('plane', 'var(--dark)')")
+ .col-10()
+ | {{ item.from }} => {{item.to}}
+ bg-dark.divider(
+ :key="'qdiv'+idx" style="height:1px" )
+div(v-else)
+ template()
+ .query-result.col-12.bg-white.text-dark()
+ | Unsuppored Query type {{query.type}}
diff --git a/src/template/module/journey/leg/nav.pug b/src/template/module/journey/leg/nav.pug
new file mode 100644
index 0000000..736c95e
--- /dev/null
+++ b/src/template/module/journey/leg/nav.pug
@@ -0,0 +1,34 @@
+.scroll-handler.row(
+ @mouseleave="nav_mouseleave"
+ @mousemove="nav_mousemove")
+
+ .col-3.col-sm-2.col-md-1
+ .list-group.text-dark.h-100
+ .fleft.list-group-item.bg-white.text-small.rounded.h-100(v-on:click.prevent="journey.leg_prev()")
+ i.fas.fa-angle-left
+ .col-6.col-sm-8.col-md-10
+ draggable.scroll-content.list-group.bg-dark(
+ tag="div",
+ :list="journey.data.main",
+ handle=".handle"
+ )
+ .list-group-item.handle.text-dark(
+ v-for="(element, idx) in journey.data.main",
+ :key="idx",
+ @click="journey.leg_sel(idx)",
+ :class="journey.sel_leg == idx ? 'bg-primary' : 'bg-white'"
+ )
+ .text {{ element.title || "Leg "+idx}}
+ i.fa.fa-times.close.fright(
+ style="top: 2px; right: 2px; position: absolute",
+ @click="journey.rm_leg(idx)"
+ )
+ .list-group-item.bg-dark
+ .list-group-item.bg-white.text-dark(@click="journey.add_leg()")
+ div
+ i.fa.fa-plus.add()
+
+ .col-3.col-sm-2.col-md-1
+ .list-group.text-dark.h-100
+ a.fright.list-group-item.bg-white.text-small.rounded.h-100(v-on:click.prevent="journey.leg_next()")
+ i.fas.fa-angle-right
\ No newline at end of file
diff --git a/src/template/module/journey/leg/top.pug b/src/template/module/journey/leg/top.pug
new file mode 100644
index 0000000..bd63bec
--- /dev/null
+++ b/src/template/module/journey/leg/top.pug
@@ -0,0 +1,36 @@
+.row.text-center.align
+ .col-5.col-sm-4.col-md-2
+ .input
+ input(
+ placeholder="Leg"
+ v-model="journey.leg_get().title")
+
+ .col-5.col-sm-4.col-md-2.mr-auto
+ .input
+ input(
+ placeholder="Day"
+ v-model="journey.leg_get().day_title[journey.sel_day]"
+ )
+ .col-8.col-sm-6.col-md-4
+ .input
+ //- label Date Range ({{ journey.leg_len() }})
+ date-picker(
+ :lang="lang",
+ v-model="journey.leg_get().date_range",
+ range="",
+ format="ddd D MMM",
+ placeholder="Date Range",
+ v-on:change="journey.date_update(journey.sel_leg)"
+ )
+ .col-4.col-sm-4.col-md-3.ml-auto
+ .input
+ input(
+ disabled="",
+ :value="journey.date_sel() + ' (' + journey.sel_day + ')'"
+ )
+ //- .col-6.list-group-item.align.center.bg-white(style="padding: 0.5rem 0;")
+ //- i.fas.fa-angle-double-right(v-on:click.prevent="journey.day_next()")
+ .col-sm-1.text-small
+
+ //- a(href="#prev", v-on:click.prevent="journey.day_prev()")
+ i.fas.fa-angle-left
\ No newline at end of file
diff --git a/src/template/module/journey/main.pug b/src/template/module/journey/main.pug
new file mode 100644
index 0000000..fb8d244
--- /dev/null
+++ b/src/template/module/journey/main.pug
@@ -0,0 +1,31 @@
+.row.fleft(style="position:absolute;right:0;")
+ .col-1
+ a(:href="'/short/' + journey.id")
+ i.fas.fa-file-contract
+ .col-1
+ a(:href="'/view/' + journey.id")
+ i.fas.fa-camera
+ .col-1
+ a(href="#", v-on:click.prevent="first_step")
+ i.fas.fa-tools
+
+
+.bg-dark.text-white(v-if="journey && journey.leg_get()")
+ .container
+ .row.align(style="padding-top:45px;")
+ .col-6.col-sm-4.col-md-3.input.text-big
+ input.text-center(v-model="journey.data.name" placeholder="My Journey" type="text")
+ //- input.small(type="text", :placeholder="journey.date_tot() + ' (' + journey.tot_len() + ')'" )
+
+ include leg/nav.pug
+ include leg/top.pug
+ .row(style="aspect-ratio:1.25;")
+ .map-container(:class=" { 'col-2 col-sm-5 col-md-8': query.type, 'col-2 col-sm-5 col-md-6': query.note , 'col-12': (!query.type && !query.note) }" )
+ include map.pug
+ .row.drawer-container(:class="{ 'col-10 col-sm-7 col-md-4': query.type, 'col-10 col-sm-7 col-md-6': query.note, 'col-0': (!query.type && !query.note) }")
+ .drawer-container(:class="{ 'col-12 ': query.type, 'col-0': !query.type }")
+ include leg/drawer.pug
+ .drawer-container(:class="{ 'col-12': query.note, 'col-0': !query.note }")
+ include leg/drawer-notes.pug
+
+//- include impexp.pug
\ No newline at end of file
diff --git a/src/template/module/journey/map.pug b/src/template/module/journey/map.pug
new file mode 100644
index 0000000..5087f8a
--- /dev/null
+++ b/src/template/module/journey/map.pug
@@ -0,0 +1,21 @@
+l-map(
+ :zoom.sync="journey.leg_get().map.zoom",
+ :center.sync="journey.leg_get().map.center",
+ style="height:100%"
+ no-blocking-animations=true
+ ref="map"
+)
+ l-tile-layer(
+ url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
+ attribution="© OpenStreetMap contributors"
+ )
+ l-control-scale(position="bottomleft", :imperial="false", :metric="true")
+ include map/override.pug
+
+ include map/hotel.pug
+ include map/activities.pug
+ include map/restaurants.pug
+
+ include map/travel.pug
+ template(v-if="edit_active")
+ include map/right_menu.pug
\ No newline at end of file
diff --git a/src/template/module/journey/map/activities.pug b/src/template/module/journey/map/activities.pug
new file mode 100644
index 0000000..a7bb1c5
--- /dev/null
+++ b/src/template/module/journey/map/activities.pug
@@ -0,0 +1,6 @@
+include mixin-marker.pug
+div(
+ v-for="(place, index) in journey.leg_get().places.activities",
+ :key="'activities'+index",
+ )
+ +map_marker("activities", "var(--lightdark)", "var(--light)", "var(--lightlight)")
\ No newline at end of file
diff --git a/src/template/module/journey/map/hotel.pug b/src/template/module/journey/map/hotel.pug
new file mode 100644
index 0000000..42947f7
--- /dev/null
+++ b/src/template/module/journey/map/hotel.pug
@@ -0,0 +1,7 @@
+include mixin-marker.pug
+div(
+ v-if="journey.leg_get().hotel",
+ v-for="(place, index) in [journey.leg_get().hotel]",
+ :key="'hotel'+index",
+ )
+ +map_marker("hotel", "var(--darkdark)", "var(--darkdark)", "var(--darkdark)")
\ No newline at end of file
diff --git a/src/template/module/journey/map/mixin-marker.pug b/src/template/module/journey/map/mixin-marker.pug
new file mode 100644
index 0000000..9dc67e4
--- /dev/null
+++ b/src/template/module/journey/map/mixin-marker.pug
@@ -0,0 +1,37 @@
+
+mixin map_marker(place, color_sel_c, color_sel_o, color_else)
+ l-marker(
+ :lat-lng="place.latlon"
+ )
+ l-icon(
+ v-if="(place.step == journey.sel_day)"
+ v-html="generate_marker(place, \""+color_sel_c+"\")"
+ )
+ l-icon(
+ v-else-if="(place.step >=0)"
+ v-html="generate_marker(place, \""+color_sel_o+"\")"
+ )
+ l-icon(
+ v-else
+ v-html="generate_marker(place, \""+color_else+"\")"
+ )
+ l-popup(
+ :options="{maxWidth:400, minWidth:300}")
+ h1.row.text-medium.text-center {{ place.sname }}
+ span.row.text-small.text-gray {{ place.display_name }}
+ span(v-if="edit_active")
+ .row.input()
+ textarea.col-12.col-sm-12.text-small(
+ placeholder="",
+ v-model="place.notes",
+ )
+ .leaflet-popup-button-group(v-if="edit_active")
+ a.text-gray(
+ v-on:click.prevent="place.step = ((place.step==journey.sel_day)?-1:journey.sel_day)"
+ v-html="generate_icon(((place.step==journey.sel_day)?'calendar-xmark':'calendar-plus'), 'NA')"
+ )
+ a.text-gray(
+ v-on:click.prevent="place_delete(\""+place+"\",index)"
+ v-html="generate_icon('trash', 'NA')"
+ )
+ span.row.text-small.text-dark(v-else) {{ place.notes }}
diff --git a/src/template/module/journey/map/override.pug b/src/template/module/journey/map/override.pug
new file mode 100644
index 0000000..5fe6755
--- /dev/null
+++ b/src/template/module/journey/map/override.pug
@@ -0,0 +1,11 @@
+l-marker(
+ v-if="map_override.active",
+ v-for="(el, idx) in map_override.elements"
+ key="'ovr'+idx"
+ :lat-lng="el"
+ )
+ l-icon(v-html="generate_marker('plus', 'darkgreen')")
+l-polyline(
+ v-if="map_override.active && map_override.elements.length>1"
+ :lat-lngs="map_override.elements" :color="'darkgreen'"
+)
\ No newline at end of file
diff --git a/src/template/module/journey/map/restaurants.pug b/src/template/module/journey/map/restaurants.pug
new file mode 100644
index 0000000..1685cd8
--- /dev/null
+++ b/src/template/module/journey/map/restaurants.pug
@@ -0,0 +1,6 @@
+include mixin-marker.pug
+div(
+ v-for="(place, index) in journey.leg_get().places.restaurants",
+ :key="'restaurants'+index",
+ )
+ +map_marker("restaurants", "var(--dark)", "var(--dark)", "var(--dark)")
\ No newline at end of file
diff --git a/src/template/module/journey/map/right_menu.pug b/src/template/module/journey/map/right_menu.pug
new file mode 100644
index 0000000..bd5990a
--- /dev/null
+++ b/src/template/module/journey/map/right_menu.pug
@@ -0,0 +1,21 @@
+.map-menu.map-menu-top
+ div(v-if="query.type" @click="drawer_click_item()" )
+ .map-menu-item(v-html="generate_icon('close')")
+ div(v-if="!query.type" @click="search_enable('hotel')")
+ .map-menu-item( v-html="generate_icon('bed')")
+ div(v-if="!query.type" @click="search_enable('restaurant')")
+ .map-menu-item( v-html="generate_icon('utensils')")
+ div(v-if="!query.type" @click="search_enable('place')")
+ .map-menu-item( v-html="generate_icon('star')")
+ .map-menu-sub(v-if="!query.type" @mouseenter="query.sub=true" @mouseleave="query.sub=false" )
+ .map-menu-item(v-html="generate_icon('route')")
+ .map-menu-item(v-if="query.sub" @click="search_enable('flight')" v-html="generate_icon('plane')")
+ .map-menu-item(v-if="query.sub" @click="search_enable('train')" v-html="generate_icon('train')")
+ .map-menu-item(v-if="query.sub" @click="search_enable('car')" v-html="generate_icon('car')")
+ .map-menu-item(v-if="query.sub" @click="search_enable('other')" v-html="generate_icon('person-biking')")
+
+.map-menu.map-menu-center
+ div(v-if="query.note" @click="drawer_click_item()" )
+ .map-menu-item(v-html="generate_icon('close')")
+ div(v-if="!query.note" @click="search_enable('notes')")
+ .map-menu-item( v-html="generate_icon('pencil')")
\ No newline at end of file
diff --git a/src/template/module/journey/map/travel.pug b/src/template/module/journey/map/travel.pug
new file mode 100644
index 0000000..d7e0799
--- /dev/null
+++ b/src/template/module/journey/map/travel.pug
@@ -0,0 +1,32 @@
+mixin flight_popup()
+ l-popup(
+ :options="{maxWidth:400, minWidth:300}"
+ )
+ h1.row.text-medium.text-center.text-uppercase {{ travel.id }}
+ span.row.text-small.text-gray {{ travel.from }} - {{travel.to}}
+ span(v-if="edit_active")
+ .row.input(style="margin-bottom:0")
+ textarea.col-12.col-sm-12.text-small(
+ placeholder="",
+ v-model="travel.notes",
+ )
+ span.row.text-small.text-dark(v-else) {{ travel.notes }}
+ span(v-if="edit_active")
+ .leaflet-popup-button-group(v-if="edit_active")
+ a.text-gray(
+ v-on:click.prevent="place_delete('flight',idx)"
+ v-html="generate_icon('trash', 'NA')"
+ )
+
+div(v-for= "(travel, idx) in journey.leg_get().travel")
+ l-polyline(:lat-lngs="travel.path" :color="travel.color || 'gray'")
+ +flight_popup()
+
+ l-marker(
+ v-for="(place, index) in travel.path"
+ :key="'plane'+index"
+ :lat-lng="place"
+ )
+ l-icon(v-html="generate_icon('plane', travel.color || 'gray', generate_rotation(index,travel.path), 'travel-path-icon')"
+ )
+ +flight_popup()
diff --git a/src/template/module/view/nav.pug b/src/template/module/view/nav.pug
new file mode 100644
index 0000000..fa9eb3b
--- /dev/null
+++ b/src/template/module/view/nav.pug
@@ -0,0 +1,10 @@
+.row.fleft(style="position:absolute;right:0;")
+ .col-1
+ a(:href="'/short/' + journey.id")
+ i.fas.fa-file-contract
+ .col-1
+ a(:href="'/view/' + journey.id")
+ i.fas.fa-camera
+ //- .col-1
+ //- a(:href="'/' + journey.id" v-on:click.prevent="first_step")
+ //- i.fas.fa-tools
diff --git a/src/template/module/view/short_leg.pug b/src/template/module/view/short_leg.pug
new file mode 100644
index 0000000..756cabb
--- /dev/null
+++ b/src/template/module/view/short_leg.pug
@@ -0,0 +1,39 @@
+.col-11.container.section
+ .row.text-center.align.padding-1
+ .input.col-5.col-sm-2
+ input(disabled="", placeholder="Unnamed" :value="item.title")
+ .col-sm-1
+ .input.col-6.col-sm-4
+ input(
+ disabled="",
+ placeholder="No Dates",
+ :value="item.date_range ? item.date_range[0].toLocal() + ' - ' + item.date_range[1].toLocal() : ''"
+ )
+ .col-1.col-sm-2
+ .input.col-5.col-sm-3.text-dark
+ .text(disabled="" placeholder="No Hotel" :value="item.hotel?item.hotel.sname:''") {{item.hotel?item.hotel.sname:'No Hotel'}}
+ //- .row.text-center
+ .input.col-sm-3(v-if="item.travel")
+ div(v-for="(item, idx) in item.travel")
+ input(disabled="", placeholder="-" :value="item.map((v) => v.id).join(', ')")
+ .row.align.padding-1
+ .input.col-sm-10.text-dark
+ .text-small(
+ placeholder="No Restaurants",
+ :value="item.places.restaurants.map((v) => v.sname + (v.notes ? '(' + v.notes + ')' : '')).join(', ') || 'No Restaurants'",
+ disabled=""
+ ) {{item.places.restaurants.map((v) => v.sname + (v.notes ? '(' + v.notes + ')' : '')).join(', ') || 'No Restaurants'}}
+ .row.align.padding-1
+ .input.col-sm-10.text-dark
+ .text-small(
+ placeholder="No Activities",
+ :value="item.places.activities.map((v) => v.sname + (v.notes ? '(' + v.notes + ')' : '')).join(', ') || 'No Activites'",
+ disabled=""
+ ) {{item.places.activities.map((v) => v.sname + (v.notes ? '(' + v.notes + ')' : '')).join(', ') || 'No Activites'}}
+ .row.align.padding-1
+ .input.col-sm-10.text-dark
+ .text-small(
+ placeholder="No Notes",
+ :value="item.notes || 'No Notes'",
+ disabled=""
+ ) {{item.notes || 'No Notes'}}
diff --git a/src/template/module/view/view_day.pug b/src/template/module/view/view_day.pug
new file mode 100644
index 0000000..a90fcd6
--- /dev/null
+++ b/src/template/module/view/view_day.pug
@@ -0,0 +1,19 @@
+div(v-for="(e, idx) in journey.data.main", :key="idx")
+ .bg-dark.text-white(v-if="journey.sel_leg == idx")
+ .container.section
+ .row
+ .col-3.fleft.text-center.text-white.text-huge
+ a(v-on:click.prevent="journey.day_prev()")
+ i.fas.fa-angle-left
+ .col-6.container.text-center.align
+ span.small {{ journey.data.main[idx].title }} {{ journey.sel_day }}
+ .text-big.text-gray {{ journey.data.main[idx].day_title[journey.sel_day] }}
+ .col-3.fright.text-center.text-white.text-huge
+ a(v-on:click.prevent="journey.day_next()")
+ i.fas.fa-angle-right
+ .row
+ .col-12.col-sm-12(style="aspect-ratio:1.25;")
+ include ../journey/map.pug
+ .row
+ .col-10
+ span.small.text-gray {{journey.data.main[idx].note || '...'}}
diff --git a/src/template/short.pug b/src/template/short.pug
new file mode 100644
index 0000000..61662f6
--- /dev/null
+++ b/src/template/short.pug
@@ -0,0 +1,16 @@
+doctype html
+include module/head.pug
+main#app(v-cloak)
+ include module/view/nav.pug
+
+ .bg-dark.text-white(v-if="journey && journey.leg_get()")
+ .container
+ .row.align(style="padding-top:45px;")
+ .col-7.col-sm-5.col-md-4.input.text-big
+ input.text-center(v-model="journey.data.name" placeholder="My Journey" type="text" disabled)
+ div(
+ v-for="(item, idx) in journey.data.main",
+ :class="idx % 2 === 0 ? 'bg-white text-dark' : 'bg-dark text-white'"
+ )
+ include module/view/short_leg.pug
+include module/foot.pug
diff --git a/src/template/view.pug b/src/template/view.pug
new file mode 100644
index 0000000..ff1fff4
--- /dev/null
+++ b/src/template/view.pug
@@ -0,0 +1,6 @@
+doctype html
+include module/head.pug
+main#app(v-cloak)
+ div(v-if="journey.leg_get()")
+ include module/view/view_day.pug
+include module/foot.pug
diff --git a/template/home.pug b/template/home.pug
deleted file mode 100644
index 41087ea..0000000
--- a/template/home.pug
+++ /dev/null
@@ -1,77 +0,0 @@
-doctype html
-
-include module/head.pug
-main#app
- .container
- section.mb-big
- .text-center
- img.main-logo.mb-medium(
- src="/public/img/helcel.png",
- alt="Helcel logo"
- )
- div
- h1.text-huge.text-withSubtitle Open Tourism Map
- h2.text-big.text-gray Collaborative Holiday Planner
- p#js-header-waypoint.m-none
- a.button.button--primary.button--mobileFull(href="#go") Get started
- .bg-dark
- .container
- .row.text-center
- .col-12.col-sm-3
- .section
- img(
- src="/public/img/lightweight.png",
- alt="Lightweight",
- width="118"
- )
- br
- h2.text-withSubtitle.text-big.text-white
- | Lightweight
- br
- span.text-medium.text-gray
- | Powered By
- br
- | Fastify & Sierra
- .col-12.col-sm-4
- .section
- img(
- src="/public/img/customizable.png",
- alt="Customizable",
- width="118"
- )
- br
- h2.text-withSubtitle.text-big.text-white
- | Customizable
- br
- span.text-medium.text-gray
- | Many Templates
- br
- | to choose from
- .col-12.col-sm-4
- .section
- h2.text-withSubtitle.text-big.text-white
- img(
- src="/public/img/opensource.png",
- alt="Open Source",
- width="118"
- )
- br
- |
- | FOSS
- br
- span.text-medium.text-gray :-)
- #go.container-medium.section
- h2.text-big Your journey
- p
- | Browse hotels, restaurants and attractions,....
- br
- |
- | Select and plan the varying elements of your journey
- .aligner.aligner--contentEnd
- .input
- input#journey_id(v-model="journey_id", placeholder="ID", type="text")
- button.button.button--primary.button--mobileFull(
- v-on:click="start_journey"
- ) Start the journey
-
-include module/foot.pug
diff --git a/template/journey.pug b/template/journey.pug
deleted file mode 100644
index 3d9a79c..0000000
--- a/template/journey.pug
+++ /dev/null
@@ -1,10 +0,0 @@
-doctype html
-
-include module/head.pug
-
-main#app(v-cloak)
- include module/nav.pug
- include module/journey_sec.pug
- include module/journey_step.pug
- include module/importexport.pug
-include module/foot.pug
diff --git a/template/module/foot.pug b/template/module/foot.pug
deleted file mode 100644
index 9c2437c..0000000
--- a/template/module/foot.pug
+++ /dev/null
@@ -1,26 +0,0 @@
-script(src="https://unpkg.com/leaflet")
-script(src="https://unpkg.com/leaflet.awesome-markers")
-//- script(src="https://unpkg.com/axios")
-script(src="https://unpkg.com/lodash")
-script(src="https://unpkg.com/sortablejs")
-
-script(src="https://unpkg.com/vue@2")
-script(src="https://unpkg.com/vue2-datepicker")
-script(src="https://unpkg.com/vue-textarea-autosize")
-script(src="https://unpkg.com/vue-multiselect@2")
-script(src="https://unpkg.com/vue2-leaflet")
-script(src="https://unpkg.com/vuedraggable")
-script(src="/public/main.js", type="text/javascript", charset="utf-8")
-footer.bg-dark
- .container
- .section.text-center.text-small
- p.text-white
- img(src="/public/img/helcel.png", alt="helcel logo", width="100")
- br
- br
- |
- | Built with ❤ by Helcel
- br
- span.text-small.text-gray v0.0.1
- p.text-gray
- a(href="https://git.helcel.net") Helcel Git
diff --git a/template/module/journey_sec.pug b/template/module/journey_sec.pug
deleted file mode 100644
index 4b5640e..0000000
--- a/template/module/journey_sec.pug
+++ /dev/null
@@ -1,20 +0,0 @@
-draggable.list-group.bg-dark(
- tag="div",
- :list="journey_data.main",
- handle=".handle"
-)
- .list-group-item.handle(
- v-for="(element, idx) in journey_data.main",
- :key="idx",
- @click="sel_section(idx)",
- :class="journey_step_data.section == idx ? 'bg-primary' : 'bg-white'"
- )
- .text {{ element.title }}
- i.fa.fa-times.close.fright(
- style="top: 2px; right: 2px; position: absolute",
- @click="rm_section(idx)"
- )
-
- .list-group-item.bg-white(@click="add_section()")
- .text Add Section
- i.fa.fa-plus.add(style="top: 12px; right: 5px; position: absolute")
diff --git a/template/module/journey_step.pug b/template/module/journey_step.pug
deleted file mode 100644
index 10281da..0000000
--- a/template/module/journey_step.pug
+++ /dev/null
@@ -1,102 +0,0 @@
-div(v-for="(e, idx) in journey_data.main", :key="idx")
- .bg-dark.text-white(v-if="journey_step_data.section == idx")
- .container.section
- .row.text-center
- .input.col-sm-2
- input(v-model="journey_data.main[idx].title")
- .input.col-sm-2
- input(
- placeholder="Day title",
- v-model="journey_data.main[idx].step_title[journey_step_data.day]"
- )
- .col-sm-3
- .right.input.col-sm-2
- input(
- disabled="",
- :value="active_date() + ' (' + journey_step_data.day + ')'"
- )
- .row
- .col-9.col-ssm-12
- include map.pug
- .col-3.col-ssm-12
- .row.text-center
- div
- label Date Range ({{ step_len(idx) }})
- .input.text-dark
- date-picker(
- :lang="lang",
- v-model="journey_data.main[idx].dateRange",
- range="",
- format="ddd D MMM",
- placeholder="Date Range",
- v-on:change="update_date(idx)"
- )
- .row.text-center
- div
- label Hotel
- multiselect#ajax(
- v-model="journey_data.main[idx].hotel",
- label="sname",
- track-by="place_id",
- placeholder="Type to search",
- open-direction="bottom",
- :options="query.nominatim",
- :searchable="true",
- :loading="querying.hotel",
- :internal-search="false",
- :clear-on-select="false",
- :options-limit="50",
- :limit="1",
- :max-height="600",
- @search-change="debounceSearch.hotel"
- )
- .row.text-center
- div
- label Restoration
- multiselect#ajax(
- v-model="journey_data.main[idx].places.restaurants",
- label="sname",
- track-by="place_id",
- placeholder="Type to search",
- open-direction="bottom",
- :multiple="true",
- :options="query.nominatim",
- :searchable="true",
- :loading="querying.food",
- :internal-search="false",
- :clear-on-select="false",
- :options-limit="50",
- :limit="10",
- :max-height="600",
- @search-change="debounceSearch.restaurants"
- )
- .row.text-center
- div
- label Activities
- multiselect#ajax(
- v-model="journey_data.main[idx].places.activities",
- label="sname",
- track-by="place_id",
- placeholder="Type to search",
- open-direction="bottom",
- :multiple="true",
- :options="query.nominatim",
- :searchable="true",
- :loading="querying.place",
- :internal-search="false",
- :clear-on-select="false",
- :options-limit="50",
- :limit="10",
- :max-height="600",
- @search-change="debounceSearch.places"
- )
- .row.text-center
- div
- label Notes
- .input.text-dark(style="width: 100%")
- textarea-autosize.text-small(
- v-model="journey_data.main[idx].notes",
- placeholder="Notes",
- :min-height="30",
- :max-height="350"
- )
diff --git a/template/module/map.pug b/template/module/map.pug
deleted file mode 100644
index 34f6e83..0000000
--- a/template/module/map.pug
+++ /dev/null
@@ -1,84 +0,0 @@
-l-map(
- :zoom.sync="journey_data.main[idx].map.zoom",
- :center.sync="journey_data.main[idx].map.center",
- style="padding-top: 100%"
-)
- l-tile-layer(
- url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
- attribution="© OpenStreetMap contributors"
- )
- l-control-scale(position="topright", :imperial="false", :metric="true")
- l-marker(
- v-if="journey_data.main[idx].hotel",
- :lat-lng="journey_data.main[idx].hotel.latlon"
- )
- l-icon
- div(v-html="generate_icon(journey_data.main[idx].hotel, 'darkblue')")
- l-popup
- h1.row.text-medium.text-center {{ journey_data.main[idx].hotel.sname }}
- span.row.text-small.text-gray {{ journey_data.main[idx].hotel.display_name }}
- span(v-if="journey_edit")
- .row.input
- textarea-autosize.col-12.col-sm-12.text-small(
- placeholder="Notes",
- v-model="journey_data.main[idx].hotel.notes",
- :min-height="30",
- :max-height="350"
- )
- span.row.text-small.text-white(v-else) {{ journey_data.main[idx].hotel.notes }}
- l-marker(
- v-for="place in journey_data.main[idx].places.activities",
- :lat-lng="place.latlon"
- )
- l-icon
- div(
- v-if="place.step == journey_step_data.day",
- v-html="generate_icon(place)"
- )
- div(
- v-else-if="place.step == -1 || place.step == undefined",
- v-html="generate_icon(place, 'gray')"
- )
- div(v-else-if="journey_edit", v-html="generate_icon(place, 'lightgray')")
- div(v-else)
- l-popup
- h1.row.text-medium.text-center {{ place.sname }}
- span.row.text-small.text-gray {{ place.display_name }}
- span(v-if="journey_edit")
- .row.input
- textarea-autosize.col-12.col-sm-12.text-small(
- placeholder="Notes",
- v-model="place.notes",
- :min-height="30",
- :max-height="350"
- )
- a.leaflet-popup-close-button.text-gray(
- style="right: 0px; visibility: visible",
- href="#rm",
- v-on:click.prevent="place.step = -1"
- ) -
- a.leaflet-popup-close-button.text-gray(
- style="right: 16px; visibility: visible",
- href="#ad",
- v-on:click.prevent="place.step = journey_step_data.day"
- ) +
- span.row.text-small.text-dark(v-else) {{ place.notes }}
-
- l-marker(
- v-for="place in journey_data.main[idx].places.restaurants",
- :lat-lng.sync="place.latlon"
- )
- l-icon
- div(v-html="generate_icon(place, 'cadetblue')")
- l-popup
- h1.row.text-medium.text-center {{ place.sname }}
- span.row.text-small.text-gray {{ place.display_name }}
- span(v-if="journey_edit")
- .row.input
- textarea-autosize.col-12.col-sm-12.text-small(
- placeholder="Notes",
- v-model="place.notes",
- :min-height="30",
- :max-height="350"
- )
- span.row.text-small.text-dark(v-else) {{ place.notes }}
diff --git a/template/module/nav.pug b/template/module/nav.pug
deleted file mode 100644
index c7173f1..0000000
--- a/template/module/nav.pug
+++ /dev/null
@@ -1,38 +0,0 @@
-header.header
- .header-inner.container
- a.header-logo.text-dark(href="/")
- img.header-logoImage(
- src="/public/img/helcel.png",
- alt="Helcel logo",
- width="40"
- )
- span.hide-small OTM
- .input.input-invis.row
- input.col-6.small(v-model="journey_data.name", type="text")
- input.col-6.small(
- disabled,
- type="text",
- :placeholder="total_date() + ' (' + total_days() + ')'"
- )
- .row.header-nav.text-big(style="margin-bottom: 0")
- .col-sm-2
- a(:href="'/short/' + journey_id")
- i.fas.fa-file-contract
- .col-sm-2
- a(:href="'/view/' + journey_id")
- i.fas.fa-camera
- .col-sm-2
- a(href="#main", v-on:click.prevent="first_step")
- i.fas.fa-tools
- .col-sm-1.text-small
- a(href="#prevprev", v-on:click.prevent="prevprev_step")
- i.fas.fa-angle-double-left
- .col-sm-1
- a(href="#prev", v-on:click.prevent="prev_step")
- i.fas.fa-angle-left
- .col-sm-1
- a(href="#next", v-on:click.prevent="next_step")
- i.fas.fa-angle-right
- .col-sm-1.text-small
- a(href="#nextnext", v-on:click.prevent="nextnext_step")
- i.fas.fa-angle-double-right
diff --git a/template/module/nav_pub.pug b/template/module/nav_pub.pug
deleted file mode 100644
index 49af761..0000000
--- a/template/module/nav_pub.pug
+++ /dev/null
@@ -1,18 +0,0 @@
-header.header
- .header-inner.container
- a.header-logo.text-dark(href="/")
- img.header-logoImage(
- src="/public/img/helcel.png",
- alt="Helcel logo",
- width="40"
- )
- span.hide-small HOTM
- .input.input-invis
- input.small(:value="journey_data.name", type="text", disabled="")
- .row.header-nav.text-big(style="margin-bottom: 0")
- .col-sm-3
- a(:href="'/short/' + journey_id")
- i.fas.fa-file-contract
- .col-sm-3
- a(:href="'/view/' + journey_id")
- i.fas.fa-camera
diff --git a/template/module/short_sec.pug b/template/module/short_sec.pug
deleted file mode 100644
index 3bfe2bf..0000000
--- a/template/module/short_sec.pug
+++ /dev/null
@@ -1,43 +0,0 @@
-.container.section
- .row.text-center
- .input.col-sm-2
- input(disabled="", :value="item.title")
- .input.col-sm-4
- input(
- disabled="",
- placeholder="No Dates",
- :value="item.dateRange ? format_date(item.dateRange[0]) + ' - ' + format_date(item.dateRange[1]) : ''"
- )
- .input.col-sm-2
- input(disabled="", placeholder="No Hotel", :value="item.hotel.sname")
- .row.text-center
- .input.col-sm-3(v-if="item.transit")
- div(v-for="(item, idx) in item.transit")
- input(disabled="", :value="item.map((v) => v.id).join(', ')")
- .row.text-center
- .input.col-sm-8(v-if="item.places && item.places.restaurants")
- textarea-autosize.text-small(
- placeholder="No Restaurants",
- :value="item.places.restaurants.map((v) => v.sname + (v.notes ? '(' + v.notes + ')' : '')).join(', ')",
- :min-height="30",
- :max-height="350",
- disabled=""
- )
- .row.text-center
- .input.col-sm-8(v-if="item.places && item.places.activities")
- textarea-autosize.text-small(
- placeholder="No Activities",
- :value="item.places.activities.map((v) => v.sname + (v.notes ? '(' + v.notes + ')' : '')).join(', ')",
- :min-height="30",
- :max-height="350",
- disabled=""
- )
- .row.text-center
- .input.col-sm-8(v-if="item.notes")
- textarea-autosize.text-small(
- placeholder="No Notes",
- :value="item.notes",
- :min-height="30",
- :max-height="350",
- disabled=""
- )
diff --git a/template/module/view_step.pug b/template/module/view_step.pug
deleted file mode 100644
index 6540d3e..0000000
--- a/template/module/view_step.pug
+++ /dev/null
@@ -1,19 +0,0 @@
-div(v-for="(e, idx) in journey_data.main", :key="idx")
- .bg-dark.text-white(v-if="journey_step_data.section == idx")
- .container.section
- .aligner.text-center.text-white.text-huge(style="margin-bottom: 5px")
- .aligner--itemTop.fleft
- a(href="#prev", v-on:click.prevent="prev_step")
- i.fas.fa-angle-left
- span.container
- span.small {{ journey_data.main[idx].title }} {{ journey_step_data.day }}
- .text-big.text-gray {{ journey_data.main[idx].step_title[journey_step_data.day] }}
- .aligner--itemEnd.fright
- a(href="#next", v-on:click.prevent="next_step")
- i.fas.fa-angle-right
- .row
- .col-12.col-sm-12
- include map.pug
- .row
- .col-12.col-sm-12
- .container
diff --git a/template/short.pug b/template/short.pug
deleted file mode 100644
index e06e6cb..0000000
--- a/template/short.pug
+++ /dev/null
@@ -1,10 +0,0 @@
-doctype html
-include module/head.pug
-main#app(v-cloak)
- include module/nav_pub.pug
- div(
- v-for="(item, idx) in journey_data.main",
- :class="idx % 2 === 0 ? 'bg-dark text-white' : ''"
- )
- include module/short_sec.pug
-include module/foot.pug
diff --git a/template/view.pug b/template/view.pug
deleted file mode 100644
index a80f3a6..0000000
--- a/template/view.pug
+++ /dev/null
@@ -1,6 +0,0 @@
-doctype html
-include module/head.pug
-main#app(v-cloak)
- div(v-if="journey_data.main[journey_step_data.section] != undefined")
- include module/view_step.pug
-include module/foot.pug
diff --git a/tsconfig-client.json b/tsconfig-client.json
new file mode 100644
index 0000000..04b8e7b
--- /dev/null
+++ b/tsconfig-client.json
@@ -0,0 +1,17 @@
+{
+ "compilerOptions": {
+ "target": "esnext",
+ "typeRoots": [
+ "./node_modules/@types",
+ "./types/ext"
+ ],
+ "lib": [
+ "esnext",
+ "DOM"
+ ],
+ "noEmit": true, // Disable emitting output (use esbuild to handle this)
+ "skipLibCheck": true, // Skip type checking of all declaration files (*.d.ts)
+ "strict": false, // Disable strict type checks if needed
+ "moduleResolution": "node",
+ }
+}
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
deleted file mode 100644
index 6a54986..0000000
--- a/tsconfig.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-
- "compilerOptions": {
- "target": "esnext",
- "typeRoots": ["./node_modules/@types", "./types/ext"],
- "lib": ["esnext", "DOM"],
- "noEmit": true, // Disable emitting output (use esbuild to handle this)
- "skipLibCheck": true, // Skip type checking of all declaration files (*.d.ts)
- "strict": false, // Disable strict type checks if needed
- "moduleResolution": "node",
- }
-}
diff --git a/yarn.lock b/yarn.lock
index 3d1e226..098f05a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5,6 +5,19 @@ __metadata:
version: 8
cacheKey: 10c0
+"@asamuzakjp/css-color@npm:^2.8.2":
+ version: 2.8.3
+ resolution: "@asamuzakjp/css-color@npm:2.8.3"
+ dependencies:
+ "@csstools/css-calc": "npm:^2.1.1"
+ "@csstools/css-color-parser": "npm:^3.0.7"
+ "@csstools/css-parser-algorithms": "npm:^3.0.4"
+ "@csstools/css-tokenizer": "npm:^3.0.3"
+ lru-cache: "npm:^10.4.3"
+ checksum: 10c0/e108c92ee5de6d8510c9aaca8375c0aeab730dc9b6d4bd287aea2a0379cfbaa09f0814dcacb3e2ddc5c79d7deedf3f82ec8d1ce0effd4a8fac8415b1fe553798
+ languageName: node
+ linkType: hard
+
"@babel/helper-string-parser@npm:^7.25.9":
version: 7.25.9
resolution: "@babel/helper-string-parser@npm:7.25.9"
@@ -40,6 +53,52 @@ __metadata:
languageName: node
linkType: hard
+"@csstools/color-helpers@npm:^5.0.2":
+ version: 5.0.2
+ resolution: "@csstools/color-helpers@npm:5.0.2"
+ checksum: 10c0/bebaddb28b9eb58b0449edd5d0c0318fa88f3cb079602ee27e88c9118070d666dcc4e09a5aa936aba2fde6ba419922ade07b7b506af97dd7051abd08dfb2959b
+ languageName: node
+ linkType: hard
+
+"@csstools/css-calc@npm:^2.1.1, @csstools/css-calc@npm:^2.1.2":
+ version: 2.1.2
+ resolution: "@csstools/css-calc@npm:2.1.2"
+ peerDependencies:
+ "@csstools/css-parser-algorithms": ^3.0.4
+ "@csstools/css-tokenizer": ^3.0.3
+ checksum: 10c0/34ced30553968ef5d5f9e00e3b90b48c47480cf130e282e99d57ec9b09f803aab8bc06325683e72a1518b5e7180a3da8b533f1b462062757c21989a53b482e1a
+ languageName: node
+ linkType: hard
+
+"@csstools/css-color-parser@npm:^3.0.7":
+ version: 3.0.8
+ resolution: "@csstools/css-color-parser@npm:3.0.8"
+ dependencies:
+ "@csstools/color-helpers": "npm:^5.0.2"
+ "@csstools/css-calc": "npm:^2.1.2"
+ peerDependencies:
+ "@csstools/css-parser-algorithms": ^3.0.4
+ "@csstools/css-tokenizer": ^3.0.3
+ checksum: 10c0/90722c5a62ca94e9d578ddf59be604a76400b932bd3d4bd23cb1ae9b7ace8fcf83c06995d2b31f96f4afef24a7cefba79beb11ed7ee4999d7ecfec3869368359
+ languageName: node
+ linkType: hard
+
+"@csstools/css-parser-algorithms@npm:^3.0.4":
+ version: 3.0.4
+ resolution: "@csstools/css-parser-algorithms@npm:3.0.4"
+ peerDependencies:
+ "@csstools/css-tokenizer": ^3.0.3
+ checksum: 10c0/d411f07765e14eede17bccc6bd4f90ff303694df09aabfede3fd104b2dfacfd4fe3697cd25ddad14684c850328f3f9420ebfa9f78380892492974db24ae47dbd
+ languageName: node
+ linkType: hard
+
+"@csstools/css-tokenizer@npm:^3.0.3":
+ version: 3.0.3
+ resolution: "@csstools/css-tokenizer@npm:3.0.3"
+ checksum: 10c0/c31bf410e1244b942e71798e37c54639d040cb59e0121b21712b40015fced2b0fb1ffe588434c5f8923c9cd0017cfc1c1c8f3921abc94c96edf471aac2eba5e5
+ languageName: node
+ linkType: hard
+
"@esbuild/aix-ppc64@npm:0.25.0":
version: 0.25.0
resolution: "@esbuild/aix-ppc64@npm:0.25.0"
@@ -376,6 +435,150 @@ __metadata:
languageName: node
linkType: hard
+"@parcel/watcher-android-arm64@npm:2.5.1":
+ version: 2.5.1
+ resolution: "@parcel/watcher-android-arm64@npm:2.5.1"
+ conditions: os=android & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@parcel/watcher-darwin-arm64@npm:2.5.1":
+ version: 2.5.1
+ resolution: "@parcel/watcher-darwin-arm64@npm:2.5.1"
+ conditions: os=darwin & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@parcel/watcher-darwin-x64@npm:2.5.1":
+ version: 2.5.1
+ resolution: "@parcel/watcher-darwin-x64@npm:2.5.1"
+ conditions: os=darwin & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@parcel/watcher-freebsd-x64@npm:2.5.1":
+ version: 2.5.1
+ resolution: "@parcel/watcher-freebsd-x64@npm:2.5.1"
+ conditions: os=freebsd & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@parcel/watcher-linux-arm-glibc@npm:2.5.1":
+ version: 2.5.1
+ resolution: "@parcel/watcher-linux-arm-glibc@npm:2.5.1"
+ conditions: os=linux & cpu=arm & libc=glibc
+ languageName: node
+ linkType: hard
+
+"@parcel/watcher-linux-arm-musl@npm:2.5.1":
+ version: 2.5.1
+ resolution: "@parcel/watcher-linux-arm-musl@npm:2.5.1"
+ conditions: os=linux & cpu=arm & libc=musl
+ languageName: node
+ linkType: hard
+
+"@parcel/watcher-linux-arm64-glibc@npm:2.5.1":
+ version: 2.5.1
+ resolution: "@parcel/watcher-linux-arm64-glibc@npm:2.5.1"
+ conditions: os=linux & cpu=arm64 & libc=glibc
+ languageName: node
+ linkType: hard
+
+"@parcel/watcher-linux-arm64-musl@npm:2.5.1":
+ version: 2.5.1
+ resolution: "@parcel/watcher-linux-arm64-musl@npm:2.5.1"
+ conditions: os=linux & cpu=arm64 & libc=musl
+ languageName: node
+ linkType: hard
+
+"@parcel/watcher-linux-x64-glibc@npm:2.5.1":
+ version: 2.5.1
+ resolution: "@parcel/watcher-linux-x64-glibc@npm:2.5.1"
+ conditions: os=linux & cpu=x64 & libc=glibc
+ languageName: node
+ linkType: hard
+
+"@parcel/watcher-linux-x64-musl@npm:2.5.1":
+ version: 2.5.1
+ resolution: "@parcel/watcher-linux-x64-musl@npm:2.5.1"
+ conditions: os=linux & cpu=x64 & libc=musl
+ languageName: node
+ linkType: hard
+
+"@parcel/watcher-win32-arm64@npm:2.5.1":
+ version: 2.5.1
+ resolution: "@parcel/watcher-win32-arm64@npm:2.5.1"
+ conditions: os=win32 & cpu=arm64
+ languageName: node
+ linkType: hard
+
+"@parcel/watcher-win32-ia32@npm:2.5.1":
+ version: 2.5.1
+ resolution: "@parcel/watcher-win32-ia32@npm:2.5.1"
+ conditions: os=win32 & cpu=ia32
+ languageName: node
+ linkType: hard
+
+"@parcel/watcher-win32-x64@npm:2.5.1":
+ version: 2.5.1
+ resolution: "@parcel/watcher-win32-x64@npm:2.5.1"
+ conditions: os=win32 & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@parcel/watcher@npm:^2.4.1":
+ version: 2.5.1
+ resolution: "@parcel/watcher@npm:2.5.1"
+ dependencies:
+ "@parcel/watcher-android-arm64": "npm:2.5.1"
+ "@parcel/watcher-darwin-arm64": "npm:2.5.1"
+ "@parcel/watcher-darwin-x64": "npm:2.5.1"
+ "@parcel/watcher-freebsd-x64": "npm:2.5.1"
+ "@parcel/watcher-linux-arm-glibc": "npm:2.5.1"
+ "@parcel/watcher-linux-arm-musl": "npm:2.5.1"
+ "@parcel/watcher-linux-arm64-glibc": "npm:2.5.1"
+ "@parcel/watcher-linux-arm64-musl": "npm:2.5.1"
+ "@parcel/watcher-linux-x64-glibc": "npm:2.5.1"
+ "@parcel/watcher-linux-x64-musl": "npm:2.5.1"
+ "@parcel/watcher-win32-arm64": "npm:2.5.1"
+ "@parcel/watcher-win32-ia32": "npm:2.5.1"
+ "@parcel/watcher-win32-x64": "npm:2.5.1"
+ detect-libc: "npm:^1.0.3"
+ is-glob: "npm:^4.0.3"
+ micromatch: "npm:^4.0.5"
+ node-addon-api: "npm:^7.0.0"
+ node-gyp: "npm:latest"
+ dependenciesMeta:
+ "@parcel/watcher-android-arm64":
+ optional: true
+ "@parcel/watcher-darwin-arm64":
+ optional: true
+ "@parcel/watcher-darwin-x64":
+ optional: true
+ "@parcel/watcher-freebsd-x64":
+ optional: true
+ "@parcel/watcher-linux-arm-glibc":
+ optional: true
+ "@parcel/watcher-linux-arm-musl":
+ optional: true
+ "@parcel/watcher-linux-arm64-glibc":
+ optional: true
+ "@parcel/watcher-linux-arm64-musl":
+ optional: true
+ "@parcel/watcher-linux-x64-glibc":
+ optional: true
+ "@parcel/watcher-linux-x64-musl":
+ optional: true
+ "@parcel/watcher-win32-arm64":
+ optional: true
+ "@parcel/watcher-win32-ia32":
+ optional: true
+ "@parcel/watcher-win32-x64":
+ optional: true
+ checksum: 10c0/8f35073d0c0b34a63d4c8d2213482f0ebc6a25de7b2cdd415d19cb929964a793cb285b68d1d50bfb732b070b3c82a2fdb4eb9c250eab709a1cd9d63345455a82
+ languageName: node
+ linkType: hard
+
"@pkgjs/parseargs@npm:^0.11.0":
version: 0.11.0
resolution: "@pkgjs/parseargs@npm:0.11.0"
@@ -383,17 +586,6 @@ __metadata:
languageName: node
linkType: hard
-"@prettier/plugin-pug@npm:^3.0.0":
- version: 3.2.1
- resolution: "@prettier/plugin-pug@npm:3.2.1"
- dependencies:
- pug-lexer: "npm:^5.0.1"
- peerDependencies:
- prettier: ^3.0.0
- checksum: 10c0/f361496e1669e308e1e74b3de2f524ba92364904142b23cf496bc26531f8cab3ccea965d0cb32b6272656c0415f0dbfe422aa6dc844f56f89cbdbdc815899390
- languageName: node
- linkType: hard
-
"@types/node@npm:^22.13.5":
version: 22.13.5
resolution: "@types/node@npm:22.13.5"
@@ -551,17 +743,6 @@ __metadata:
languageName: node
linkType: hard
-"axios@npm:^1.7.9":
- version: 1.7.9
- resolution: "axios@npm:1.7.9"
- dependencies:
- follow-redirects: "npm:^1.15.6"
- form-data: "npm:^4.0.0"
- proxy-from-env: "npm:^1.1.0"
- checksum: 10c0/b7a41e24b59fee5f0f26c1fc844b45b17442832eb3a0fb42dd4f1430eb4abc571fe168e67913e8a1d91c993232bd1d1ab03e20e4d1fee8c6147649b576fc1b0b
- languageName: node
- linkType: hard
-
"babel-walk@npm:3.0.0-canary-5":
version: 3.0.0-canary-5
resolution: "babel-walk@npm:3.0.0-canary-5"
@@ -611,7 +792,7 @@ __metadata:
languageName: node
linkType: hard
-"braces@npm:~3.0.2":
+"braces@npm:^3.0.3, braces@npm:~3.0.2":
version: 3.0.3
resolution: "braces@npm:3.0.3"
dependencies:
@@ -686,7 +867,7 @@ __metadata:
languageName: node
linkType: hard
-"chokidar@npm:^3.5.2":
+"chokidar@npm:>=3.0.0 <4.0.0, chokidar@npm:^3.5.2":
version: 3.6.0
resolution: "chokidar@npm:3.6.0"
dependencies:
@@ -705,6 +886,15 @@ __metadata:
languageName: node
linkType: hard
+"chokidar@npm:^4.0.0":
+ version: 4.0.3
+ resolution: "chokidar@npm:4.0.3"
+ dependencies:
+ readdirp: "npm:^4.0.1"
+ checksum: 10c0/a58b9df05bb452f7d105d9e7229ac82fa873741c0c40ddcc7bb82f8a909fbe3f7814c9ebe9bc9a2bef9b737c0ec6e2d699d179048ef06ad3ec46315df0ebe6ad
+ languageName: node
+ linkType: hard
+
"chownr@npm:^3.0.0":
version: 3.0.0
resolution: "chownr@npm:3.0.0"
@@ -781,6 +971,36 @@ __metadata:
languageName: node
linkType: hard
+"css-tree@npm:1.1.3":
+ version: 1.1.3
+ resolution: "css-tree@npm:1.1.3"
+ dependencies:
+ mdn-data: "npm:2.0.14"
+ source-map: "npm:^0.6.1"
+ checksum: 10c0/499a507bfa39b8b2128f49736882c0dd636b0cd3370f2c69f4558ec86d269113286b7df469afc955de6a68b0dba00bc533e40022a73698081d600072d5d83c1c
+ languageName: node
+ linkType: hard
+
+"cssstyle@npm:^4.2.1":
+ version: 4.2.1
+ resolution: "cssstyle@npm:4.2.1"
+ dependencies:
+ "@asamuzakjp/css-color": "npm:^2.8.2"
+ rrweb-cssom: "npm:^0.8.0"
+ checksum: 10c0/02ba8c47c0caaab57acadacb3eb6c0f5f009000f55d61f6563670e07d389b26edefeed497e6c1847fcd2e6bbe0b6974c2d4291f97fa0c6ec6add13a7fa926d84
+ languageName: node
+ linkType: hard
+
+"data-urls@npm:^5.0.0":
+ version: 5.0.0
+ resolution: "data-urls@npm:5.0.0"
+ dependencies:
+ whatwg-mimetype: "npm:^4.0.0"
+ whatwg-url: "npm:^14.0.0"
+ checksum: 10c0/1b894d7d41c861f3a4ed2ae9b1c3f0909d4575ada02e36d3d3bc584bdd84278e20709070c79c3b3bff7ac98598cb191eb3e86a89a79ea4ee1ef360e1694f92ad
+ languageName: node
+ linkType: hard
+
"debug@npm:4, debug@npm:^4, debug@npm:^4.3.4":
version: 4.4.0
resolution: "debug@npm:4.4.0"
@@ -793,6 +1013,13 @@ __metadata:
languageName: node
linkType: hard
+"decimal.js@npm:^10.4.3":
+ version: 10.5.0
+ resolution: "decimal.js@npm:10.5.0"
+ checksum: 10c0/785c35279df32762143914668df35948920b6c1c259b933e0519a69b7003fc0a5ed2a766b1e1dda02574450c566b21738a45f15e274b47c2ac02072c0d1f3ac3
+ languageName: node
+ linkType: hard
+
"deferred-leveldown@npm:^7.0.0":
version: 7.0.0
resolution: "deferred-leveldown@npm:7.0.0"
@@ -824,6 +1051,15 @@ __metadata:
languageName: node
linkType: hard
+"detect-libc@npm:^1.0.3":
+ version: 1.0.3
+ resolution: "detect-libc@npm:1.0.3"
+ bin:
+ detect-libc: ./bin/detect-libc.js
+ checksum: 10c0/4da0deae9f69e13bc37a0902d78bf7169480004b1fed3c19722d56cff578d16f0e11633b7fbf5fb6249181236c72e90024cbd68f0b9558ae06e281f47326d50d
+ languageName: node
+ linkType: hard
+
"doctypes@npm:^1.1.0":
version: 1.1.0
resolution: "doctypes@npm:1.1.0"
@@ -884,6 +1120,13 @@ __metadata:
languageName: node
linkType: hard
+"entities@npm:^4.5.0":
+ version: 4.5.0
+ resolution: "entities@npm:4.5.0"
+ checksum: 10c0/5b039739f7621f5d1ad996715e53d964035f75ad3b9a4d38c6b3804bb226e282ffeae2443624d8fdd9c47d8e926ae9ac009c54671243f0c3294c26af7cc85250
+ languageName: node
+ linkType: hard
+
"env-paths@npm:^2.2.0":
version: 2.2.1
resolution: "env-paths@npm:2.2.1"
@@ -933,6 +1176,20 @@ __metadata:
languageName: node
linkType: hard
+"esbuild-plugin-sass@npm:^1.0.1":
+ version: 1.0.1
+ resolution: "esbuild-plugin-sass@npm:1.0.1"
+ dependencies:
+ css-tree: "npm:1.1.3"
+ fs-extra: "npm:10.0.0"
+ sass: "npm:1.47.0"
+ tmp: "npm:0.2.1"
+ peerDependencies:
+ esbuild: ">=0.11.14"
+ checksum: 10c0/4a8b0aec2de41a44bfeb5518e5754be6b8a39a8873f4e543bfb40595636db639b5a5f9e2561594c8de7c81e5dddb88b83e73336827cada875203a185b4a1d053
+ languageName: node
+ linkType: hard
+
"esbuild@npm:^0.25.0":
version: 0.25.0
resolution: "esbuild@npm:0.25.0"
@@ -1091,7 +1348,7 @@ __metadata:
languageName: node
linkType: hard
-"fastify@npm:^5.0.0":
+"fastify@npm:^5.2.1":
version: 5.2.1
resolution: "fastify@npm:5.2.1"
dependencies:
@@ -1143,16 +1400,6 @@ __metadata:
languageName: node
linkType: hard
-"follow-redirects@npm:^1.15.6":
- version: 1.15.9
- resolution: "follow-redirects@npm:1.15.9"
- peerDependenciesMeta:
- debug:
- optional: true
- checksum: 10c0/5829165bd112c3c0e82be6c15b1a58fa9dcfaede3b3c54697a82fe4a62dd5ae5e8222956b448d2f98e331525f05d00404aba7d696de9e761ef6e42fdc780244f
- languageName: node
- linkType: hard
-
"foreground-child@npm:^3.1.0":
version: 3.3.0
resolution: "foreground-child@npm:3.3.0"
@@ -1163,7 +1410,7 @@ __metadata:
languageName: node
linkType: hard
-"form-data@npm:^4.0.0":
+"form-data@npm:^4.0.1":
version: 4.0.2
resolution: "form-data@npm:4.0.2"
dependencies:
@@ -1175,6 +1422,17 @@ __metadata:
languageName: node
linkType: hard
+"fs-extra@npm:10.0.0":
+ version: 10.0.0
+ resolution: "fs-extra@npm:10.0.0"
+ dependencies:
+ graceful-fs: "npm:^4.2.0"
+ jsonfile: "npm:^6.0.1"
+ universalify: "npm:^2.0.0"
+ checksum: 10c0/85802f3d9e49d197744a8372f0d78d5a1faa3df73f4c5375d6366a4b9f745197d3da1f095841443d50f29a9f81cdc01363eb6d17bef2ba70c268559368211040
+ languageName: node
+ linkType: hard
+
"fs-minipass@npm:^3.0.0":
version: 3.0.3
resolution: "fs-minipass@npm:3.0.3"
@@ -1184,6 +1442,13 @@ __metadata:
languageName: node
linkType: hard
+"fs.realpath@npm:^1.0.0":
+ version: 1.0.0
+ resolution: "fs.realpath@npm:1.0.0"
+ checksum: 10c0/444cf1291d997165dfd4c0d58b69f0e4782bfd9149fd72faa4fe299e68e0e93d6db941660b37dd29153bf7186672ececa3b50b7e7249477b03fdf850f287c948
+ languageName: node
+ linkType: hard
+
"fsevents@npm:~2.3.2":
version: 2.3.3
resolution: "fsevents@npm:2.3.3"
@@ -1279,6 +1544,20 @@ __metadata:
languageName: node
linkType: hard
+"glob@npm:^7.1.3":
+ version: 7.2.3
+ resolution: "glob@npm:7.2.3"
+ dependencies:
+ fs.realpath: "npm:^1.0.0"
+ inflight: "npm:^1.0.4"
+ inherits: "npm:2"
+ minimatch: "npm:^3.1.1"
+ once: "npm:^1.3.0"
+ path-is-absolute: "npm:^1.0.0"
+ checksum: 10c0/65676153e2b0c9095100fe7f25a778bf45608eeb32c6048cf307f579649bcc30353277b3b898a3792602c65764e5baa4f643714dfbdfd64ea271d210c7a425fe
+ languageName: node
+ linkType: hard
+
"gopd@npm:^1.2.0":
version: 1.2.0
resolution: "gopd@npm:1.2.0"
@@ -1286,7 +1565,7 @@ __metadata:
languageName: node
linkType: hard
-"graceful-fs@npm:^4.2.6":
+"graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.6":
version: 4.2.11
resolution: "graceful-fs@npm:4.2.11"
checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2
@@ -1325,6 +1604,15 @@ __metadata:
languageName: node
linkType: hard
+"html-encoding-sniffer@npm:^4.0.0":
+ version: 4.0.0
+ resolution: "html-encoding-sniffer@npm:4.0.0"
+ dependencies:
+ whatwg-encoding: "npm:^3.1.1"
+ checksum: 10c0/523398055dc61ac9b34718a719cb4aa691e4166f29187e211e1607de63dc25ac7af52ca7c9aead0c4b3c0415ffecb17326396e1202e2e86ff4bca4c0ee4c6140
+ languageName: node
+ linkType: hard
+
"http-cache-semantics@npm:^4.1.1":
version: 4.1.1
resolution: "http-cache-semantics@npm:4.1.1"
@@ -1345,7 +1633,7 @@ __metadata:
languageName: node
linkType: hard
-"http-proxy-agent@npm:^7.0.0":
+"http-proxy-agent@npm:^7.0.0, http-proxy-agent@npm:^7.0.2":
version: 7.0.2
resolution: "http-proxy-agent@npm:7.0.2"
dependencies:
@@ -1355,7 +1643,7 @@ __metadata:
languageName: node
linkType: hard
-"https-proxy-agent@npm:^7.0.1":
+"https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.6":
version: 7.0.6
resolution: "https-proxy-agent@npm:7.0.6"
dependencies:
@@ -1365,7 +1653,7 @@ __metadata:
languageName: node
linkType: hard
-"iconv-lite@npm:^0.6.2":
+"iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2":
version: 0.6.3
resolution: "iconv-lite@npm:0.6.3"
dependencies:
@@ -1388,6 +1676,20 @@ __metadata:
languageName: node
linkType: hard
+"immutable@npm:^4.0.0":
+ version: 4.3.7
+ resolution: "immutable@npm:4.3.7"
+ checksum: 10c0/9b099197081b22f6433003e34929da8ecddbbdc1474cdc8aa3b7669dee4adda349c06143de22def36016d1b6de5322b043eccd7a11db1dad2ca85dad4fff5435
+ languageName: node
+ linkType: hard
+
+"immutable@npm:^5.0.2":
+ version: 5.0.3
+ resolution: "immutable@npm:5.0.3"
+ checksum: 10c0/3269827789e1026cd25c2ea97f0b2c19be852ffd49eda1b674b20178f73d84fa8d945ad6f5ac5bc4545c2b4170af9f6e1f77129bc1cae7974a4bf9b04a9cdfb9
+ languageName: node
+ linkType: hard
+
"imurmurhash@npm:^0.1.4":
version: 0.1.4
resolution: "imurmurhash@npm:0.1.4"
@@ -1395,7 +1697,17 @@ __metadata:
languageName: node
linkType: hard
-"inherits@npm:2.0.4, inherits@npm:^2.0.3, inherits@npm:^2.0.4":
+"inflight@npm:^1.0.4":
+ version: 1.0.6
+ resolution: "inflight@npm:1.0.6"
+ dependencies:
+ once: "npm:^1.3.0"
+ wrappy: "npm:1"
+ checksum: 10c0/7faca22584600a9dc5b9fca2cd5feb7135ac8c935449837b315676b4c90aa4f391ec4f42240178244b5a34e8bede1948627fda392ca3191522fc46b34e985ab2
+ languageName: node
+ linkType: hard
+
+"inherits@npm:2, inherits@npm:2.0.4, inherits@npm:^2.0.3, inherits@npm:^2.0.4":
version: 2.0.4
resolution: "inherits@npm:2.0.4"
checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2
@@ -1468,7 +1780,7 @@ __metadata:
languageName: node
linkType: hard
-"is-glob@npm:^4.0.1, is-glob@npm:~4.0.1":
+"is-glob@npm:^4.0.1, is-glob@npm:^4.0.3, is-glob@npm:~4.0.1":
version: 4.0.3
resolution: "is-glob@npm:4.0.3"
dependencies:
@@ -1484,6 +1796,13 @@ __metadata:
languageName: node
linkType: hard
+"is-potential-custom-element-name@npm:^1.0.1":
+ version: 1.0.1
+ resolution: "is-potential-custom-element-name@npm:1.0.1"
+ checksum: 10c0/b73e2f22bc863b0939941d369486d308b43d7aef1f9439705e3582bfccaa4516406865e32c968a35f97a99396dac84e2624e67b0a16b0a15086a785e16ce7db9
+ languageName: node
+ linkType: hard
+
"is-promise@npm:^2.0.0":
version: 2.2.2
resolution: "is-promise@npm:2.2.2"
@@ -1553,6 +1872,40 @@ __metadata:
languageName: node
linkType: hard
+"jsdom@npm:^26.0.0":
+ version: 26.0.0
+ resolution: "jsdom@npm:26.0.0"
+ dependencies:
+ cssstyle: "npm:^4.2.1"
+ data-urls: "npm:^5.0.0"
+ decimal.js: "npm:^10.4.3"
+ form-data: "npm:^4.0.1"
+ html-encoding-sniffer: "npm:^4.0.0"
+ http-proxy-agent: "npm:^7.0.2"
+ https-proxy-agent: "npm:^7.0.6"
+ is-potential-custom-element-name: "npm:^1.0.1"
+ nwsapi: "npm:^2.2.16"
+ parse5: "npm:^7.2.1"
+ rrweb-cssom: "npm:^0.8.0"
+ saxes: "npm:^6.0.0"
+ symbol-tree: "npm:^3.2.4"
+ tough-cookie: "npm:^5.0.0"
+ w3c-xmlserializer: "npm:^5.0.0"
+ webidl-conversions: "npm:^7.0.0"
+ whatwg-encoding: "npm:^3.1.1"
+ whatwg-mimetype: "npm:^4.0.0"
+ whatwg-url: "npm:^14.1.0"
+ ws: "npm:^8.18.0"
+ xml-name-validator: "npm:^5.0.0"
+ peerDependencies:
+ canvas: ^3.0.0
+ peerDependenciesMeta:
+ canvas:
+ optional: true
+ checksum: 10c0/e48725ba4027edcfc9bca5799eaec72c6561ecffe3675a8ff87fe9c3541ca4ff9f82b4eff5b3d9c527302da0d859b2f60e9364347a5d42b77f5c76c436c569dc
+ languageName: node
+ linkType: hard
+
"json-schema-ref-resolver@npm:^2.0.0":
version: 2.0.1
resolution: "json-schema-ref-resolver@npm:2.0.1"
@@ -1569,6 +1922,19 @@ __metadata:
languageName: node
linkType: hard
+"jsonfile@npm:^6.0.1":
+ version: 6.1.0
+ resolution: "jsonfile@npm:6.1.0"
+ dependencies:
+ graceful-fs: "npm:^4.1.6"
+ universalify: "npm:^2.0.0"
+ dependenciesMeta:
+ graceful-fs:
+ optional: true
+ checksum: 10c0/4f95b5e8a5622b1e9e8f33c96b7ef3158122f595998114d1e7f03985649ea99cb3cd99ce1ed1831ae94c8c8543ab45ebd044207612f31a56fd08462140e46865
+ languageName: node
+ linkType: hard
+
"jstransformer@npm:1.0.0":
version: 1.0.0
resolution: "jstransformer@npm:1.0.0"
@@ -1658,7 +2024,7 @@ __metadata:
languageName: node
linkType: hard
-"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0":
+"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0, lru-cache@npm:^10.4.3":
version: 10.4.3
resolution: "lru-cache@npm:10.4.3"
checksum: 10c0/ebd04fbca961e6c1d6c0af3799adcc966a1babe798f685bb84e6599266599cd95d94630b10262f5424539bc4640107e8a33aa28585374abf561d30d16f4b39fb
@@ -1698,6 +2064,23 @@ __metadata:
languageName: node
linkType: hard
+"mdn-data@npm:2.0.14":
+ version: 2.0.14
+ resolution: "mdn-data@npm:2.0.14"
+ checksum: 10c0/67241f8708c1e665a061d2b042d2d243366e93e5bf1f917693007f6d55111588b952dcbfd3ea9c2d0969fb754aad81b30fdcfdcc24546495fc3b24336b28d4bd
+ languageName: node
+ linkType: hard
+
+"micromatch@npm:^4.0.5":
+ version: 4.0.8
+ resolution: "micromatch@npm:4.0.8"
+ dependencies:
+ braces: "npm:^3.0.3"
+ picomatch: "npm:^2.3.1"
+ checksum: 10c0/166fa6eb926b9553f32ef81f5f531d27b4ce7da60e5baf8c021d043b27a388fb95e46a8038d5045877881e673f8134122b59624d5cecbd16eb50a42e7a6b5ca8
+ languageName: node
+ linkType: hard
+
"mime-db@npm:1.52.0":
version: 1.52.0
resolution: "mime-db@npm:1.52.0"
@@ -1732,7 +2115,7 @@ __metadata:
languageName: node
linkType: hard
-"minimatch@npm:^3.1.2":
+"minimatch@npm:^3.1.1, minimatch@npm:^3.1.2":
version: 3.1.2
resolution: "minimatch@npm:3.1.2"
dependencies:
@@ -1857,6 +2240,15 @@ __metadata:
languageName: node
linkType: hard
+"node-addon-api@npm:^7.0.0":
+ version: 7.1.1
+ resolution: "node-addon-api@npm:7.1.1"
+ dependencies:
+ node-gyp: "npm:latest"
+ checksum: 10c0/fb32a206276d608037fa1bcd7e9921e177fe992fc610d098aa3128baca3c0050fc1e014fa007e9b3874cf865ddb4f5bd9f43ccb7cbbbe4efaff6a83e920b17e9
+ languageName: node
+ linkType: hard
+
"node-gyp-build@npm:^4.3.0":
version: 4.8.4
resolution: "node-gyp-build@npm:4.8.4"
@@ -1926,6 +2318,13 @@ __metadata:
languageName: node
linkType: hard
+"nwsapi@npm:^2.2.16":
+ version: 2.2.16
+ resolution: "nwsapi@npm:2.2.16"
+ checksum: 10c0/0aa0637f4d51043d0183d994e08336bae996b03b42984381bf09ebdf3ff4909c018eda6b2a8aba0a08f3ea8303db8a0dad0608b38dc0bff15fd87017286ae21a
+ languageName: node
+ linkType: hard
+
"object-assign@npm:^4.1.1":
version: 4.1.1
resolution: "object-assign@npm:4.1.1"
@@ -1940,6 +2339,15 @@ __metadata:
languageName: node
linkType: hard
+"once@npm:^1.3.0":
+ version: 1.4.0
+ resolution: "once@npm:1.4.0"
+ dependencies:
+ wrappy: "npm:1"
+ checksum: 10c0/5d48aca287dfefabd756621c5dfce5c91a549a93e9fdb7b8246bc4c4790aa2ec17b34a260530474635147aeb631a2dcc8b32c613df0675f96041cbb8244517d0
+ languageName: node
+ linkType: hard
+
"p-map@npm:^7.0.2":
version: 7.0.3
resolution: "p-map@npm:7.0.3"
@@ -1954,6 +2362,22 @@ __metadata:
languageName: node
linkType: hard
+"parse5@npm:^7.2.1":
+ version: 7.2.1
+ resolution: "parse5@npm:7.2.1"
+ dependencies:
+ entities: "npm:^4.5.0"
+ checksum: 10c0/829d37a0c709215a887e410a7118d754f8e1afd7edb529db95bc7bbf8045fb0266a7b67801331d8e8d9d073ea75793624ec27ce9ff3b96862c3b9008f4d68e80
+ languageName: node
+ linkType: hard
+
+"path-is-absolute@npm:^1.0.0":
+ version: 1.0.1
+ resolution: "path-is-absolute@npm:1.0.1"
+ checksum: 10c0/127da03c82172a2a50099cddbf02510c1791fc2cc5f7713ddb613a56838db1e8168b121a920079d052e0936c23005562059756d653b7c544c53185efe53be078
+ languageName: node
+ linkType: hard
+
"path-key@npm:^3.1.0":
version: 3.1.1
resolution: "path-key@npm:3.1.1"
@@ -1988,7 +2412,7 @@ __metadata:
languageName: node
linkType: hard
-"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1":
+"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.3.1":
version: 2.3.1
resolution: "picomatch@npm:2.3.1"
checksum: 10c0/26c02b8d06f03206fc2ab8d16f19960f2ff9e81a658f831ecb656d8f17d9edc799e8364b1f4a7873e89d9702dff96204be0fa26fe4181f6843f040f819dac4be
@@ -2032,13 +2456,6 @@ __metadata:
languageName: node
linkType: hard
-"pretier@npm:^0.0.1":
- version: 0.0.1
- resolution: "pretier@npm:0.0.1"
- checksum: 10c0/206f5b353c32a9ad0e38243ad2caf7c8859ef43c975514e1c725088e4925d8b1480f73b96ebfd5ceaa1a563a77a3b8c893db7bb3642585bfa0c635f7ee149885
- languageName: node
- linkType: hard
-
"prettier@npm:^3.5.2":
version: 3.5.2
resolution: "prettier@npm:3.5.2"
@@ -2081,13 +2498,6 @@ __metadata:
languageName: node
linkType: hard
-"proxy-from-env@npm:^1.1.0":
- version: 1.1.0
- resolution: "proxy-from-env@npm:1.1.0"
- checksum: 10c0/fe7dd8b1bdbbbea18d1459107729c3e4a2243ca870d26d34c2c1bcd3e4425b7bcc5112362df2d93cc7fb9746f6142b5e272fd1cc5c86ddf8580175186f6ad42b
- languageName: node
- linkType: hard
-
"pstree.remy@npm:^1.1.8":
version: 1.1.8
resolution: "pstree.remy@npm:1.1.8"
@@ -2222,6 +2632,13 @@ __metadata:
languageName: node
linkType: hard
+"punycode@npm:^2.3.1":
+ version: 2.3.1
+ resolution: "punycode@npm:2.3.1"
+ checksum: 10c0/14f76a8206bc3464f794fb2e3d3cc665ae416c01893ad7a02b23766eb07159144ee612ad67af5e84fa4479ccfe67678c4feb126b0485651b302babf66f04f9e9
+ languageName: node
+ linkType: hard
+
"queue-microtask@npm:^1.2.3":
version: 1.2.3
resolution: "queue-microtask@npm:1.2.3"
@@ -2247,6 +2664,13 @@ __metadata:
languageName: node
linkType: hard
+"readdirp@npm:^4.0.1":
+ version: 4.1.2
+ resolution: "readdirp@npm:4.1.2"
+ checksum: 10c0/60a14f7619dec48c9c850255cd523e2717001b0e179dc7037cfa0895da7b9e9ab07532d324bfb118d73a710887d1e35f79c495fa91582784493e085d18c72c62
+ languageName: node
+ linkType: hard
+
"readdirp@npm:~3.6.0":
version: 3.6.0
resolution: "readdirp@npm:3.6.0"
@@ -2324,6 +2748,17 @@ __metadata:
languageName: node
linkType: hard
+"rimraf@npm:^3.0.0":
+ version: 3.0.2
+ resolution: "rimraf@npm:3.0.2"
+ dependencies:
+ glob: "npm:^7.1.3"
+ bin:
+ rimraf: bin.js
+ checksum: 10c0/9cb7757acb489bd83757ba1a274ab545eafd75598a9d817e0c3f8b164238dd90eba50d6b848bd4dcc5f3040912e882dc7ba71653e35af660d77b25c381d402e8
+ languageName: node
+ linkType: hard
+
"rimraf@npm:^5.0.5":
version: 5.0.10
resolution: "rimraf@npm:5.0.10"
@@ -2335,6 +2770,13 @@ __metadata:
languageName: node
linkType: hard
+"rrweb-cssom@npm:^0.8.0":
+ version: 0.8.0
+ resolution: "rrweb-cssom@npm:0.8.0"
+ checksum: 10c0/56f2bfd56733adb92c0b56e274c43f864b8dd48784d6fe946ef5ff8d438234015e59ad837fc2ad54714b6421384141c1add4eb569e72054e350d1f8a50b8ac7b
+ languageName: node
+ linkType: hard
+
"safe-buffer@npm:5.2.1, safe-buffer@npm:~5.2.0":
version: 5.2.1
resolution: "safe-buffer@npm:5.2.1"
@@ -2365,6 +2807,45 @@ __metadata:
languageName: node
linkType: hard
+"sass@npm:1.47.0":
+ version: 1.47.0
+ resolution: "sass@npm:1.47.0"
+ dependencies:
+ chokidar: "npm:>=3.0.0 <4.0.0"
+ immutable: "npm:^4.0.0"
+ source-map-js: "npm:>=0.6.2 <2.0.0"
+ bin:
+ sass: sass.js
+ checksum: 10c0/863afee6aac8529a009d184e5cf8cd79dbd4d4759a49170b2ece5a519911311b630f9630e66db287c4e883d5635c4e8a2a12e0d4579bf484df72680a64dccf56
+ languageName: node
+ linkType: hard
+
+"sass@npm:^1.85.1":
+ version: 1.85.1
+ resolution: "sass@npm:1.85.1"
+ dependencies:
+ "@parcel/watcher": "npm:^2.4.1"
+ chokidar: "npm:^4.0.0"
+ immutable: "npm:^5.0.2"
+ source-map-js: "npm:>=0.6.2 <2.0.0"
+ dependenciesMeta:
+ "@parcel/watcher":
+ optional: true
+ bin:
+ sass: sass.js
+ checksum: 10c0/f843aa1df1dca2f0e9cb2fb247e4939fd514ae4c182cdd1900a0622c0d71b40dfb1c4225f78b78e165a318287ca137ec597695db3e496408bd16a921a2bc2b3f
+ languageName: node
+ linkType: hard
+
+"saxes@npm:^6.0.0":
+ version: 6.0.0
+ resolution: "saxes@npm:6.0.0"
+ dependencies:
+ xmlchars: "npm:^2.2.0"
+ checksum: 10c0/3847b839f060ef3476eb8623d099aa502ad658f5c40fd60c105ebce86d244389b0d76fcae30f4d0c728d7705ceb2f7e9b34bb54717b6a7dbedaf5dad2d9a4b74
+ languageName: node
+ linkType: hard
+
"secure-json-parse@npm:^3.0.1":
version: 3.0.2
resolution: "secure-json-parse@npm:3.0.2"
@@ -2464,6 +2945,20 @@ __metadata:
languageName: node
linkType: hard
+"source-map-js@npm:>=0.6.2 <2.0.0":
+ version: 1.2.1
+ resolution: "source-map-js@npm:1.2.1"
+ checksum: 10c0/7bda1fc4c197e3c6ff17de1b8b2c20e60af81b63a52cb32ec5a5d67a20a7d42651e2cb34ebe93833c5a2a084377e17455854fee3e21e7925c64a51b6a52b0faf
+ languageName: node
+ linkType: hard
+
+"source-map@npm:^0.6.1":
+ version: 0.6.1
+ resolution: "source-map@npm:0.6.1"
+ checksum: 10c0/ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011
+ languageName: node
+ linkType: hard
+
"split2@npm:^4.0.0":
version: 4.2.0
resolution: "split2@npm:4.2.0"
@@ -2559,6 +3054,13 @@ __metadata:
languageName: node
linkType: hard
+"symbol-tree@npm:^3.2.4":
+ version: 3.2.4
+ resolution: "symbol-tree@npm:3.2.4"
+ checksum: 10c0/dfbe201ae09ac6053d163578778c53aa860a784147ecf95705de0cd23f42c851e1be7889241495e95c37cabb058edb1052f141387bef68f705afc8f9dd358509
+ languageName: node
+ linkType: hard
+
"tar@npm:^7.4.3":
version: 7.4.3
resolution: "tar@npm:7.4.3"
@@ -2582,6 +3084,33 @@ __metadata:
languageName: node
linkType: hard
+"tldts-core@npm:^6.1.78":
+ version: 6.1.78
+ resolution: "tldts-core@npm:6.1.78"
+ checksum: 10c0/aea5e664da879cd862ccf5df9286531ddf4c34a9ca832480188bf6cd165cd45654f5b0a0f0f5315e16203ebfb87d52f8630b9419e729b3cfe5eff073c398693e
+ languageName: node
+ linkType: hard
+
+"tldts@npm:^6.1.32":
+ version: 6.1.78
+ resolution: "tldts@npm:6.1.78"
+ dependencies:
+ tldts-core: "npm:^6.1.78"
+ bin:
+ tldts: bin/cli.js
+ checksum: 10c0/966f3f5a63405db6abb49b479784baa677510993f21ffbd67571f3d819451d70a603f1246b13f1c309a7573c4d9fbe0241aca6ff6e8399cbe7d2dd70b7ee4052
+ languageName: node
+ linkType: hard
+
+"tmp@npm:0.2.1":
+ version: 0.2.1
+ resolution: "tmp@npm:0.2.1"
+ dependencies:
+ rimraf: "npm:^3.0.0"
+ checksum: 10c0/67607aa012059c9ce697bee820ee51bc0f39b29a8766def4f92d3f764d67c7cf9205d537d24e0cb1ce9685c40d4c628ead010910118ea18348666b5c46ed9123
+ languageName: node
+ linkType: hard
+
"to-regex-range@npm:^5.0.1":
version: 5.0.1
resolution: "to-regex-range@npm:5.0.1"
@@ -2621,6 +3150,24 @@ __metadata:
languageName: node
linkType: hard
+"tough-cookie@npm:^5.0.0":
+ version: 5.1.1
+ resolution: "tough-cookie@npm:5.1.1"
+ dependencies:
+ tldts: "npm:^6.1.32"
+ checksum: 10c0/84fe18b7c28ce273c916d95028c00ffff58c285d58e90fbd44eb9380dd1bc21892c675cd1bbd4bfbc95108fe833c406b285844757d41636248bfe264655a6ef8
+ languageName: node
+ linkType: hard
+
+"tr46@npm:^5.0.0":
+ version: 5.0.0
+ resolution: "tr46@npm:5.0.0"
+ dependencies:
+ punycode: "npm:^2.3.1"
+ checksum: 10c0/1521b6e7bbc8adc825c4561480f9fe48eb2276c81335eed9fa610aa4c44a48a3221f78b10e5f18b875769eb3413e30efbf209ed556a17a42aa8d690df44b7bee
+ languageName: node
+ linkType: hard
+
"undefsafe@npm:^2.0.5":
version: 2.0.5
resolution: "undefsafe@npm:2.0.5"
@@ -2635,6 +3182,13 @@ __metadata:
languageName: node
linkType: hard
+"undici@npm:^7.3.0":
+ version: 7.3.0
+ resolution: "undici@npm:7.3.0"
+ checksum: 10c0/62c5e335725cadb02e19950932c7823fc330cbfd80106e6836daa6db1379aa727510b77de0a4e6f912087b288ded93f7daf4b8c154ad36fd5c9c4b96b26888b8
+ languageName: node
+ linkType: hard
+
"unique-filename@npm:^4.0.0":
version: 4.0.0
resolution: "unique-filename@npm:4.0.0"
@@ -2653,6 +3207,13 @@ __metadata:
languageName: node
linkType: hard
+"universalify@npm:^2.0.0":
+ version: 2.0.1
+ resolution: "universalify@npm:2.0.1"
+ checksum: 10c0/73e8ee3809041ca8b818efb141801a1004e3fc0002727f1531f4de613ea281b494a40909596dae4a042a4fb6cd385af5d4db2e137b1362e0e91384b828effd3a
+ languageName: node
+ linkType: hard
+
"util-deprecate@npm:^1.0.1":
version: 1.0.2
resolution: "util-deprecate@npm:1.0.2"
@@ -2674,18 +3235,61 @@ __metadata:
"@fastify/leveldb": "npm:^6.0.0"
"@fastify/static": "npm:^8.0.0"
"@fastify/view": "npm:^10.0.0"
- "@prettier/plugin-pug": "npm:^3.0.0"
"@types/node": "npm:^22.13.5"
- axios: "npm:^1.7.9"
esbuild: "npm:^0.25.0"
- fastify: "npm:^5.0.0"
+ esbuild-plugin-sass: "npm:^1.0.1"
+ fastify: "npm:^5.2.1"
+ jsdom: "npm:^26.0.0"
nodemon: "npm:^3.0.1"
- pretier: "npm:^0.0.1"
prettier: "npm:^3.5.2"
pug: "npm:^3.0.2"
+ sass: "npm:^1.85.1"
+ undici: "npm:^7.3.0"
languageName: unknown
linkType: soft
+"w3c-xmlserializer@npm:^5.0.0":
+ version: 5.0.0
+ resolution: "w3c-xmlserializer@npm:5.0.0"
+ dependencies:
+ xml-name-validator: "npm:^5.0.0"
+ checksum: 10c0/8712774c1aeb62dec22928bf1cdfd11426c2c9383a1a63f2bcae18db87ca574165a0fbe96b312b73652149167ac6c7f4cf5409f2eb101d9c805efe0e4bae798b
+ languageName: node
+ linkType: hard
+
+"webidl-conversions@npm:^7.0.0":
+ version: 7.0.0
+ resolution: "webidl-conversions@npm:7.0.0"
+ checksum: 10c0/228d8cb6d270c23b0720cb2d95c579202db3aaf8f633b4e9dd94ec2000a04e7e6e43b76a94509cdb30479bd00ae253ab2371a2da9f81446cc313f89a4213a2c4
+ languageName: node
+ linkType: hard
+
+"whatwg-encoding@npm:^3.1.1":
+ version: 3.1.1
+ resolution: "whatwg-encoding@npm:3.1.1"
+ dependencies:
+ iconv-lite: "npm:0.6.3"
+ checksum: 10c0/273b5f441c2f7fda3368a496c3009edbaa5e43b71b09728f90425e7f487e5cef9eb2b846a31bd760dd8077739c26faf6b5ca43a5f24033172b003b72cf61a93e
+ languageName: node
+ linkType: hard
+
+"whatwg-mimetype@npm:^4.0.0":
+ version: 4.0.0
+ resolution: "whatwg-mimetype@npm:4.0.0"
+ checksum: 10c0/a773cdc8126b514d790bdae7052e8bf242970cebd84af62fb2f35a33411e78e981f6c0ab9ed1fe6ec5071b09d5340ac9178e05b52d35a9c4bcf558ba1b1551df
+ languageName: node
+ linkType: hard
+
+"whatwg-url@npm:^14.0.0, whatwg-url@npm:^14.1.0":
+ version: 14.1.1
+ resolution: "whatwg-url@npm:14.1.1"
+ dependencies:
+ tr46: "npm:^5.0.0"
+ webidl-conversions: "npm:^7.0.0"
+ checksum: 10c0/de1e9cc2f04cb000f232c839d4999384ba41b680ef8a89e7c61c9bc40354ba8593c775d068faaf0819f5866e4d6ca3e7b9b386e2123aa475bfd33da02316f476
+ languageName: node
+ linkType: hard
+
"which@npm:^2.0.1":
version: 2.0.2
resolution: "which@npm:2.0.2"
@@ -2742,6 +3346,42 @@ __metadata:
languageName: node
linkType: hard
+"wrappy@npm:1":
+ version: 1.0.2
+ resolution: "wrappy@npm:1.0.2"
+ checksum: 10c0/56fece1a4018c6a6c8e28fbc88c87e0fbf4ea8fd64fc6c63b18f4acc4bd13e0ad2515189786dd2c30d3eec9663d70f4ecf699330002f8ccb547e4a18231fc9f0
+ languageName: node
+ linkType: hard
+
+"ws@npm:^8.18.0":
+ version: 8.18.1
+ resolution: "ws@npm:8.18.1"
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: ">=5.0.2"
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+ checksum: 10c0/e498965d6938c63058c4310ffb6967f07d4fa06789d3364829028af380d299fe05762961742971c764973dce3d1f6a2633fe8b2d9410c9b52e534b4b882a99fa
+ languageName: node
+ linkType: hard
+
+"xml-name-validator@npm:^5.0.0":
+ version: 5.0.0
+ resolution: "xml-name-validator@npm:5.0.0"
+ checksum: 10c0/3fcf44e7b73fb18be917fdd4ccffff3639373c7cb83f8fc35df6001fecba7942f1dbead29d91ebb8315e2f2ff786b508f0c9dc0215b6353f9983c6b7d62cb1f5
+ languageName: node
+ linkType: hard
+
+"xmlchars@npm:^2.2.0":
+ version: 2.2.0
+ resolution: "xmlchars@npm:2.2.0"
+ checksum: 10c0/b64b535861a6f310c5d9bfa10834cf49127c71922c297da9d4d1b45eeaae40bf9b4363275876088fbe2667e5db028d2cd4f8ee72eed9bede840a67d57dab7593
+ languageName: node
+ linkType: hard
+
"yallist@npm:^4.0.0":
version: 4.0.0
resolution: "yallist@npm:4.0.0"