diff options
-rw-r--r-- | scripts/lib.js | 30 | ||||
-rw-r--r-- | scripts/main.js | 46 |
2 files changed, 71 insertions, 5 deletions
diff --git a/scripts/lib.js b/scripts/lib.js index 4e4850e..9a5e957 100644 --- a/scripts/lib.js +++ b/scripts/lib.js | |||
@@ -2,9 +2,39 @@ const h = preact.h; | |||
2 | const CELL_SIZE = 32; | 2 | const CELL_SIZE = 32; |
3 | const GRID_SIZE_X = 21; | 3 | const GRID_SIZE_X = 21; |
4 | const GRID_SIZE_Y = 21; | 4 | const GRID_SIZE_Y = 21; |
5 | const FLOOR_COUNT = 15; | ||
5 | 6 | ||
6 | const ASSET_SVG_PROPS = {"width": CELL_SIZE, "height": CELL_SIZE, | 7 | const ASSET_SVG_PROPS = {"width": CELL_SIZE, "height": CELL_SIZE, |
7 | "version": "1.1"}; | 8 | "version": "1.1"}; |
8 | const ASSET_WHITE_BG = h("rect", {"x": 0, "y": 0, "width": CELL_SIZE, | 9 | const ASSET_WHITE_BG = h("rect", {"x": 0, "y": 0, "width": CELL_SIZE, |
9 | "height": CELL_SIZE, "fill": "white", | 10 | "height": CELL_SIZE, "fill": "white", |
10 | "stroke-width": 0 }); | 11 | "stroke-width": 0 }); |
12 | |||
13 | // Bytes is a Uint8Array | ||
14 | async function compressBytes(bytes) | ||
15 | { | ||
16 | let blob = new Blob([bytes]); | ||
17 | const ds = new CompressionStream("deflate"); | ||
18 | const compressed = blob.stream().pipeThrough(ds); | ||
19 | let compressed_blob = await new Response(compressed).blob(); | ||
20 | const reader = new FileReader(); | ||
21 | return new Promise((resolve, _) => { | ||
22 | reader.onloadend = (event) => { | ||
23 | const result = event.target.result; | ||
24 | resolve(result.replace(/^data:.+;base64,/, '') | ||
25 | .replaceAll("/", "-").replaceAll("+", "_")); | ||
26 | } | ||
27 | reader.readAsDataURL(compressed_blob); | ||
28 | }); | ||
29 | } | ||
30 | |||
31 | // Decompress into Uint8Array | ||
32 | function decompressBytes(s) | ||
33 | { | ||
34 | const decoded = window.atob(s.replaceAll("_", "+").replaceAll("-", "/")); | ||
35 | const decoded_array = Uint8Array.from(decoded, c => c.charCodeAt(0)); | ||
36 | let blob = new Blob([decoded_array]); | ||
37 | const cs = new DecompressionStream("deflate"); | ||
38 | const decompressed = blob.stream().pipeThrough(cs); | ||
39 | return new Response(decompressed).bytes(); | ||
40 | } | ||
diff --git a/scripts/main.js b/scripts/main.js index 01301a1..7347f37 100644 --- a/scripts/main.js +++ b/scripts/main.js | |||
@@ -3,8 +3,15 @@ const DEFAULT_APP_STATE = { | |||
3 | plan: new Array(GRID_SIZE_X * GRID_SIZE_Y).fill(0), | 3 | plan: new Array(GRID_SIZE_X * GRID_SIZE_Y).fill(0), |
4 | // mouse_state can be “normal”, or “dnd”. | 4 | // mouse_state can be “normal”, or “dnd”. |
5 | mouse_state: "normal", | 5 | mouse_state: "normal", |
6 | plan_code: "", | ||
6 | }; | 7 | }; |
7 | 8 | ||
9 | async function serializePlan(app_state) | ||
10 | { | ||
11 | let rest = new Array(GRID_SIZE_X * GRID_SIZE_Y * (FLOOR_COUNT - 1)).fill(0); | ||
12 | return compressBytes(Uint8Array.from(app_state.plan.concat(rest))); | ||
13 | } | ||
14 | |||
8 | function genGrid(x_count, y_count, cell_size) | 15 | function genGrid(x_count, y_count, cell_size) |
9 | { | 16 | { |
10 | let lines = []; | 17 | let lines = []; |
@@ -62,7 +69,6 @@ function PlanGridView({content, mouse_state}) | |||
62 | } | 69 | } |
63 | 70 | ||
64 | let hilight_box = null; | 71 | let hilight_box = null; |
65 | console.debug(mouse_coord); | ||
66 | 72 | ||
67 | if(mouse_coord !== null && mouse_state != "dnd") | 73 | if(mouse_coord !== null && mouse_state != "dnd") |
68 | { | 74 | { |
@@ -130,7 +136,13 @@ function App({initial_state}) | |||
130 | let new_state = structuredClone(state); | 136 | let new_state = structuredClone(state); |
131 | new_state.plan[cell_index] = asset_index; | 137 | new_state.plan[cell_index] = asset_index; |
132 | new_state.mouse_state = "normal"; | 138 | new_state.mouse_state = "normal"; |
133 | setState(new_state); | 139 | serializePlan(new_state).then((s) => { |
140 | new_state.plan_code = s; | ||
141 | setState(new_state); | ||
142 | const url = new URL(location); | ||
143 | url.searchParams.set("plan", s); | ||
144 | window.history.pushState({}, "", url); | ||
145 | }); | ||
134 | } | 146 | } |
135 | 147 | ||
136 | function onClickSaveImg() | 148 | function onClickSaveImg() |
@@ -157,13 +169,37 @@ function App({initial_state}) | |||
157 | } | 169 | } |
158 | 170 | ||
159 | return h("div", {}, | 171 | return h("div", {}, |
172 | h("h2", {}, `Floor ${state.floor}`), | ||
160 | h(AssetsView, {on_drag_begin: onDragAssetBegin, | 173 | h(AssetsView, {on_drag_begin: onDragAssetBegin, |
161 | on_drag_end: onDragAssetEnd}), | 174 | on_drag_end: onDragAssetEnd}), |
162 | h(PlanView, {plan: state.plan, mouse_state: state.mouse_state}), | 175 | h(PlanView, {plan: state.plan, mouse_state: state.mouse_state}), |
163 | h("div", {}, | 176 | h("div", {}, |
164 | h("a", {"href": "javascript:void(0);", onclick: onClickSaveImg}, | 177 | h("a", {"href": "javascript:void(0);", onclick: onClickSaveImg}, |
165 | "Open Image!"))); | 178 | "Open Image!")), |
179 | h("div", {}, | ||
180 | h("textarea", {readonly: true}, state.plan_code)), | ||
181 | ); | ||
166 | } | 182 | } |
167 | 183 | ||
168 | preact.render(h(App, {initial_state: DEFAULT_APP_STATE}), | 184 | function render(app_state) |
169 | document.getElementById("Plan")); | 185 | { |
186 | preact.render(h(App, {initial_state: app_state}), | ||
187 | document.getElementById("Plan")); | ||
188 | } | ||
189 | |||
190 | const paramsString = window.location.search; | ||
191 | const searchParams = new URLSearchParams(paramsString); | ||
192 | const encoded_plan = searchParams.get("plan"); | ||
193 | if(encoded_plan === null) | ||
194 | { | ||
195 | render(DEFAULT_APP_STATE); | ||
196 | } | ||
197 | else | ||
198 | { | ||
199 | decompressBytes(encoded_plan).then(a => { | ||
200 | let state = structuredClone(DEFAULT_APP_STATE); | ||
201 | state.plan = Array.from(a.subarray(0, GRID_SIZE_X * GRID_SIZE_Y)); | ||
202 | state.plan_code = encoded_plan; | ||
203 | render(state); | ||
204 | }); | ||
205 | } | ||