├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── Makefile ├── README.md ├── docs ├── index.html ├── kp-chart.js ├── kp-chart.wasm └── styles.css ├── js └── index.js ├── package-lock.json ├── package.json ├── src ├── data │ ├── day.rs │ ├── job.rs │ ├── mod.rs │ ├── person.rs │ └── week.rs ├── lib.rs └── web │ ├── chart.rs │ ├── mod.rs │ ├── people.rs │ └── root.rs ├── static ├── index.html └── styles.css └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /pkg 3 | /node_modules 4 | .cache/ 5 | dist/ 6 | **/*.rs.bk 7 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "anyhow" 5 | version = "1.0.27" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | 8 | [[package]] 9 | name = "anymap" 10 | version = "0.12.1" 11 | source = "registry+https://github.com/rust-lang/crates.io-index" 12 | 13 | [[package]] 14 | name = "autocfg" 15 | version = "1.0.0" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | 18 | [[package]] 19 | name = "bincode" 20 | version = "1.2.1" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | dependencies = [ 23 | "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 24 | "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", 25 | ] 26 | 27 | [[package]] 28 | name = "boolinator" 29 | version = "2.4.0" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | 32 | [[package]] 33 | name = "bumpalo" 34 | version = "3.2.1" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | 37 | [[package]] 38 | name = "byteorder" 39 | version = "1.3.4" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | 42 | [[package]] 43 | name = "bytes" 44 | version = "0.5.4" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | 47 | [[package]] 48 | name = "cfg-if" 49 | version = "0.1.10" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | 52 | [[package]] 53 | name = "cfg-match" 54 | version = "0.2.1" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | 57 | [[package]] 58 | name = "console_error_panic_hook" 59 | version = "0.1.6" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | dependencies = [ 62 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 63 | "wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", 64 | ] 65 | 66 | [[package]] 67 | name = "console_log" 68 | version = "0.1.2" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | dependencies = [ 71 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 72 | "web-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)", 73 | ] 74 | 75 | [[package]] 76 | name = "dtoa" 77 | version = "0.4.3" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | 80 | [[package]] 81 | name = "fnv" 82 | version = "1.0.6" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | 85 | [[package]] 86 | name = "futures" 87 | version = "0.3.4" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | dependencies = [ 90 | "futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 91 | "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 92 | "futures-executor 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 93 | "futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 94 | "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 95 | "futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 96 | "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 97 | ] 98 | 99 | [[package]] 100 | name = "futures-channel" 101 | version = "0.3.4" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | dependencies = [ 104 | "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 105 | "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 106 | ] 107 | 108 | [[package]] 109 | name = "futures-core" 110 | version = "0.3.4" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | 113 | [[package]] 114 | name = "futures-executor" 115 | version = "0.3.4" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | dependencies = [ 118 | "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 119 | "futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 120 | "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 121 | ] 122 | 123 | [[package]] 124 | name = "futures-io" 125 | version = "0.3.4" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | 128 | [[package]] 129 | name = "futures-macro" 130 | version = "0.3.4" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | dependencies = [ 133 | "proc-macro-hack 0.5.14 (registry+https://github.com/rust-lang/crates.io-index)", 134 | "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", 135 | "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 136 | "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", 137 | ] 138 | 139 | [[package]] 140 | name = "futures-sink" 141 | version = "0.3.4" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | 144 | [[package]] 145 | name = "futures-task" 146 | version = "0.3.4" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | 149 | [[package]] 150 | name = "futures-util" 151 | version = "0.3.4" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | dependencies = [ 154 | "futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 155 | "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 156 | "futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 157 | "futures-macro 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 158 | "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 159 | "futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 160 | "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 161 | "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", 162 | "proc-macro-hack 0.5.14 (registry+https://github.com/rust-lang/crates.io-index)", 163 | "proc-macro-nested 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 164 | "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 165 | ] 166 | 167 | [[package]] 168 | name = "gloo" 169 | version = "0.2.1" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | dependencies = [ 172 | "gloo-console-timer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 173 | "gloo-events 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 174 | "gloo-file 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 175 | "gloo-timers 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 176 | ] 177 | 178 | [[package]] 179 | name = "gloo-console-timer" 180 | version = "0.1.0" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | dependencies = [ 183 | "web-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)", 184 | ] 185 | 186 | [[package]] 187 | name = "gloo-events" 188 | version = "0.1.1" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | dependencies = [ 191 | "wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", 192 | "web-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)", 193 | ] 194 | 195 | [[package]] 196 | name = "gloo-file" 197 | version = "0.1.0" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | dependencies = [ 200 | "gloo-events 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 201 | "js-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)", 202 | "wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", 203 | "web-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)", 204 | ] 205 | 206 | [[package]] 207 | name = "gloo-timers" 208 | version = "0.2.1" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | dependencies = [ 211 | "js-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)", 212 | "wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", 213 | "web-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)", 214 | ] 215 | 216 | [[package]] 217 | name = "http" 218 | version = "0.2.0" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | dependencies = [ 221 | "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", 222 | "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 223 | "itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 224 | ] 225 | 226 | [[package]] 227 | name = "indexmap" 228 | version = "1.3.2" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | dependencies = [ 231 | "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 232 | ] 233 | 234 | [[package]] 235 | name = "itoa" 236 | version = "0.4.2" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | 239 | [[package]] 240 | name = "js-sys" 241 | version = "0.3.36" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | dependencies = [ 244 | "wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", 245 | ] 246 | 247 | [[package]] 248 | name = "kp-chart" 249 | version = "0.2.0" 250 | dependencies = [ 251 | "console_log 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 252 | "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 253 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 254 | "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", 255 | "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", 256 | "serde_json 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", 257 | "wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", 258 | "wasm-bindgen-futures 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", 259 | "web-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)", 260 | "yew 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", 261 | ] 262 | 263 | [[package]] 264 | name = "lazy_static" 265 | version = "1.4.0" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | 268 | [[package]] 269 | name = "log" 270 | version = "0.4.8" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | dependencies = [ 273 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 274 | ] 275 | 276 | [[package]] 277 | name = "memchr" 278 | version = "2.3.3" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | 281 | [[package]] 282 | name = "pin-utils" 283 | version = "0.1.0-alpha.4" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | 286 | [[package]] 287 | name = "proc-macro-hack" 288 | version = "0.5.14" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | 291 | [[package]] 292 | name = "proc-macro-nested" 293 | version = "0.1.4" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | 296 | [[package]] 297 | name = "proc-macro2" 298 | version = "0.4.9" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | dependencies = [ 301 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 302 | ] 303 | 304 | [[package]] 305 | name = "proc-macro2" 306 | version = "1.0.9" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | dependencies = [ 309 | "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 310 | ] 311 | 312 | [[package]] 313 | name = "quote" 314 | version = "0.6.4" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | dependencies = [ 317 | "proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", 318 | ] 319 | 320 | [[package]] 321 | name = "quote" 322 | version = "1.0.3" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | dependencies = [ 325 | "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", 326 | ] 327 | 328 | [[package]] 329 | name = "ryu" 330 | version = "1.0.3" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | 333 | [[package]] 334 | name = "serde" 335 | version = "1.0.70" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | dependencies = [ 338 | "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", 339 | ] 340 | 341 | [[package]] 342 | name = "serde_derive" 343 | version = "1.0.70" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | dependencies = [ 346 | "proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", 347 | "quote 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", 348 | "syn 0.14.5 (registry+https://github.com/rust-lang/crates.io-index)", 349 | ] 350 | 351 | [[package]] 352 | name = "serde_json" 353 | version = "1.0.24" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | dependencies = [ 356 | "dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 357 | "itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 358 | "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", 359 | ] 360 | 361 | [[package]] 362 | name = "slab" 363 | version = "0.4.2" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | 366 | [[package]] 367 | name = "syn" 368 | version = "0.14.5" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | dependencies = [ 371 | "proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", 372 | "quote 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", 373 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 374 | ] 375 | 376 | [[package]] 377 | name = "syn" 378 | version = "1.0.17" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | dependencies = [ 381 | "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", 382 | "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 383 | "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 384 | ] 385 | 386 | [[package]] 387 | name = "thiserror" 388 | version = "1.0.13" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | dependencies = [ 391 | "thiserror-impl 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", 392 | ] 393 | 394 | [[package]] 395 | name = "thiserror-impl" 396 | version = "1.0.13" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | dependencies = [ 399 | "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", 400 | "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 401 | "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", 402 | ] 403 | 404 | [[package]] 405 | name = "unicode-xid" 406 | version = "0.1.0" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | 409 | [[package]] 410 | name = "unicode-xid" 411 | version = "0.2.0" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | 414 | [[package]] 415 | name = "wasm-bindgen" 416 | version = "0.2.59" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | dependencies = [ 419 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 420 | "wasm-bindgen-macro 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", 421 | ] 422 | 423 | [[package]] 424 | name = "wasm-bindgen-backend" 425 | version = "0.2.59" 426 | source = "registry+https://github.com/rust-lang/crates.io-index" 427 | dependencies = [ 428 | "bumpalo 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 429 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 430 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 431 | "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", 432 | "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 433 | "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", 434 | "wasm-bindgen-shared 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", 435 | ] 436 | 437 | [[package]] 438 | name = "wasm-bindgen-futures" 439 | version = "0.4.9" 440 | source = "registry+https://github.com/rust-lang/crates.io-index" 441 | dependencies = [ 442 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 443 | "js-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)", 444 | "wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", 445 | "web-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)", 446 | ] 447 | 448 | [[package]] 449 | name = "wasm-bindgen-macro" 450 | version = "0.2.59" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | dependencies = [ 453 | "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 454 | "wasm-bindgen-macro-support 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", 455 | ] 456 | 457 | [[package]] 458 | name = "wasm-bindgen-macro-support" 459 | version = "0.2.59" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | dependencies = [ 462 | "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", 463 | "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 464 | "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", 465 | "wasm-bindgen-backend 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", 466 | "wasm-bindgen-shared 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", 467 | ] 468 | 469 | [[package]] 470 | name = "wasm-bindgen-shared" 471 | version = "0.2.59" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | 474 | [[package]] 475 | name = "web-sys" 476 | version = "0.3.36" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | dependencies = [ 479 | "js-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)", 480 | "wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", 481 | ] 482 | 483 | [[package]] 484 | name = "yew" 485 | version = "0.13.2" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | dependencies = [ 488 | "anyhow 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", 489 | "anymap 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", 490 | "bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 491 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 492 | "cfg-match 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 493 | "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 494 | "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 495 | "gloo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 496 | "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 497 | "indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 498 | "js-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)", 499 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 500 | "proc-macro-hack 0.5.14 (registry+https://github.com/rust-lang/crates.io-index)", 501 | "proc-macro-nested 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 502 | "ryu 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 503 | "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", 504 | "serde_json 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", 505 | "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 506 | "thiserror 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", 507 | "wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", 508 | "wasm-bindgen-futures 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", 509 | "web-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)", 510 | "yew-macro 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", 511 | ] 512 | 513 | [[package]] 514 | name = "yew-macro" 515 | version = "0.13.0" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | dependencies = [ 518 | "boolinator 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 519 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 520 | "proc-macro-hack 0.5.14 (registry+https://github.com/rust-lang/crates.io-index)", 521 | "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", 522 | "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 523 | "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", 524 | ] 525 | 526 | [metadata] 527 | "checksum anyhow 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "013a6e0a2cbe3d20f9c60b65458f7a7f7a5e636c5d0f45a5a6aee5d4b1f01785" 528 | "checksum anymap 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344" 529 | "checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 530 | "checksum bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf" 531 | "checksum boolinator 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9" 532 | "checksum bumpalo 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" 533 | "checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 534 | "checksum bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" 535 | "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 536 | "checksum cfg-match 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8100e46ff92eb85bf6dc2930c73f2a4f7176393c84a9446b3d501e1b354e7b34" 537 | "checksum console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211" 538 | "checksum console_log 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1e7871d2947441b0fdd8e2bd1ce2a2f75304f896582c0d572162d48290683c48" 539 | "checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd" 540 | "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" 541 | "checksum futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780" 542 | "checksum futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8" 543 | "checksum futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" 544 | "checksum futures-executor 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba" 545 | "checksum futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" 546 | "checksum futures-macro 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7" 547 | "checksum futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6" 548 | "checksum futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" 549 | "checksum futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" 550 | "checksum gloo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "68ce6f2dfa9f57f15b848efa2aade5e1850dc72986b87a2b0752d44ca08f4967" 551 | "checksum gloo-console-timer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b48675544b29ac03402c6dffc31a912f716e38d19f7e74b78b7e900ec3c941ea" 552 | "checksum gloo-events 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "088514ec8ef284891c762c88a66b639b3a730134714692ee31829765c5bc814f" 553 | "checksum gloo-file 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f9fecfe46b5dc3cc46f58e98ba580cc714f2c93860796d002eb3527a465ef49" 554 | "checksum gloo-timers 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f" 555 | "checksum http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b708cc7f06493459026f53b9a61a7a121a5d1ec6238dee58ea4941132b30156b" 556 | "checksum indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" 557 | "checksum itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5adb58558dcd1d786b5f0bd15f3226ee23486e24b7b58304b60f64dc68e62606" 558 | "checksum js-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1cb931d43e71f560c81badb0191596562bafad2be06a3f9025b845c847c60df5" 559 | "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 560 | "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 561 | "checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 562 | "checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" 563 | "checksum proc-macro-hack 0.5.14 (registry+https://github.com/rust-lang/crates.io-index)" = "fcfdefadc3d57ca21cf17990a28ef4c0f7c61383a28cb7604cf4a18e6ede1420" 564 | "checksum proc-macro-nested 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694" 565 | "checksum proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "cccdc7557a98fe98453030f077df7f3a042052fae465bb61d2c2c41435cfd9b6" 566 | "checksum proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" 567 | "checksum quote 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b71f9f575d55555aa9c06188be9d4e2bfc83ed02537948ac0d520c24d0419f1a" 568 | "checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" 569 | "checksum ryu 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76" 570 | "checksum serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)" = "0c3adf19c07af6d186d91dae8927b83b0553d07ca56cbf7f2f32560455c91920" 571 | "checksum serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)" = "3525a779832b08693031b8ecfb0de81cd71cfd3812088fafe9a7496789572124" 572 | "checksum serde_json 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c6908c7b925cd6c590358a4034de93dbddb20c45e1d021931459fd419bf0e2" 573 | "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 574 | "checksum syn 0.14.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4bad7abdf6633f07c7046b90484f1d9dc055eca39f8c991177b1046ce61dba9a" 575 | "checksum syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" 576 | "checksum thiserror 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e3711fd1c4e75b3eff12ba5c40dba762b6b65c5476e8174c1a664772060c49bf" 577 | "checksum thiserror-impl 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "ae2b85ba4c9aa32dd3343bd80eb8d22e9b54b7688c17ea3907f236885353b233" 578 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 579 | "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 580 | "checksum wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "3557c397ab5a8e347d434782bcd31fc1483d927a6826804cec05cc792ee2519d" 581 | "checksum wasm-bindgen-backend 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "e0da9c9a19850d3af6df1cb9574970b566d617ecfaf36eb0b706b6f3ef9bd2f8" 582 | "checksum wasm-bindgen-futures 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "457414a91863c0ec00090dba537f88ab955d93ca6555862c29b6d860990b8a8a" 583 | "checksum wasm-bindgen-macro 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "0f6fde1d36e75a714b5fe0cffbb78978f222ea6baebb726af13c78869fdb4205" 584 | "checksum wasm-bindgen-macro-support 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "25bda4168030a6412ea8a047e27238cadf56f0e53516e1e83fec0a8b7c786f6d" 585 | "checksum wasm-bindgen-shared 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "fc9f36ad51f25b0219a3d4d13b90eb44cd075dff8b6280cca015775d7acaddd8" 586 | "checksum web-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)" = "721c6263e2c66fd44501cc5efbfa2b7dfa775d13e4ea38c46299646ed1f9c70a" 587 | "checksum yew 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "35e21ea7b89b427fe0db9922cad08f51e5d9464434b3843414b253008b7cc0d1" 588 | "checksum yew-macro 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7799777bca19f96d9eb0c865e53c489565ffb890b9d48b58c9c50ef5c8df3532" 589 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kp-chart" 3 | version = "0.2.0" 4 | authors = ["Benjamin Fry "] 5 | edition = "2018" 6 | 7 | [lib] 8 | name = "kp_chart" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | console_log = "0.1.2" 13 | futures = "0.3.4" 14 | log = "0.4.8" 15 | serde = "1.0" 16 | serde_derive = "1.0" 17 | serde_json = "1.0" 18 | web-sys = { version = "0.3.36", features = ['Document', 'Element', 'HtmlElement', 'Node', 'Window', 'RtcDataChannel', 'RtcDataChannelInit', 'RtcPeerConnection', 'RtcSessionDescription', 'RtcSessionDescriptionInit', 'RtcSdpType', 'RtcOfferOptions', 'RtcConfiguration', 'RtcIceTransportPolicy'] } 19 | wasm-bindgen = "0.2" 20 | wasm-bindgen-futures = "0.4.9" 21 | yew = { version = "0.13.0", features = ["web_sys"] } 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET_DIR ?= ./target 2 | CARGO_TARGET_DIR := ${TARGET_DIR} 3 | MODE ?= development 4 | RUSTFLAGS := -Ctarget-cpu=generic 5 | TMP_DIST_DIR := /tmp/kp-chart-dist 6 | 7 | .PHONY: init 8 | init: 9 | @echo "========> $@" 10 | @rustup --version || (curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh) 11 | rustup self update 12 | rustup update 13 | rustup target add wasm32-unknown-unknown 14 | cargo install wasm-pack 15 | npm --version 16 | npm install 17 | 18 | .PHONY: build 19 | wasm: 20 | @echo "========> $@" 21 | wasm-pack build 22 | 23 | .PHONY: build 24 | build: 25 | @echo "========> $@" 26 | npm run build -- --mode=${MODE} 27 | 28 | .PHONY: test 29 | test: build 30 | @echo "========> $@" 31 | cargo test 32 | npm run test 33 | 34 | .PHONY: run 35 | run: build 36 | @echo "========> $@" 37 | npm run start 38 | 39 | .PHONY: clean 40 | clean: 41 | @echo "========> $@" 42 | rm -rf ./pkg 43 | rm -rf ./dist 44 | 45 | 46 | .PHONY: deploy 47 | deploy: clean 48 | @echo "========> $@" 49 | @git --version 50 | 51 | # build the project 52 | $(MAKE) MODE=production WASM_MODE=--release build 53 | 54 | # deploy 55 | git worktree add ${TMP_DIST_DIR} gh-pages 56 | rm -rf ${TMP_DIST_DIR}/* 57 | cp -rp dist/* ${TMP_DIST_DIR} 58 | cd ${TMP_DIST_DIR} && \ 59 | git add -A && \ 60 | git diff --staged --quiet || \ 61 | (git commit -m "deployed on $(shell date) by ${USER}" && \ 62 | git push origin gh-pages) 63 | $(MAKE) clean_worktree 64 | 65 | .PHONY: clean_worktree 66 | clean_worktree: 67 | @echo "========> $@" 68 | rm -rf ${TMP_DIST_DIR} 69 | git worktree prune 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kitchen Patrol Charting Application 2 | 3 | An example application, written in Rust, by someone with little frontend experience 4 | 5 | ## Usage 6 | 7 | Browse to [https://bluejekyll.github.io/kp-chart/](https://bluejekyll.github.io/kp-chart/) 8 | 9 | ## Building 10 | 11 | - Initililize once 12 | 13 | ```console 14 | $> make init 15 | ``` 16 | 17 | - Build 18 | 19 | ```console 20 | $> make build 21 | ``` 22 | 23 | - Start a local appserver 24 | 25 | ```console 26 | $> make run 27 | ``` 28 | 29 | ## Deploying 30 | 31 | ```console 32 | $> make deploy 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Kitchen Patrol 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /docs/kp-chart.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | if( typeof Rust === "undefined" ) { 4 | var Rust = {}; 5 | } 6 | 7 | (function( root, factory ) { 8 | if( typeof define === "function" && define.amd ) { 9 | define( [], factory ); 10 | } else if( typeof module === "object" && module.exports ) { 11 | module.exports = factory(); 12 | } else { 13 | Rust.kp_chart = factory(); 14 | } 15 | }( this, function() { 16 | return (function( module_factory ) { 17 | var instance = module_factory(); 18 | 19 | if( typeof window === "undefined" && typeof process === "object" ) { 20 | var fs = require( "fs" ); 21 | var path = require( "path" ); 22 | var wasm_path = path.join( __dirname, "kp-chart.wasm" ); 23 | var buffer = fs.readFileSync( wasm_path ); 24 | var mod = new WebAssembly.Module( buffer ); 25 | var wasm_instance = new WebAssembly.Instance( mod, instance.imports ); 26 | return instance.initialize( wasm_instance ); 27 | } else { 28 | return fetch( "kp-chart.wasm", {credentials: "same-origin"} ) 29 | .then( function( response ) { return response.arrayBuffer(); } ) 30 | .then( function( bytes ) { return WebAssembly.compile( bytes ); } ) 31 | .then( function( mod ) { return WebAssembly.instantiate( mod, instance.imports ) } ) 32 | .then( function( wasm_instance ) { 33 | var exports = instance.initialize( wasm_instance ); 34 | console.log( "Finished loading Rust wasm module 'kp_chart'" ); 35 | return exports; 36 | }) 37 | .catch( function( error ) { 38 | console.log( "Error loading Rust wasm module 'kp_chart':", error ); 39 | throw error; 40 | }); 41 | } 42 | }( function() { 43 | var Module = {}; 44 | 45 | Module.STDWEB_PRIVATE = {}; 46 | 47 | // This is based on code from Emscripten's preamble.js. 48 | Module.STDWEB_PRIVATE.to_utf8 = function to_utf8( str, addr ) { 49 | for( var i = 0; i < str.length; ++i ) { 50 | // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8. 51 | // See http://unicode.org/faq/utf_bom.html#utf16-3 52 | // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description and https://www.ietf.org/rfc/rfc2279.txt and https://tools.ietf.org/html/rfc3629 53 | var u = str.charCodeAt( i ); // possibly a lead surrogate 54 | if( u >= 0xD800 && u <= 0xDFFF ) { 55 | u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt( ++i ) & 0x3FF); 56 | } 57 | 58 | if( u <= 0x7F ) { 59 | HEAPU8[ addr++ ] = u; 60 | } else if( u <= 0x7FF ) { 61 | HEAPU8[ addr++ ] = 0xC0 | (u >> 6); 62 | HEAPU8[ addr++ ] = 0x80 | (u & 63); 63 | } else if( u <= 0xFFFF ) { 64 | HEAPU8[ addr++ ] = 0xE0 | (u >> 12); 65 | HEAPU8[ addr++ ] = 0x80 | ((u >> 6) & 63); 66 | HEAPU8[ addr++ ] = 0x80 | (u & 63); 67 | } else if( u <= 0x1FFFFF ) { 68 | HEAPU8[ addr++ ] = 0xF0 | (u >> 18); 69 | HEAPU8[ addr++ ] = 0x80 | ((u >> 12) & 63); 70 | HEAPU8[ addr++ ] = 0x80 | ((u >> 6) & 63); 71 | HEAPU8[ addr++ ] = 0x80 | (u & 63); 72 | } else if( u <= 0x3FFFFFF ) { 73 | HEAPU8[ addr++ ] = 0xF8 | (u >> 24); 74 | HEAPU8[ addr++ ] = 0x80 | ((u >> 18) & 63); 75 | HEAPU8[ addr++ ] = 0x80 | ((u >> 12) & 63); 76 | HEAPU8[ addr++ ] = 0x80 | ((u >> 6) & 63); 77 | HEAPU8[ addr++ ] = 0x80 | (u & 63); 78 | } else { 79 | HEAPU8[ addr++ ] = 0xFC | (u >> 30); 80 | HEAPU8[ addr++ ] = 0x80 | ((u >> 24) & 63); 81 | HEAPU8[ addr++ ] = 0x80 | ((u >> 18) & 63); 82 | HEAPU8[ addr++ ] = 0x80 | ((u >> 12) & 63); 83 | HEAPU8[ addr++ ] = 0x80 | ((u >> 6) & 63); 84 | HEAPU8[ addr++ ] = 0x80 | (u & 63); 85 | } 86 | } 87 | }; 88 | 89 | Module.STDWEB_PRIVATE.noop = function() {}; 90 | Module.STDWEB_PRIVATE.to_js = function to_js( address ) { 91 | var kind = HEAPU8[ address + 12 ]; 92 | if( kind === 0 ) { 93 | return undefined; 94 | } else if( kind === 1 ) { 95 | return null; 96 | } else if( kind === 2 ) { 97 | return HEAP32[ address / 4 ]; 98 | } else if( kind === 3 ) { 99 | return HEAPF64[ address / 8 ]; 100 | } else if( kind === 4 ) { 101 | var pointer = HEAPU32[ address / 4 ]; 102 | var length = HEAPU32[ (address + 4) / 4 ]; 103 | return Module.STDWEB_PRIVATE.to_js_string( pointer, length ); 104 | } else if( kind === 5 ) { 105 | return false; 106 | } else if( kind === 6 ) { 107 | return true; 108 | } else if( kind === 7 ) { 109 | var pointer = HEAPU32[ address / 4 ]; 110 | var length = HEAPU32[ (address + 4) / 4 ]; 111 | var output = []; 112 | for( var i = 0; i < length; ++i ) { 113 | output.push( Module.STDWEB_PRIVATE.to_js( pointer + i * 16 ) ); 114 | } 115 | return output; 116 | } else if( kind === 8 ) { 117 | var value_array_pointer = HEAPU32[ address / 4 ]; 118 | var length = HEAPU32[ (address + 4) / 4 ]; 119 | var key_array_pointer = HEAPU32[ (address + 8) / 4 ]; 120 | var output = {}; 121 | for( var i = 0; i < length; ++i ) { 122 | var key_pointer = HEAPU32[ (key_array_pointer + i * 8) / 4 ]; 123 | var key_length = HEAPU32[ (key_array_pointer + 4 + i * 8) / 4 ]; 124 | var key = Module.STDWEB_PRIVATE.to_js_string( key_pointer, key_length ); 125 | var value = Module.STDWEB_PRIVATE.to_js( value_array_pointer + i * 16 ); 126 | output[ key ] = value; 127 | } 128 | return output; 129 | } else if( kind === 9 ) { 130 | return Module.STDWEB_PRIVATE.acquire_js_reference( HEAP32[ address / 4 ] ); 131 | } else if( kind === 10 ) { 132 | var adapter_pointer = HEAPU32[ address / 4 ]; 133 | var pointer = HEAPU32[ (address + 4) / 4 ]; 134 | var deallocator_pointer = HEAPU32[ (address + 8) / 4 ]; 135 | var output = function() { 136 | if( pointer === 0 ) { 137 | throw new ReferenceError( "Already dropped Rust function called!" ); 138 | } 139 | 140 | var args = Module.STDWEB_PRIVATE.alloc( 16 ); 141 | Module.STDWEB_PRIVATE.serialize_array( args, arguments ); 142 | Module.STDWEB_PRIVATE.dyncall( "vii", adapter_pointer, [pointer, args] ); 143 | var result = Module.STDWEB_PRIVATE.tmp; 144 | Module.STDWEB_PRIVATE.tmp = null; 145 | 146 | return result; 147 | }; 148 | 149 | output.drop = function() { 150 | output.drop = Module.STDWEB_PRIVATE.noop; 151 | var function_pointer = pointer; 152 | pointer = 0; 153 | 154 | Module.STDWEB_PRIVATE.dyncall( "vi", deallocator_pointer, [function_pointer] ); 155 | }; 156 | 157 | return output; 158 | } else if( kind === 13 ) { 159 | var adapter_pointer = HEAPU32[ address / 4 ]; 160 | var pointer = HEAPU32[ (address + 4) / 4 ]; 161 | var deallocator_pointer = HEAPU32[ (address + 8) / 4 ]; 162 | var output = function() { 163 | if( pointer === 0 ) { 164 | throw new ReferenceError( "Already called or dropped FnOnce function called!" ); 165 | } 166 | 167 | output.drop = Module.STDWEB_PRIVATE.noop; 168 | var function_pointer = pointer; 169 | pointer = 0; 170 | 171 | var args = Module.STDWEB_PRIVATE.alloc( 16 ); 172 | Module.STDWEB_PRIVATE.serialize_array( args, arguments ); 173 | Module.STDWEB_PRIVATE.dyncall( "vii", adapter_pointer, [function_pointer, args] ); 174 | var result = Module.STDWEB_PRIVATE.tmp; 175 | Module.STDWEB_PRIVATE.tmp = null; 176 | 177 | return result; 178 | }; 179 | 180 | output.drop = function() { 181 | output.drop = Module.STDWEB_PRIVATE.noop; 182 | var function_pointer = pointer; 183 | pointer = 0; 184 | 185 | Module.STDWEB_PRIVATE.dyncall( "vi", deallocator_pointer, [function_pointer] ); 186 | }; 187 | 188 | return output; 189 | } else if( kind === 14 ) { 190 | var pointer = HEAPU32[ address / 4 ]; 191 | var length = HEAPU32[ (address + 4) / 4 ]; 192 | var array_kind = HEAPU32[ (address + 8) / 4 ]; 193 | var pointer_end = pointer + length; 194 | 195 | switch( array_kind ) { 196 | case 0: 197 | return HEAPU8.subarray( pointer, pointer_end ); 198 | case 1: 199 | return HEAP8.subarray( pointer, pointer_end ); 200 | case 2: 201 | return HEAPU16.subarray( pointer, pointer_end ); 202 | case 3: 203 | return HEAP16.subarray( pointer, pointer_end ); 204 | case 4: 205 | return HEAPU32.subarray( pointer, pointer_end ); 206 | case 5: 207 | return HEAP32.subarray( pointer, pointer_end ); 208 | case 6: 209 | return HEAPF32.subarray( pointer, pointer_end ); 210 | case 7: 211 | return HEAPF64.subarray( pointer, pointer_end ); 212 | } 213 | } else if( kind === 15 ) { 214 | return Module.STDWEB_PRIVATE.get_raw_value( HEAPU32[ address / 4 ] ); 215 | } 216 | }; 217 | 218 | Module.STDWEB_PRIVATE.serialize_object = function serialize_object( address, value ) { 219 | var keys = Object.keys( value ); 220 | var length = keys.length; 221 | var key_array_pointer = Module.STDWEB_PRIVATE.alloc( length * 8 ); 222 | var value_array_pointer = Module.STDWEB_PRIVATE.alloc( length * 16 ); 223 | HEAPU8[ address + 12 ] = 8; 224 | HEAPU32[ address / 4 ] = value_array_pointer; 225 | HEAPU32[ (address + 4) / 4 ] = length; 226 | HEAPU32[ (address + 8) / 4 ] = key_array_pointer; 227 | for( var i = 0; i < length; ++i ) { 228 | var key = keys[ i ]; 229 | var key_length = Module.STDWEB_PRIVATE.utf8_len( key ); 230 | var key_pointer = Module.STDWEB_PRIVATE.alloc( key_length ); 231 | Module.STDWEB_PRIVATE.to_utf8( key, key_pointer ); 232 | 233 | var key_address = key_array_pointer + i * 8; 234 | HEAPU32[ key_address / 4 ] = key_pointer; 235 | HEAPU32[ (key_address + 4) / 4 ] = key_length; 236 | 237 | Module.STDWEB_PRIVATE.from_js( value_array_pointer + i * 16, value[ key ] ); 238 | } 239 | }; 240 | 241 | Module.STDWEB_PRIVATE.serialize_array = function serialize_array( address, value ) { 242 | var length = value.length; 243 | var pointer = Module.STDWEB_PRIVATE.alloc( length * 16 ); 244 | HEAPU8[ address + 12 ] = 7; 245 | HEAPU32[ address / 4 ] = pointer; 246 | HEAPU32[ (address + 4) / 4 ] = length; 247 | for( var i = 0; i < length; ++i ) { 248 | Module.STDWEB_PRIVATE.from_js( pointer + i * 16, value[ i ] ); 249 | } 250 | }; 251 | 252 | Module.STDWEB_PRIVATE.from_js = function from_js( address, value ) { 253 | var kind = Object.prototype.toString.call( value ); 254 | if( kind === "[object String]" ) { 255 | var length = Module.STDWEB_PRIVATE.utf8_len( value ); 256 | var pointer = 0; 257 | if( length > 0 ) { 258 | pointer = Module.STDWEB_PRIVATE.alloc( length ); 259 | Module.STDWEB_PRIVATE.to_utf8( value, pointer ); 260 | } 261 | HEAPU8[ address + 12 ] = 4; 262 | HEAPU32[ address / 4 ] = pointer; 263 | HEAPU32[ (address + 4) / 4 ] = length; 264 | } else if( kind === "[object Number]" ) { 265 | if( value === (value|0) ) { 266 | HEAPU8[ address + 12 ] = 2; 267 | HEAP32[ address / 4 ] = value; 268 | } else { 269 | HEAPU8[ address + 12 ] = 3; 270 | HEAPF64[ address / 8 ] = value; 271 | } 272 | } else if( value === null ) { 273 | HEAPU8[ address + 12 ] = 1; 274 | } else if( value === undefined ) { 275 | HEAPU8[ address + 12 ] = 0; 276 | } else if( value === false ) { 277 | HEAPU8[ address + 12 ] = 5; 278 | } else if( value === true ) { 279 | HEAPU8[ address + 12 ] = 6; 280 | } else if( kind === "[object Symbol]" ) { 281 | var id = Module.STDWEB_PRIVATE.register_raw_value( value ); 282 | HEAPU8[ address + 12 ] = 15; 283 | HEAP32[ address / 4 ] = id; 284 | } else { 285 | var refid = Module.STDWEB_PRIVATE.acquire_rust_reference( value ); 286 | HEAPU8[ address + 12 ] = 9; 287 | HEAP32[ address / 4 ] = refid; 288 | } 289 | }; 290 | 291 | // This is ported from Rust's stdlib; it's faster than 292 | // the string conversion from Emscripten. 293 | Module.STDWEB_PRIVATE.to_js_string = function to_js_string( index, length ) { 294 | index = index|0; 295 | length = length|0; 296 | var end = (index|0) + (length|0); 297 | var output = ""; 298 | while( index < end ) { 299 | var x = HEAPU8[ index++ ]; 300 | if( x < 128 ) { 301 | output += String.fromCharCode( x ); 302 | continue; 303 | } 304 | var init = (x & (0x7F >> 2)); 305 | var y = 0; 306 | if( index < end ) { 307 | y = HEAPU8[ index++ ]; 308 | } 309 | var ch = (init << 6) | (y & 63); 310 | if( x >= 0xE0 ) { 311 | var z = 0; 312 | if( index < end ) { 313 | z = HEAPU8[ index++ ]; 314 | } 315 | var y_z = ((y & 63) << 6) | (z & 63); 316 | ch = init << 12 | y_z; 317 | if( x >= 0xF0 ) { 318 | var w = 0; 319 | if( index < end ) { 320 | w = HEAPU8[ index++ ]; 321 | } 322 | ch = (init & 7) << 18 | ((y_z << 6) | (w & 63)); 323 | 324 | output += String.fromCharCode( 0xD7C0 + (ch >> 10) ); 325 | ch = 0xDC00 + (ch & 0x3FF); 326 | } 327 | } 328 | output += String.fromCharCode( ch ); 329 | continue; 330 | } 331 | return output; 332 | }; 333 | 334 | Module.STDWEB_PRIVATE.id_to_ref_map = {}; 335 | Module.STDWEB_PRIVATE.id_to_refcount_map = {}; 336 | Module.STDWEB_PRIVATE.ref_to_id_map = new WeakMap(); 337 | // Not all types can be stored in a WeakMap 338 | Module.STDWEB_PRIVATE.ref_to_id_map_fallback = new Map(); 339 | Module.STDWEB_PRIVATE.last_refid = 1; 340 | 341 | Module.STDWEB_PRIVATE.id_to_raw_value_map = {}; 342 | Module.STDWEB_PRIVATE.last_raw_value_id = 1; 343 | 344 | Module.STDWEB_PRIVATE.acquire_rust_reference = function( reference ) { 345 | if( reference === undefined || reference === null ) { 346 | return 0; 347 | } 348 | 349 | var id_to_refcount_map = Module.STDWEB_PRIVATE.id_to_refcount_map; 350 | var id_to_ref_map = Module.STDWEB_PRIVATE.id_to_ref_map; 351 | var ref_to_id_map = Module.STDWEB_PRIVATE.ref_to_id_map; 352 | var ref_to_id_map_fallback = Module.STDWEB_PRIVATE.ref_to_id_map_fallback; 353 | 354 | var refid = ref_to_id_map.get( reference ); 355 | if( refid === undefined ) { 356 | refid = ref_to_id_map_fallback.get( reference ); 357 | } 358 | if( refid === undefined ) { 359 | refid = Module.STDWEB_PRIVATE.last_refid++; 360 | try { 361 | ref_to_id_map.set( reference, refid ); 362 | } catch (e) { 363 | ref_to_id_map_fallback.set( reference, refid ); 364 | } 365 | } 366 | 367 | if( refid in id_to_ref_map ) { 368 | id_to_refcount_map[ refid ]++; 369 | } else { 370 | id_to_ref_map[ refid ] = reference; 371 | id_to_refcount_map[ refid ] = 1; 372 | } 373 | 374 | return refid; 375 | }; 376 | 377 | Module.STDWEB_PRIVATE.acquire_js_reference = function( refid ) { 378 | return Module.STDWEB_PRIVATE.id_to_ref_map[ refid ]; 379 | }; 380 | 381 | Module.STDWEB_PRIVATE.increment_refcount = function( refid ) { 382 | Module.STDWEB_PRIVATE.id_to_refcount_map[ refid ]++; 383 | }; 384 | 385 | Module.STDWEB_PRIVATE.decrement_refcount = function( refid ) { 386 | var id_to_refcount_map = Module.STDWEB_PRIVATE.id_to_refcount_map; 387 | if( 0 == --id_to_refcount_map[ refid ] ) { 388 | var id_to_ref_map = Module.STDWEB_PRIVATE.id_to_ref_map; 389 | var ref_to_id_map_fallback = Module.STDWEB_PRIVATE.ref_to_id_map_fallback; 390 | var reference = id_to_ref_map[ refid ]; 391 | delete id_to_ref_map[ refid ]; 392 | delete id_to_refcount_map[ refid ]; 393 | ref_to_id_map_fallback.delete(reference); 394 | } 395 | }; 396 | 397 | Module.STDWEB_PRIVATE.register_raw_value = function( value ) { 398 | var id = Module.STDWEB_PRIVATE.last_raw_value_id++; 399 | Module.STDWEB_PRIVATE.id_to_raw_value_map[ id ] = value; 400 | return id; 401 | }; 402 | 403 | Module.STDWEB_PRIVATE.unregister_raw_value = function( id ) { 404 | delete Module.STDWEB_PRIVATE.id_to_raw_value_map[ id ]; 405 | }; 406 | 407 | Module.STDWEB_PRIVATE.get_raw_value = function( id ) { 408 | return Module.STDWEB_PRIVATE.id_to_raw_value_map[ id ]; 409 | }; 410 | 411 | Module.STDWEB_PRIVATE.alloc = function alloc( size ) { 412 | return Module.web_malloc( size ); 413 | }; 414 | 415 | Module.STDWEB_PRIVATE.dyncall = function( signature, ptr, args ) { 416 | return Module.web_table.get( ptr ).apply( null, args ); 417 | }; 418 | 419 | // This is based on code from Emscripten's preamble.js. 420 | Module.STDWEB_PRIVATE.utf8_len = function utf8_len( str ) { 421 | var len = 0; 422 | for( var i = 0; i < str.length; ++i ) { 423 | // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8. 424 | // See http://unicode.org/faq/utf_bom.html#utf16-3 425 | var u = str.charCodeAt( i ); // possibly a lead surrogate 426 | if( u >= 0xD800 && u <= 0xDFFF ) { 427 | u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt( ++i ) & 0x3FF); 428 | } 429 | 430 | if( u <= 0x7F ) { 431 | ++len; 432 | } else if( u <= 0x7FF ) { 433 | len += 2; 434 | } else if( u <= 0xFFFF ) { 435 | len += 3; 436 | } else if( u <= 0x1FFFFF ) { 437 | len += 4; 438 | } else if( u <= 0x3FFFFFF ) { 439 | len += 5; 440 | } else { 441 | len += 6; 442 | } 443 | } 444 | return len; 445 | }; 446 | 447 | Module.STDWEB_PRIVATE.prepare_any_arg = function( value ) { 448 | var arg = Module.STDWEB_PRIVATE.alloc( 16 ); 449 | Module.STDWEB_PRIVATE.from_js( arg, value ); 450 | return arg; 451 | }; 452 | 453 | Module.STDWEB_PRIVATE.acquire_tmp = function( dummy ) { 454 | var value = Module.STDWEB_PRIVATE.tmp; 455 | Module.STDWEB_PRIVATE.tmp = null; 456 | return value; 457 | }; 458 | 459 | 460 | 461 | var HEAP8 = null; 462 | var HEAP16 = null; 463 | var HEAP32 = null; 464 | var HEAPU8 = null; 465 | var HEAPU16 = null; 466 | var HEAPU32 = null; 467 | var HEAPF32 = null; 468 | var HEAPF64 = null; 469 | 470 | Object.defineProperty( Module, 'exports', { value: {} } ); 471 | 472 | function __web_on_grow() { 473 | var buffer = Module.instance.exports.memory.buffer; 474 | HEAP8 = new Int8Array( buffer ); 475 | HEAP16 = new Int16Array( buffer ); 476 | HEAP32 = new Int32Array( buffer ); 477 | HEAPU8 = new Uint8Array( buffer ); 478 | HEAPU16 = new Uint16Array( buffer ); 479 | HEAPU32 = new Uint32Array( buffer ); 480 | HEAPF32 = new Float32Array( buffer ); 481 | HEAPF64 = new Float64Array( buffer ); 482 | } 483 | 484 | return { 485 | imports: { 486 | env: { 487 | "__extjs_80d6d56760c65e49b7be8b6b01c1ea861b046bf0": function($0) { 488 | Module.STDWEB_PRIVATE.decrement_refcount( $0 ); 489 | }, 490 | "__extjs_da39a3ee5e6b4b0d3255bfef95601890afd80709": function($0) { 491 | 492 | }, 493 | "__extjs_9f22d4ca7bc938409787341b7db181f8dd41e6df": function($0) { 494 | Module.STDWEB_PRIVATE.increment_refcount( $0 ); 495 | }, 496 | "__extjs_4cc2b2ed53586a2bd32ca2206724307e82bb32ff": function($0, $1) { 497 | $0 = Module.STDWEB_PRIVATE.to_js($0);$1 = Module.STDWEB_PRIVATE.to_js($1);($0). appendChild (($1)); 498 | }, 499 | "__extjs_e5fb9179be14d883494f9afd3d5f19a87ee532cc": function($0, $1) { 500 | $1 = Module.STDWEB_PRIVATE.to_js($1);Module.STDWEB_PRIVATE.from_js($0, (function(){return ($1). nextSibling ;})()); 501 | }, 502 | "__extjs_b26a87e444d448e2efeef401f8474b1886c40ae0": function($0, $1) { 503 | $1 = Module.STDWEB_PRIVATE.to_js($1);Module.STDWEB_PRIVATE.from_js($0, (function(){return ($1). lastChild ;})()); 504 | }, 505 | "__extjs_0aced9e2351ced72f1ff99645a129132b16c0d3c": function($0) { 506 | var value = Module.STDWEB_PRIVATE.get_raw_value( $0 );return Module.STDWEB_PRIVATE.register_raw_value( value ); 507 | }, 508 | "__extjs_db0226ae1bbecd407e9880ee28ddc70fc3322d9c": function($0) { 509 | $0 = Module.STDWEB_PRIVATE.to_js($0);Module.STDWEB_PRIVATE.unregister_raw_value (($0)); 510 | }, 511 | "__extjs_7e5e0af700270c95236d095748467db3ee37c15b": function($0, $1) { 512 | $0 = Module.STDWEB_PRIVATE.to_js($0);$1 = Module.STDWEB_PRIVATE.to_js($1);($0). checked = ($1); 513 | }, 514 | "__extjs_de2896a7ccf316486788a4d0bc433c25d2f1a12b": function($0) { 515 | var r = Module.STDWEB_PRIVATE.acquire_js_reference( $0 );return (r instanceof DOMException) && (r.name === "NotFoundError"); 516 | }, 517 | "__extjs_d16972c13e7882e1313d54277c2688b305eebc63": function($0) { 518 | return (Module.STDWEB_PRIVATE.acquire_js_reference( $0 ) instanceof HTMLTextAreaElement) | 0; 519 | }, 520 | "__extjs_55930e70138d1eac196bf34081691354e06aa248": function($0, $1) { 521 | $1 = Module.STDWEB_PRIVATE.to_js($1);Module.STDWEB_PRIVATE.from_js($0, (function(){return ($1). sessionStorage ;})()); 522 | }, 523 | "__extjs_cf32fb39093cd2549f37c2d392ef3198dcaa2ad4": function($0) { 524 | return (Module.STDWEB_PRIVATE.acquire_js_reference( $0 ) instanceof Node) | 0; 525 | }, 526 | "__extjs_ff5103e6cc179d13b4c7a785bdce2708fd559fc0": function($0) { 527 | Module.STDWEB_PRIVATE.tmp = Module.STDWEB_PRIVATE.to_js( $0 ); 528 | }, 529 | "__extjs_a8e1d9cfe0b41d7d61b849811ad1cfba32de989b": function($0, $1, $2) { 530 | $1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);Module.STDWEB_PRIVATE.from_js($0, (function(){return ($1). createElement (($2));})()); 531 | }, 532 | "__extjs_a342681e5c1e3fb0bdeac6e35d67bf944fcd4102": function($0, $1) { 533 | $1 = Module.STDWEB_PRIVATE.to_js($1);Module.STDWEB_PRIVATE.from_js($0, (function(){return ($1). value ;})()); 534 | }, 535 | "__extjs_02719998c6ece772fc2c8c3dd585272cdb2a127e": function($0, $1) { 536 | $0 = Module.STDWEB_PRIVATE.to_js($0);$1 = Module.STDWEB_PRIVATE.to_js($1);($0). add (($1)); 537 | }, 538 | "__extjs_425fcd9ee090672474c80ebf7d7b7719e5ba47fc": function($0) { 539 | $0 = Module.STDWEB_PRIVATE.to_js($0);console.debug (($0)); 540 | }, 541 | "__extjs_792ff14631f0ebffafcf6ed24405be73234b64ba": function($0, $1) { 542 | $1 = Module.STDWEB_PRIVATE.to_js($1);Module.STDWEB_PRIVATE.from_js($0, (function(){return ($1). classList ;})()); 543 | }, 544 | "__extjs_e031828dc4b7f1b8d9625d60486f03b0936c3f4f": function($0, $1, $2) { 545 | $1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);Module.STDWEB_PRIVATE.from_js($0, (function(){try {return {value : function (){return ($1). removeChild (($2));}(), success : true};}catch (error){return {error : error , success : false};}})()); 546 | }, 547 | "__extjs_2ff57da66ea0e6d13328bc60a5a5dbfee840cbf2": function($0, $1, $2) { 548 | $0 = Module.STDWEB_PRIVATE.to_js($0);$1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);var listener = ($0); ($1). removeEventListener (($2), listener); listener.drop (); 549 | }, 550 | "__extjs_c41297f1f679af47d6390b4b617d1a8375706933": function($0) { 551 | $0 = Module.STDWEB_PRIVATE.to_js($0);console.error (($0)); 552 | }, 553 | "__extjs_a619fcd124d0713b9c1330733bb52e7a93475a4b": function($0, $1) { 554 | $1 = Module.STDWEB_PRIVATE.to_js($1);Module.STDWEB_PRIVATE.from_js($0, (function(){var self = ($1); if (self.selectedIndex < 0){return null ;}else {return self.selectedIndex ;}})()); 555 | }, 556 | "__extjs_a3b76c5b7916fd257ee3f362dc672b974e56c476": function($0, $1) { 557 | $1 = Module.STDWEB_PRIVATE.to_js($1);Module.STDWEB_PRIVATE.from_js($0, (function(){return ($1). success ;})()); 558 | }, 559 | "__extjs_6ca5ed896d5e65a07120817a084b0c9d1668daf5": function($0) { 560 | return (Module.STDWEB_PRIVATE.acquire_js_reference( $0 ) instanceof HTMLInputElement) | 0; 561 | }, 562 | "__extjs_dc4a9844a3da9e83cb7a74b4e08eed6ff1be91f9": function($0, $1, $2) { 563 | $1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);Module.STDWEB_PRIVATE.from_js($0, (function(){return ($1). createTextNode (($2));})()); 564 | }, 565 | "__extjs_5ecfd7ee5cecc8be26c1e6e3c90ce666901b547c": function($0, $1) { 566 | $1 = Module.STDWEB_PRIVATE.to_js($1);Module.STDWEB_PRIVATE.from_js($0, (function(){return ($1). error ;})()); 567 | }, 568 | "__extjs_7ed1f62e776725bc93d54f5154abfb28a460024a": function($0) { 569 | return (Module.STDWEB_PRIVATE.acquire_js_reference( $0 ) instanceof MouseEvent) | 0; 570 | }, 571 | "__extjs_4028145202a86da6f0ee9067e044568730858725": function($0) { 572 | $0 = Module.STDWEB_PRIVATE.to_js($0);($0). type = "" ; 573 | }, 574 | "__extjs_f484b2485bfbca4799114a1eed5e17c124da2193": function($0, $1) { 575 | $1 = Module.STDWEB_PRIVATE.to_js($1);Module.STDWEB_PRIVATE.from_js($0, (function(){return ($1). localStorage ;})()); 576 | }, 577 | "__extjs_74d5764ddc102a8d3b6252116087a68f2db0c9d4": function($0) { 578 | Module.STDWEB_PRIVATE.from_js($0, (function(){return window ;})()); 579 | }, 580 | "__extjs_1bec6cd85a41c300a38db6f77d11403ddcfae787": function($0) { 581 | return (Module.STDWEB_PRIVATE.acquire_js_reference( $0 ) instanceof Element) | 0; 582 | }, 583 | "__extjs_c023351d5bff43ef3dd317b499821cd4e71492f0": function($0) { 584 | var r = Module.STDWEB_PRIVATE.acquire_js_reference( $0 );return (r instanceof DOMException) && (r.name === "HierarchyRequestError"); 585 | }, 586 | "__extjs_27ff97ff577dd39402cb36ac5c70bcb0711afba8": function($0, $1, $2) { 587 | $1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);Module.STDWEB_PRIVATE.from_js($0, (function(){return ($1). getItem (($2));})()); 588 | }, 589 | "__extjs_aafcab8f69692c3778f32d5ffbed6214b6ecf266": function($0) { 590 | $0 = Module.STDWEB_PRIVATE.to_js($0);($0). stopPropagation (); 591 | }, 592 | "__extjs_b79ab773ae35a43a8d7a215353fdb0413bd6224c": function($0, $1) { 593 | $0 = Module.STDWEB_PRIVATE.to_js($0);$1 = Module.STDWEB_PRIVATE.to_js($1);($0). nodeValue = ($1); 594 | }, 595 | "__extjs_39e486671818674ef5d1eeb54119acb740296cc9": function($0, $1, $2) { 596 | $0 = Module.STDWEB_PRIVATE.to_js($0);$1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);($0). setItem (($1), ($2)); 597 | }, 598 | "__extjs_97495987af1720d8a9a923fa4683a7b683e3acd6": function($0, $1) { 599 | console.error( 'Panic error message:', Module.STDWEB_PRIVATE.to_js_string( $0, $1 ) ); 600 | }, 601 | "__extjs_72fc447820458c720c68d0d8e078ede631edd723": function($0, $1, $2) { 602 | console.error( 'Panic location:', Module.STDWEB_PRIVATE.to_js_string( $0, $1 ) + ':' + $2 ); 603 | }, 604 | "__extjs_8dc3eee0077e1d4de8467d5817789266b81b33ad": function($0, $1) { 605 | $0 = Module.STDWEB_PRIVATE.to_js($0);$1 = Module.STDWEB_PRIVATE.to_js($1);($0). type = ($1); 606 | }, 607 | "__extjs_4077c66de83a520233f5f35f5a8f3073f5bac5fc": function($0, $1, $2, $3) { 608 | $1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);$3 = Module.STDWEB_PRIVATE.to_js($3);Module.STDWEB_PRIVATE.from_js($0, (function(){try {return {value : function (){return ($1). insertBefore (($2), ($3));}(), success : true};}catch (error){return {error : error , success : false};}})()); 609 | }, 610 | "__extjs_3d06b88dd7c8555dc8918ce12a4aa30730e8d3ba": function($0) { 611 | return (Module.STDWEB_PRIVATE.acquire_js_reference( $0 ) instanceof HTMLSelectElement) | 0; 612 | }, 613 | "__extjs_3fdba5930b45aa718ed8a660c7a88a76e22a21d8": function($0, $1) { 614 | $0 = Module.STDWEB_PRIVATE.to_js($0);$1 = Module.STDWEB_PRIVATE.to_js($1);($0). remove (($1)); 615 | }, 616 | "__extjs_5ac38c9ecbb9a6f75e30e71400dabbd8d3562771": function($0) { 617 | return (Module.STDWEB_PRIVATE.acquire_js_reference( $0 ) instanceof Event) | 0; 618 | }, 619 | "__extjs_4f184f99dbb48468f75bc10e9fc4b1707e193775": function($0, $1, $2) { 620 | $0 = Module.STDWEB_PRIVATE.to_js($0);$1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);($0). setAttribute (($1), ($2)); 621 | }, 622 | "__extjs_dc2fd915bd92f9e9c6a3bd15174f1414eee3dbaf": function() { 623 | console.error( 'Encountered a panic!' ); 624 | }, 625 | "__extjs_7c5535365a3df6a4cc1f59c4a957bfce1dbfb8ee": function($0, $1, $2, $3) { 626 | $1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);$3 = Module.STDWEB_PRIVATE.to_js($3);Module.STDWEB_PRIVATE.from_js($0, (function(){var listener = ($1); ($2). addEventListener (($3), listener); return listener ;})()); 627 | }, 628 | "__extjs_e7aa18dc6d8c65f9c161c079ef483a13d144e4d3": function($0, $1) { 629 | $1 = Module.STDWEB_PRIVATE.to_js($1);Module.STDWEB_PRIVATE.from_js($0, (function(){return ($1). nodeName ;})()); 630 | }, 631 | "__extjs_74e6b3628156d1f468b2cc770c3cd6665ca63ace": function($0, $1) { 632 | $0 = Module.STDWEB_PRIVATE.to_js($0);$1 = Module.STDWEB_PRIVATE.to_js($1);($0). removeAttribute (($1)); 633 | }, 634 | "__extjs_1c8769c3b326d77ceb673ada3dc887cf1d509509": function($0) { 635 | Module.STDWEB_PRIVATE.from_js($0, (function(){return document ;})()); 636 | }, 637 | "__extjs_4f998a6a2e8abfce697424379bb997930abe9f9e": function($0, $1) { 638 | $0 = Module.STDWEB_PRIVATE.to_js($0);$1 = Module.STDWEB_PRIVATE.to_js($1);($0). value = ($1); 639 | }, 640 | "__extjs_496ebd7b1bc0e6eebd7206e8bee7671ea3b8006f": function($0, $1, $2) { 641 | $1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);Module.STDWEB_PRIVATE.from_js($0, (function(){return ($1). querySelector (($2));})()); 642 | }, 643 | "__web_on_grow": __web_on_grow 644 | } 645 | }, 646 | initialize: function( instance ) { 647 | Object.defineProperty( Module, 'instance', { value: instance } ); 648 | Object.defineProperty( Module, 'web_malloc', { value: Module.instance.exports.__web_malloc } ); 649 | Object.defineProperty( Module, 'web_free', { value: Module.instance.exports.__web_free } ); 650 | Object.defineProperty( Module, 'web_table', { value: Module.instance.exports.__web_table } ); 651 | 652 | 653 | __web_on_grow(); 654 | Module.instance.exports.main(); 655 | 656 | return Module.exports; 657 | } 658 | }; 659 | } 660 | )); 661 | })); 662 | -------------------------------------------------------------------------------- /docs/kp-chart.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluejekyll/kp-chart/ad981b798c07547a2de6b933e2abcd733f92900c/docs/kp-chart.wasm -------------------------------------------------------------------------------- /docs/styles.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | text-align: center; 6 | font-family: Arial, Helvetica, sans-serif; 7 | } 8 | 9 | h1 { 10 | font-family: Georgia, serif; 11 | font-style: italic; 12 | } 13 | 14 | table { 15 | margin: 20px; 16 | background-color: cornsilk; 17 | box-shadow: 5px 5px 5px #aaaaaa; 18 | text-align: left; 19 | border-collapse: collapse; 20 | border: 1px solid #dddddd; 21 | } 22 | 23 | table th, table td { 24 | padding: 5px; 25 | } 26 | 27 | thead { 28 | padding: 10px; 29 | border: 1px; 30 | font-size: 12pt; 31 | font-style: normal; 32 | border-top-color: #c50d0d; 33 | border-top-width: 1px; 34 | border-top-style: solid; 35 | border-bottom-color: #290dc5; 36 | border-bottom-width: 5px; 37 | border-bottom-style: double; 38 | } 39 | 40 | tbody td { 41 | font-family: "Chalkboard", "ChalkboardSE-Regular", "Comic Sans", "Comic Sans MS", sans-serif; 42 | font-variant: small-caps; 43 | font-weight: bold; 44 | font-size: 16pt; 45 | color: crimson; 46 | } 47 | 48 | tbody th { 49 | border: 1px; 50 | font-size: 12pt; 51 | font-style: normal; 52 | 53 | } 54 | 55 | tbody th, tbody td { 56 | border-bottom-color: #000000; 57 | border-bottom-width: 1px; 58 | border-bottom-style: solid; 59 | border-right-color: #cacaca; 60 | border-right-width: 1px; 61 | border-right-style: solid;} 62 | 63 | tfoot td { 64 | border: none; 65 | } 66 | 67 | .edit_delete { 68 | color: black; 69 | } 70 | 71 | .disabled { 72 | color: lightgray; 73 | } 74 | 75 | i { 76 | cursor: pointer; 77 | } 78 | 79 | i:active { 80 | color: lightgray; 81 | } -------------------------------------------------------------------------------- /js/index.js: -------------------------------------------------------------------------------- 1 | import("../pkg/index.js").catch(console.error); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kp-chart", 3 | "version": "1.0.0", 4 | "description": "To keep up the contributions to the dollar jar.", 5 | "main": "index.js", 6 | "dependencies": { 7 | "webpack-dev-server": "^3.10.3" 8 | }, 9 | "devDependencies": { 10 | "copy-webpack-plugin": "^5.1.1", 11 | "@wasm-tool/wasm-pack-plugin": "^1.2.0", 12 | "webpack": "^4.42.0", 13 | "webpack-cli": "^3.3.11", 14 | "base64-loader": "^1.0.0" 15 | }, 16 | "scripts": { 17 | "start": "webpack-dev-server --open -d", 18 | "test": "wasm-pack test --headless", 19 | "build": "webpack" 20 | }, 21 | "keywords": [], 22 | "author": "", 23 | "license": "ISC" 24 | } 25 | -------------------------------------------------------------------------------- /src/data/day.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Display, Formatter}; 2 | use std::iter::Cycle; 3 | use std::slice::Iter; 4 | 5 | use crate::data::{Ability, Job, Person}; 6 | 7 | #[derive(Clone, Debug)] 8 | pub struct Day { 9 | name: String, 10 | jobs: Vec<(Job, Vec)>, 11 | } 12 | 13 | impl Day { 14 | pub fn new( 15 | name: String, 16 | jobs: Vec, 17 | children: &mut Cycle>, 18 | teens: &mut Cycle>, 19 | adults: &mut Cycle>, 20 | ) -> Self { 21 | let mut day_jobs = jobs 22 | .clone() 23 | .into_iter() 24 | .map(|j| (j, Vec::::new())) 25 | .collect::>(); 26 | 27 | // pass through all children jobs first 28 | for (job, ref mut workers) in day_jobs.iter_mut() { 29 | for ability in job.people().iter() { 30 | match *ability { 31 | Ability::Child => workers.push( 32 | children 33 | .next() 34 | .cloned() 35 | .unwrap_or_else(|| Person::new("No Child Here", Ability::Child)), 36 | ), 37 | Ability::Teen => workers.push( 38 | teens 39 | .next() 40 | .cloned() 41 | .unwrap_or_else(|| Person::new("No Teen Here", Ability::Teen)), 42 | ), 43 | Ability::Adult => workers.push( 44 | adults 45 | .next() 46 | .cloned() 47 | .unwrap_or_else(|| Person::new("No Adult Here", Ability::Adult)), 48 | ), 49 | } 50 | } 51 | } 52 | 53 | Self { 54 | name, 55 | jobs: day_jobs, 56 | } 57 | } 58 | 59 | pub fn name(&self) -> &str { 60 | &self.name 61 | } 62 | 63 | pub fn jobs(&self) -> &[(Job, Vec)] { 64 | &self.jobs 65 | } 66 | 67 | pub fn get_job_people(&self, job: usize) -> &[Person] { 68 | &self.jobs[job].1 69 | } 70 | } 71 | 72 | impl Display for Day { 73 | fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> { 74 | for (job, people) in self.jobs.iter() { 75 | write!(fmt, "{}: ", job)?; 76 | for person in people.iter() { 77 | write!(fmt, "{}, ", person)?; 78 | } 79 | writeln!(fmt, "")?; 80 | } 81 | Ok(()) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/data/job.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Display, Formatter}; 2 | 3 | use crate::data::Ability; 4 | 5 | #[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] 6 | pub struct Job { 7 | name: String, 8 | people: Vec, 9 | } 10 | 11 | impl Job { 12 | pub fn new(name: &'static str, people: Vec) -> Self { 13 | Self { 14 | name: name.to_string(), 15 | people, 16 | } 17 | } 18 | 19 | pub fn name(&self) -> &str { 20 | &self.name 21 | } 22 | 23 | pub fn people(&self) -> &[Ability] { 24 | &self.people 25 | } 26 | } 27 | 28 | impl Display for Job { 29 | fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> { 30 | write!(fmt, "{}", self.name) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/data/mod.rs: -------------------------------------------------------------------------------- 1 | mod day; 2 | mod job; 3 | mod person; 4 | mod week; 5 | 6 | pub use self::day::Day; 7 | pub use self::job::Job; 8 | pub use self::person::{Ability, Person}; 9 | pub use self::week::Week; 10 | -------------------------------------------------------------------------------- /src/data/person.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use std::fmt::{self, Display, Formatter}; 3 | 4 | #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] 5 | pub struct Person { 6 | name: String, 7 | ability: Ability, 8 | } 9 | 10 | impl Person { 11 | pub fn new(name: &'static str, ability: Ability) -> Self { 12 | Self { 13 | name: name.to_string(), 14 | ability, 15 | } 16 | } 17 | 18 | pub fn name(&self) -> &str { 19 | &self.name 20 | } 21 | 22 | pub fn ability(&self) -> Ability { 23 | self.ability 24 | } 25 | 26 | pub fn set_name(&mut self, name: String) { 27 | self.name = name; 28 | } 29 | 30 | pub fn set_ability(&mut self, ability: Ability) { 31 | self.ability = ability; 32 | } 33 | } 34 | 35 | impl Display for Person { 36 | fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> { 37 | write!(fmt, "{}", self.name) 38 | } 39 | } 40 | 41 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Ord, PartialOrd, Serialize, Deserialize)] 42 | pub enum Ability { 43 | Adult = 0, 44 | Teen = 1, 45 | Child = 2, 46 | } 47 | 48 | impl Default for Ability { 49 | fn default() -> Self { 50 | Ability::Adult 51 | } 52 | } 53 | 54 | impl Ability { 55 | pub fn enumerate() -> &'static [Ability] { 56 | &[Ability::Adult, Ability::Teen, Ability::Child] 57 | } 58 | 59 | pub fn to_str(&self) -> &'static str { 60 | match self { 61 | Ability::Adult => "Adult", 62 | Ability::Teen => "Teen", 63 | Ability::Child => "Child", 64 | } 65 | } 66 | 67 | pub fn from_i32(prim: i32) -> Self { 68 | match prim { 69 | 0 => Ability::Adult, 70 | 1 => Ability::Teen, 71 | 2 => Ability::Child, 72 | _ => panic!("bad value for Ability: {}", prim), 73 | } 74 | } 75 | } 76 | 77 | impl From for i32 { 78 | fn from(ability: Ability) -> i32 { 79 | ability as i32 80 | } 81 | } 82 | 83 | impl Display for Ability { 84 | fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> { 85 | write!(fmt, "{}", self.to_str()) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/data/week.rs: -------------------------------------------------------------------------------- 1 | use crate::data::{Day, Job}; 2 | 3 | #[derive(Clone, Debug)] 4 | pub struct Week { 5 | week: Vec, 6 | } 7 | 8 | impl Week { 9 | pub fn new(week: Vec) -> Self { 10 | Self { week } 11 | } 12 | 13 | pub fn num_jobs(&self) -> usize { 14 | self.week[0].jobs().len() 15 | } 16 | 17 | pub fn days(&self) -> &[Day] { 18 | &self.week 19 | } 20 | 21 | pub fn jobs(&self) -> impl Iterator { 22 | self.week[0].jobs().iter().map(|(job, _)| job) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "2048"] 2 | 3 | pub mod data; 4 | pub mod web; 5 | 6 | use log::Level; 7 | use std::iter::*; 8 | use wasm_bindgen::prelude::*; 9 | use yew::prelude::*; 10 | 11 | use self::data::*; 12 | 13 | pub fn default_jobs() -> Vec { 14 | let mut jobs = Vec::::new(); 15 | jobs.push(Job::new( 16 | "Breakfast dishes", 17 | vec![Ability::Teen, Ability::Child], 18 | )); 19 | jobs.push(Job::new( 20 | "Lunch preparation", 21 | vec![Ability::Adult, Ability::Adult], 22 | )); 23 | jobs.push(Job::new( 24 | "Lunch dishes", 25 | vec![Ability::Adult, Ability::Teen], 26 | )); 27 | jobs.push(Job::new( 28 | "Dinner Setting", 29 | vec![Ability::Teen, Ability::Child, Ability::Child], 30 | )); 31 | jobs.push(Job::new( 32 | "Dinner shopping and chef", 33 | vec![Ability::Adult, Ability::Adult], 34 | )); 35 | jobs.push(Job::new( 36 | "Dinner dishes", 37 | vec![Ability::Adult, Ability::Teen], 38 | )); 39 | jobs.push(Job::new("Late night dishes", vec![Ability::Teen])); 40 | jobs.push(Job::new("Cabin cleanup", vec![Ability::Adult])); 41 | jobs.push(Job::new("Nag", vec![Ability::Adult])); 42 | 43 | jobs 44 | } 45 | 46 | pub fn default_people() -> Vec { 47 | let mut people = Vec::::new(); 48 | people.push(Person::new("Grandma", Ability::Adult)); 49 | people.push(Person::new("Grandpa", Ability::Adult)); 50 | people.push(Person::new("Mom", Ability::Adult)); 51 | people.push(Person::new("Dad", Ability::Adult)); 52 | people.push(Person::new("Aunt Jane", Ability::Adult)); 53 | people.push(Person::new("Uncle Joe", Ability::Adult)); 54 | people.push(Person::new("Jackie", Ability::Teen)); 55 | people.push(Person::new("Jake", Ability::Teen)); 56 | people.push(Person::new("Jill", Ability::Child)); 57 | people.push(Person::new("Jeffrey", Ability::Child)); 58 | 59 | return people; 60 | } 61 | 62 | pub fn calculate_day_jobs() -> Week { 63 | let jobs = default_jobs(); 64 | let people = default_people(); 65 | calculate(5, jobs, people) 66 | } 67 | 68 | pub fn calculate(num_days: usize, jobs: Vec, people: Vec) -> Week { 69 | let children = people 70 | .clone() 71 | .into_iter() 72 | .filter(|p| p.ability() == Ability::Child) 73 | .collect::>(); 74 | let mut children_iter = children.iter().cycle(); 75 | 76 | let teens = people 77 | .clone() 78 | .into_iter() 79 | .filter(|p| p.ability() == Ability::Teen) 80 | .collect::>(); 81 | let mut teens_iter = teens.iter().cycle(); 82 | 83 | let adults = people 84 | .clone() 85 | .into_iter() 86 | .filter(|p| p.ability() == Ability::Adult) 87 | .collect::>(); 88 | let mut adults_iter = adults.iter().cycle(); 89 | 90 | // make sure we have a good balance of jobs across adults, we nee the count of adult jobs 91 | let adult_job_count = jobs.iter().fold(0_usize, |count, j| { 92 | j.people().iter().filter(|a| **a == Ability::Adult).count() + count 93 | }); 94 | 95 | let mut days = Vec::with_capacity(num_days); 96 | for i in 0..num_days { 97 | let day = Day::new( 98 | format!("day_{}", i), 99 | jobs.clone(), 100 | &mut children_iter, 101 | &mut teens_iter, 102 | &mut adults_iter, 103 | ); 104 | 105 | // force an additional rotation to offset Dinner duty 106 | // we need to make sure we balance the rotation of major adult jobs 107 | if (adult_job_count + 1) == adults.len() { 108 | adults_iter.next(); 109 | adults_iter.next(); 110 | } else { 111 | adults_iter.next(); 112 | } 113 | 114 | days.push(day); 115 | } 116 | 117 | Week::new(days) 118 | } 119 | 120 | #[wasm_bindgen(start)] 121 | pub fn start() -> Result<(), JsValue> { 122 | console_log::init_with_level(Level::Debug).expect("failed to initialize logger"); 123 | yew::initialize(); 124 | 125 | App::::new().mount_to_body_with_props(()); 126 | yew::run_loop(); 127 | 128 | Ok(()) 129 | } 130 | -------------------------------------------------------------------------------- /src/web/chart.rs: -------------------------------------------------------------------------------- 1 | use log::debug; 2 | use yew::prelude::*; 3 | 4 | use crate::data::*; 5 | use crate::web::people::PeopleStore; 6 | use yew::services::{storage::Area, StorageService}; 7 | 8 | #[derive(Clone)] 9 | pub struct Chart { 10 | people_version: usize, 11 | week: Week, 12 | } 13 | 14 | #[derive(Clone, Default, PartialEq, Properties)] 15 | pub struct ChartProps { 16 | pub people_version: usize, 17 | } 18 | 19 | impl Chart { 20 | fn calculate() -> Self { 21 | debug!("calculating new week"); 22 | let mut local_store = StorageService::new(Area::Local).expect("failed to get storage"); 23 | 24 | let jobs = crate::default_jobs(); 25 | let (people_version, people) = PeopleStore::restore(&mut local_store) 26 | .map(|p| (p.inc, p.people)) 27 | .unwrap_or_else(|| (0, crate::default_people())); 28 | Self { 29 | people_version: people_version, 30 | week: crate::calculate(5, jobs, people), 31 | } 32 | } 33 | } 34 | 35 | impl Component for Chart { 36 | type Message = (); 37 | type Properties = ChartProps; 38 | 39 | fn create(_props: Self::Properties, _link: ComponentLink) -> Self { 40 | debug!("creating Chart"); 41 | Self::calculate() 42 | } 43 | 44 | fn update(&mut self, _msg: Self::Message) -> ShouldRender { 45 | false 46 | } 47 | 48 | fn change(&mut self, props: Self::Properties) -> ShouldRender { 49 | if self.people_version != props.people_version { 50 | debug!("updating Chart"); 51 | *self = Self::calculate(); 52 | true 53 | } else { 54 | false 55 | } 56 | } 57 | 58 | fn view(&self) -> Html { 59 | let header = |name: &str| { 60 | html! { 61 | { format!("{}", name) } 62 | } 63 | }; 64 | let people_cell = |people: &[Person]| { 65 | let mut people_str = String::new(); 66 | for person in people { 67 | people_str.push_str(person.name()); 68 | people_str.push_str(", "); 69 | } 70 | 71 | html! { 72 | { people_str } 73 | } 74 | }; 75 | let job_row = |(job_idx, job): (usize, &Job)| { 76 | let days = self.week.days(); 77 | html! { 78 | { header(job.name()) } { for days.iter().map(|d| people_cell(d.get_job_people(job_idx))) } 79 | } 80 | }; 81 | 82 | html! { 83 | <> 84 |

{"Job Chart"}

85 | 86 | 87 | { for self.week.days().iter().map(|d| header(d.name())) } 88 | 89 | 90 | { for self.week.jobs().enumerate().map(|j| job_row(j)) } 91 | 92 |
{"Job"}
93 | 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/web/mod.rs: -------------------------------------------------------------------------------- 1 | mod chart; 2 | mod people; 3 | mod root; 4 | 5 | pub use self::chart::Chart; 6 | pub use self::people::PeopleModel; 7 | pub use self::root::RootModel; 8 | -------------------------------------------------------------------------------- /src/web/people.rs: -------------------------------------------------------------------------------- 1 | use log::{debug, error}; 2 | use serde::{Deserialize, Serialize}; 3 | use web_sys::HtmlSelectElement; 4 | use yew::callback::Callback; 5 | use yew::format::Json; 6 | use yew::prelude::*; 7 | use yew::services::{storage::Area, StorageService}; 8 | 9 | use crate::data::*; 10 | 11 | const PEOPLE_KEY: &str = "people_v1"; 12 | type IsEditting = bool; 13 | type Id = usize; 14 | 15 | pub enum PeopleMsg { 16 | AddPerson, 17 | SavePeople, 18 | EditPerson(Id), 19 | DeletePerson(Id), 20 | PersonNameInput(Id, String), 21 | PersonAbilityInput(Id, Ability), 22 | } 23 | 24 | #[derive(Clone)] 25 | pub struct PeopleModel { 26 | inc: usize, 27 | people: Vec<(Person, IsEditting)>, 28 | on_save: Option>, 29 | link: ComponentLink, 30 | } 31 | 32 | #[derive(Clone, Default, PartialEq, Properties)] 33 | pub struct PeopleProps { 34 | pub on_save: Option>, 35 | } 36 | 37 | #[derive(Clone, Serialize, Deserialize)] 38 | pub struct PeopleStore { 39 | pub inc: usize, 40 | pub people: Vec, 41 | } 42 | 43 | impl PeopleStore { 44 | pub fn restore(local_store: &mut StorageService) -> Option { 45 | let from_store = local_store.restore(PEOPLE_KEY); 46 | match from_store { 47 | Json(Ok(people)) => Some(people), 48 | // TODO: reset local store... 49 | Json(Err(err)) => { 50 | error!("could not load from local store: {}", err); 51 | None 52 | } 53 | } 54 | } 55 | 56 | pub fn store(&mut self, local_store: &mut StorageService) { 57 | self.inc += 1; 58 | debug!("saving people: {}", self.inc); 59 | local_store.store(PEOPLE_KEY, Json(self as &Self)); 60 | } 61 | } 62 | 63 | impl From for PeopleStore { 64 | fn from(model: PeopleModel) -> Self { 65 | Self { 66 | inc: model.inc, 67 | people: model.people.into_iter().map(|(p, _)| p).collect(), 68 | } 69 | } 70 | } 71 | 72 | impl PeopleModel { 73 | fn from( 74 | model: PeopleStore, 75 | on_save: Option>, 76 | link: ComponentLink, 77 | ) -> Self { 78 | Self { 79 | inc: model.inc, 80 | people: model.people.into_iter().map(|p| (p, false)).collect(), 81 | on_save, 82 | link, 83 | } 84 | } 85 | } 86 | 87 | impl Component for PeopleModel { 88 | type Message = PeopleMsg; 89 | type Properties = PeopleProps; 90 | 91 | fn create(props: Self::Properties, link: ComponentLink) -> Self { 92 | debug!("creating PeopleModel"); 93 | 94 | let mut local_store = StorageService::new(Area::Local).expect("failed to get storage"); 95 | 96 | match PeopleStore::restore(&mut local_store) { 97 | Some(this) => Self { 98 | inc: this.inc, 99 | people: this.people.into_iter().map(|p| (p, false)).collect(), 100 | on_save: props.on_save, 101 | link, 102 | }, 103 | None => { 104 | let people = crate::default_people(); 105 | // TODO: make a borrowed type 106 | let mut people = PeopleStore { 107 | inc: 0, 108 | people: people, 109 | }; 110 | 111 | people.store(&mut local_store); 112 | Self { 113 | inc: people.inc, 114 | people: people.people.into_iter().map(|p| (p, false)).collect(), 115 | on_save: props.on_save, 116 | link, 117 | } 118 | } 119 | } 120 | } 121 | 122 | fn update(&mut self, msg: Self::Message) -> ShouldRender { 123 | match msg { 124 | PeopleMsg::SavePeople => { 125 | debug!("saving PeopleModel"); 126 | let mut local_store = 127 | StorageService::new(Area::Local).expect("failed to get storage"); 128 | let mut people: PeopleStore = self.clone().into(); 129 | people.store(&mut local_store); 130 | *self = PeopleModel::from(people, self.on_save.take(), self.link.clone()); 131 | 132 | self.on_save.as_ref().map(|e| e.emit(self.inc)); 133 | true 134 | } 135 | PeopleMsg::AddPerson => { 136 | debug!("adding a Person"); 137 | let person = Person::new("Jane Doe", Ability::Adult); 138 | self.people.push((person, true)); 139 | true 140 | } 141 | PeopleMsg::EditPerson(id) => { 142 | debug!("edit person: {}", id); 143 | self.people 144 | .get_mut(id) 145 | .map(|p| { 146 | if !p.1 { 147 | p.1 = true; 148 | true 149 | } else { 150 | false 151 | } 152 | }) 153 | .unwrap_or(false) 154 | } 155 | PeopleMsg::DeletePerson(idx) => { 156 | let person = self.people.remove(idx); 157 | debug!("deleted {:?}", person); 158 | true 159 | } 160 | PeopleMsg::PersonNameInput(id, name) => self 161 | .people 162 | .get_mut(id) 163 | .map(|p| { 164 | debug!("saving name: {}", name); 165 | if p.0.name() != name { 166 | p.0.set_name(name); 167 | true 168 | } else { 169 | false 170 | } 171 | }) 172 | .unwrap_or(false), 173 | PeopleMsg::PersonAbilityInput(id, ability) => self 174 | .people 175 | .get_mut(id) 176 | .map(|p| { 177 | debug!("saving name: {}", ability); 178 | if p.0.ability() != ability { 179 | p.0.set_ability(ability); 180 | true 181 | } else { 182 | false 183 | } 184 | }) 185 | .unwrap_or(false), 186 | } 187 | } 188 | 189 | fn view(&self) -> Html { 190 | // let select = |is_selected: bool| { 191 | // html!{ 192 | // 193 | // } 194 | // }; 195 | 196 | let edit_delete = |id: Id, is_editting: IsEditting, link: &ComponentLink| { 197 | let on_edit = link.callback(PeopleMsg::EditPerson); 198 | let on_delete = link.callback(PeopleMsg::DeletePerson); 199 | 200 | html! { 201 | 202 | } 203 | }; 204 | let person_row = |id: Id, person: &(Person, IsEditting), link: &ComponentLink| { 205 | let name_on_input = link.callback(|(i, n)| PeopleMsg::PersonNameInput(i, n)); 206 | let ability_on_input = link.callback(|(i, a)| PeopleMsg::PersonAbilityInput(i, a)); 207 | 208 | html! { 209 | 210 | 211 | 212 | { edit_delete(id, person.1, &self.link) } 213 | 214 | } 215 | }; 216 | 217 | html! { 218 | <> 219 |

{"All the beautiful people"}

220 | 221 | 222 | 223 | 224 | 225 | { for self.people.iter().enumerate().map(|(i, p)| person_row(i, p, &self.link)) } 226 | 227 | 228 | 237 | 238 |
{"Person"}{"Ability"}{" "}
229 | 232 | 235 | //button onclick=|_| PeopleMsg::SavePeople, >{"Save all the People"} 236 |
239 | 240 | } 241 | } 242 | } 243 | 244 | // #[derive(Clone, Eq, PartialEq, Default)] 245 | // struct Select { 246 | // is_selected: bool, 247 | // } 248 | 249 | // impl Component for Select { 250 | // type Message = (); 251 | // type Properties = Self; 252 | 253 | // fn create(props: Self::Properties, _context: &mut Env) -> Self { 254 | // Self { 255 | // is_selected: props.is_selected, 256 | // } 257 | // } 258 | 259 | // fn update(&mut self, _msg: Self::Message, _context: &mut Env) -> ShouldRender { 260 | // true 261 | // } 262 | 263 | // fn change( 264 | // &mut self, 265 | // _props: Self::Properties, 266 | // _context: &mut Env, 267 | // ) -> ShouldRender { 268 | // true 269 | // } 270 | // } 271 | 272 | // impl Renderable for Select { 273 | // fn view(&self) -> Html { 274 | // html! { 275 | // 276 | // } 277 | // } 278 | // } 279 | 280 | /// EditDelete Component for a person row 281 | #[derive(Clone)] 282 | struct EditDelete { 283 | id: Id, 284 | is_editting: IsEditting, 285 | on_edit: Option>, 286 | on_delete: Option>, 287 | link: ComponentLink, 288 | } 289 | 290 | #[derive(Clone, PartialEq, Default, Properties)] 291 | struct EditDeleteProps { 292 | pub id: Id, 293 | pub is_editting: IsEditting, 294 | pub on_edit: Option>, 295 | pub on_delete: Option>, 296 | } 297 | 298 | enum EditDeleteMsg { 299 | Edit, 300 | Delete, 301 | } 302 | 303 | impl Component for EditDelete { 304 | type Message = EditDeleteMsg; 305 | type Properties = EditDeleteProps; 306 | 307 | fn create(props: Self::Properties, link: ComponentLink) -> Self { 308 | Self { 309 | id: props.id, 310 | is_editting: props.is_editting, 311 | on_edit: props.on_edit, 312 | on_delete: props.on_delete, 313 | link, 314 | } 315 | } 316 | 317 | fn update(&mut self, msg: Self::Message) -> ShouldRender { 318 | match msg { 319 | EditDeleteMsg::Edit => { 320 | debug!("editting: {}", self.id); 321 | if !self.is_editting { 322 | self.on_edit.as_ref().map(|c| c.emit(self.id)); 323 | } 324 | } 325 | EditDeleteMsg::Delete => { 326 | debug!("deleting: {}", self.id); 327 | self.on_delete.as_ref().map(|c| c.emit(self.id)); 328 | } 329 | } 330 | 331 | false 332 | } 333 | 334 | fn change(&mut self, props: Self::Properties) -> ShouldRender { 335 | if self.is_editting != props.is_editting { 336 | self.is_editting = props.is_editting; 337 | return true; 338 | } 339 | false 340 | } 341 | 342 | fn view(&self) -> Html { 343 | let disabled = if self.is_editting { "disabled" } else { "" }; 344 | 345 | html! { 346 |
347 |