A sorter site for Idolm@ster Characters
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Filtering, querystring parsing, data versioning, src folders

+2901 -1464
+24 -15
index.html
··· 1 1 <html> 2 2 3 3 <head> 4 - <link rel="shortcut icon" href="src/yinyang.ico" type="image/x-icon"> 5 - <link rel="icon" href="src/yinyang.ico" type="image/x-icon"> 4 + <!-- Metadata --> 5 + <link rel="shortcut icon" href="src/assets/yinyang.ico" type="image/x-icon"> 6 + <link rel="icon" href="src/assets/yinyang.ico" type="image/x-icon"> 6 7 <meta charset="utf-8"> 7 8 <meta name="og:site_name" content="Touhou Character Sorting"> 8 9 <meta name="og:description" content="A simple website for sorting Touhou characters in a formatted list."> 9 10 <meta name="og:image" content="https://i.imgur.com/IZzJMk6.jpg"> 10 - <title>Touhou Character Sorting</title> 11 + <title>Touhou Character Sorting</title> 12 + 13 + <link rel="stylesheet" type="text/css" href="src/css/reset.css"> 14 + <link rel="stylesheet" type="text/css" href="src/css/styles.css"> 15 + 16 + <script src="src/js/data.js"></script> 17 + <script src="src/js/data/2017-05-01.js"></script> 18 + <script src="src/js/data/2018-02-20.js"></script> 19 + 20 + <script src="src/js/lz-string.min.js"></script> 21 + <script src="src/js/seedrandom.min.js"></script> 22 + <script src="src/js/main.js"></script> 23 + 11 24 <!-- 4chan identifier tripcode: !FreliaEfl2 --> 12 - <link rel="stylesheet" type="text/css" href="src/reset.css"> 13 - <link rel="stylesheet" type="text/css" href="src/styles.css"> 14 - <script src="src/data.js"></script> 15 - <script src="src/seedrandom.min.js"></script> 16 - <script src="src/main.js"></script> 17 25 </head> 18 26 19 27 <body> ··· 27 35 </div> 28 36 29 37 <div class="sorter"> 30 - <img src="src/defaultL.jpg" class="left sort image"> 38 + <img src="src/assets/defaultL.jpg" class="left sort image"> 31 39 32 - <div class="starting button">Touhou Project Character Sorter<br><br>Click to Start!</div> 33 - <div class="starting button">Load Progress</div> 34 - <div class="sorting button">Tie</div> 35 - <div class="sorting button">Undo</div> 36 - <div class="sorting button">Save Progress</div> 40 + <div class="starting start button">Touhou Project Character Sorter<br><br>Click to Start!</div> 41 + <div class="starting load button">Load Progress</div> 42 + <div class="sorting tie button">Tie</div> 43 + <div class="sorting undo button">Undo</div> 44 + <div class="sorting save button">Save Progress</div> 37 45 38 - <img src="src/defaultR.jpg" class="right sort image"> 46 + <img src="src/assets/defaultR.jpg" class="right sort image"> 39 47 40 48 <div class="left sort text"></div> 41 49 <div class="right sort text"></div> ··· 50 58 <br><br> 51 59 52 60 <p>Sorter for Touhou characters. Pick your sources, and hit the Start button.</p> 61 + <p><strong>Certain options have details that you can hover to read.</strong></p> 53 62 <p>Click on the character you like better from the two, or tie them if you like them equally or don't know them.</p> 54 63 <p>Depending on how many sources you pick, you'll get up to 700+ picks, so set aside a good few cups of tea for this.</p> 55 64
+147
src/css/styles.css
··· 1 + body { 2 + font-family: Arial, Helvetica, sans-serif; 3 + font-size: 16px; 4 + } 5 + 6 + .container { 7 + display: grid; 8 + grid-row-gap: 1em; 9 + margin-top: 1em; 10 + } 11 + 12 + .progress { 13 + margin: 1em auto; 14 + width: 500px; 15 + display: none; 16 + } 17 + 18 + .progressbar { 19 + position: relative; 20 + width: 492px; 21 + border: 1px solid black; 22 + padding: 3px; 23 + margin: 2px 0px; 24 + } 25 + 26 + .progresstext { 27 + position: absolute; 28 + width: 492px; 29 + margin: 3px 0px; 30 + text-align: center; 31 + font-size: 0.7em; 32 + } 33 + 34 + .progressfill { 35 + height: 20px; 36 + background-color: lightgreen; 37 + width: 79%; 38 + } 39 + 40 + .sorter { 41 + margin: 0px auto; 42 + display: grid; 43 + grid-template-columns: 120px 1fr 120px; 44 + grid-gap: 5px; 45 + width: 420px; 46 + } 47 + 48 + .sorter > * { 49 + cursor: pointer; 50 + } 51 + 52 + .button { 53 + border: 1px solid black; 54 + text-align: center; 55 + padding: 10%; 56 + grid-column: 2 / 3; 57 + } 58 + 59 + .starting.button { 60 + grid-row: span 3; 61 + } 62 + 63 + .sorting.button { 64 + grid-row: span 2; 65 + display: none; 66 + } 67 + 68 + .sorter > .image { 69 + width: 120px; 70 + height: 180px; 71 + margin: auto; 72 + grid-row: 1 / 7; 73 + } 74 + 75 + .sorter > .text { 76 + width: 120px; 77 + height: 60px; 78 + display: none; 79 + } 80 + 81 + .sorter > .text > p { 82 + margin: 5px 5px; 83 + width: calc(100%-10px); 84 + text-align: center; 85 + font-size: 0.8em; 86 + line-height: 1.5em; 87 + } 88 + 89 + .sorter > .left { 90 + grid-column: 1 / 2; 91 + border: 1px solid #000000; 92 + } 93 + 94 + .sorter > .right { 95 + grid-column: 3 / 4; 96 + border: 1px solid #000000; 97 + } 98 + 99 + .options { 100 + margin: 1em auto; 101 + display: grid; 102 + text-align: left; 103 + grid-template-columns: repeat(3, 1fr); 104 + grid-gap: 10px; 105 + width: 450px; 106 + } 107 + 108 + .options > div { 109 + font-size: 0.5625em; 110 + } 111 + 112 + label { 113 + cursor: pointer; 114 + } 115 + 116 + label.tooltip:hover { 117 + color: #990000; 118 + } 119 + 120 + .options > .large.option, .options > hr { 121 + grid-column: span 3; 122 + text-align: center; 123 + width: 100%; 124 + } 125 + 126 + .info { 127 + margin: 0px auto 3em; 128 + display: block; 129 + text-align: center; 130 + font-size: 0.6875em; 131 + width: 80%; 132 + line-height: 1.2em; 133 + } 134 + 135 + a { 136 + color: #990000; 137 + font-weight: bold; 138 + text-decoration: none; 139 + } 140 + 141 + a:hover { 142 + color: #FF6600; 143 + } 144 + 145 + a:visited { 146 + color: #6600FF; 147 + }
-1266
src/data.js
··· 1 - const imageRoot = "https://i.imgur.com/"; 2 - 3 - /** 4 - * Sorter options. Characters will be removed from the sorting array based on selected options, working down the array. 5 - * 6 - * @type {{name: string, key: string, tooltip?: string, checked?: boolean, sub?: {name: string, tooltip?: string, checked?: string}[]}[]} 7 - */ 8 - const options = [ 9 - { 10 - name: "Filter by Series Entry", 11 - key: "series", 12 - tooltip: "Check this to remove certain series entries.", 13 - checked: false, 14 - sub: [ 15 - { name: "Books and CDs" }, 16 - { name: "The Highly Responsive to Prayers", tooltip: "01 - Reiiden" }, 17 - { name: "The Story of Eastern Wonderland", tooltip: "02 - Fuumaroku" }, 18 - { name: "Phantasmagoria of Dim.Dream", tooltip: "03 - Yumejikuu" }, 19 - { name: "Lotus Land Story", tooltip: "04 - Gensoukyou" }, 20 - { name: "Mystic Square", tooltip: "05 - Kaikidan" }, 21 - { name: "Embodiment of Scarlet Devil", tooltip: "06 - Koumakan" }, 22 - { name: "Perfect Cherry Blossom", tooltip: "07 - Youyoumu" }, 23 - { name: "Immaterial and Missing Power", tooltip: "07.5 - Suimusou" }, 24 - { name: "Imperishable Night", tooltip: "08 - Eiyashou" }, 25 - { name: "Phantasmagoria of Flower View", tooltip: "09 - Kaeidzuka" }, 26 - { name: "Shoot the Bullet", tooltip: "09.5 - Bunkachou" }, 27 - { name: "Mountain of Faith", tooltip: "10 - Fuujinroku" }, 28 - { name: "Scarlet Weather Rhapsody", tooltip: "10.5 - Hisouten" }, 29 - { name: "Subterranean Animism", tooltip: "11 - Chireiden" }, 30 - { name: "Undefined Fantastic Object", tooltip: "12 - Seirensen" }, 31 - { name: "Touhou Hisoutensoku", tooltip: "12.3 - Hisoutensoku" }, 32 - { name: "Double Spoiler", tooltip: "12.5 - Bunkachou" }, 33 - { name: "Great Fairy Wars", tooltip: "12.8 - Daisensou" }, 34 - { name: "Ten Desires", tooltip: "13 - Shinreibyou" }, 35 - { name: "Hopeless Masquerade", tooltip: "13.5 - Shinkirou" }, 36 - { name: "Double Dealing Character", tooltip: "14 - Kishinjou" }, 37 - { name: "Impossible Spell Card", tooltip: "14.3 - Amanojaku" }, 38 - { name: "Urban Legend in Limbo", tooltip: "14.5 - Shinpiroku" }, 39 - { name: "Legacy of Lunatic Kingdom", tooltip: "15 - Kanjuden" }, 40 - { name: "Antinomy of Common Flowers", tooltip: "15.5 - Hyouibana" }, 41 - { name: "Hidden Star in Four Seasons", tooltip: "16 - Tenkuushou" } 42 - ] 43 - }, 44 - { 45 - name: "Filter by Stage Appearances", 46 - key: "stage", 47 - tooltip: "Check this to remove certain stages.", 48 - checked: false, 49 - sub: [ 50 - { name: "Stage 1" }, 51 - { name: "Stage 2" }, 52 - { name: "Stage 3" }, 53 - { name: "Stage 4" }, 54 - { name: "Stage 5/Penultimate", tooltip: "Stage 4 in 5-stage games, and Stage 8 in 9-stage games." 55 - }, 56 - { name: "Stage 6/Final" }, 57 - { name: "Stage EX/Phantasm" } 58 - ] 59 - }, 60 - { 61 - name: "Include PC-98 Duplicates", 62 - key: "pc98", 63 - tooltip: 64 - "Check this to include PC-98 characters with a Windows counterpart.", 65 - checked: false 66 - }, 67 - { 68 - name: "Include Non-Girls", 69 - key: "notgirl", 70 - tooltip: "Check this to include certain non-female characters.", 71 - checked: false 72 - } 73 - ]; 74 - 75 - const characterData = [ 76 - { 77 - name: "Hakurei Reimu", 78 - img: "c5DqpgX.png", 79 - opts: { 80 - series: [0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 20, 21, 22, 23, 24, 25, 26], 81 - stage: [3] 82 - } 83 - }, 84 - { 85 - name: "Kirisame Marisa", 86 - img: "tJnkSzK.png", 87 - opts: { 88 - series: [0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26], 89 - stage: [3, 6] 90 - } 91 - }, 92 - { 93 - name: "Rumia", 94 - img: "0YT7QlS.png", 95 - opts: { 96 - series: [0, 6, 11, 20], 97 - stage: [0] 98 - } 99 - }, 100 - { 101 - name: "Daiyousei", 102 - img: "NWlZud3.png", 103 - opts: { 104 - series: [0, 6], 105 - stage: [1, 6] 106 - } 107 - }, 108 - { 109 - name: "Cirno", 110 - img: "qdveFSy.png", 111 - opts: { 112 - series: [0, 6, 7, 10, 11, 16, 18, 20, 21, 22, 26], 113 - stage: [1] 114 - } 115 - }, 116 - { 117 - name: "Hong Meiling", 118 - img: "ptGp0x4.png", 119 - opts: { 120 - series: [0, 6, 8, 11, 16], 121 - stage: [2] 122 - } 123 - }, 124 - { 125 - name: "Koakuma", 126 - img: "vBKdDm4.png", 127 - opts: { 128 - series: [0, 6], 129 - stage: [3] 130 - } 131 - }, 132 - { 133 - name: "Patchouli Knowledge", 134 - img: "A7ZnuHo.png", 135 - opts: { 136 - series: [0, 6, 8, 11, 12, 16, 20], 137 - stage: [3, 6] 138 - } 139 - }, 140 - { 141 - name: "Izayoi Sakuya", 142 - img: "sgZPf11.png", 143 - opts: { 144 - series: [0, 6, 7, 8, 9, 10, 11, 12, 16, 20, 21, 22], 145 - stage: [4, 5] 146 - } 147 - }, 148 - { 149 - name: "Remilia Scarlet", 150 - img: "8UX7hKE.png", 151 - opts: { 152 - series: [0, 6, 8, 9, 11, 12, 16, 20, 22], 153 - stage: [5] 154 - } 155 - }, 156 - { 157 - name: "Flandre Scarlet", 158 - img: "OhaDcnc.png", 159 - opts: { 160 - series: [0, 6, 11], 161 - stage: [6] 162 - } 163 - }, 164 - { 165 - name: "Letty Whiterock", 166 - img: "MgzqjFK.png", 167 - opts: { 168 - series: [0, 7, 11, 20], 169 - stage: [0] 170 - } 171 - }, 172 - { 173 - name: "Chen", 174 - img: "ohmetZh.png", 175 - opts: { 176 - series: [0, 7, 8, 11, 12, 16, 20], 177 - stage: [1, 6] 178 - } 179 - }, 180 - { 181 - name: "Alice Margatroid", 182 - img: "aDIf0pN.png", 183 - opts: { 184 - series: [0, 7, 8, 9, 11, 12, 16, 20], 185 - stage: [2] 186 - } 187 - }, 188 - { 189 - name: "Lily White", 190 - img: "2Pr8b2N.png", 191 - opts: { 192 - series: [0, 7, 10, 20, 26], 193 - stage: [3, 6] 194 - } 195 - }, 196 - { 197 - name: "Lunasa Prismriver", 198 - img: "htOMdDQ.png", 199 - opts: { 200 - series: [0, 7, 10, 20, 25], 201 - stage: [3] 202 - } 203 - }, 204 - { 205 - name: "Merlin Prismriver", 206 - img: "PrRPujP.png", 207 - opts: { 208 - series: [0, 7, 10, 20, 25], 209 - stage: [3] 210 - } 211 - }, 212 - { 213 - name: "Lyrica Prismriver", 214 - img: "ze79bFC.png", 215 - opts: { 216 - series: [0, 7, 10, 20, 25], 217 - stage: [3] 218 - } 219 - }, 220 - { 221 - name: "Konpaku Youmu", 222 - img: "WMjyRLJ.png", 223 - opts: { 224 - series: [0, 7, 8, 9, 10, 11, 12, 16, 19, 20, 22], 225 - stage: [4, 5] 226 - } 227 - }, 228 - { 229 - name: "Saigyouji Yuyuko", 230 - img: "VT9mTGb.png", 231 - opts: { 232 - series: [0, 7, 8, 9, 11, 12, 16, 19, 20, 22], 233 - stage: [0, 5] 234 - } 235 - }, 236 - { 237 - name: "Yakumo Ran", 238 - img: "rshnJPV.png", 239 - opts: { 240 - series: [0, 7, 8, 9, 11, 12, 16, 20], 241 - stage: [6] 242 - } 243 - }, 244 - { 245 - name: "Yakumo Yukari", 246 - img: "qsceD4I.png", 247 - opts: { 248 - series: [0, 7, 8, 9, 11, 12, 16, 20, 22, 25], 249 - stage: [6] 250 - } 251 - }, 252 - { 253 - name: "Ibuki Suika", 254 - img: "pLdMjQ3.png", 255 - opts: { 256 - series: [0, 8, 11, 12, 16, 17, 20, 22], 257 - stage: [5] 258 - } 259 - }, 260 - { 261 - name: "Wriggle Nightbug", 262 - img: "8DLUAPf.png", 263 - opts: { 264 - series: [0, 9, 11, 20], 265 - stage: [0] 266 - } 267 - }, 268 - { 269 - name: "Mystia Lorelei", 270 - img: "6KyhLqE.png", 271 - opts: { 272 - series: [0, 9, 10, 11, 20], 273 - stage: [1] 274 - } 275 - }, 276 - { 277 - name: "Kamishirasawa Keine", 278 - img: "99w0Chm.png", 279 - opts: { 280 - series: [0, 9, 11, 20, 22], 281 - stage: [2, 6] 282 - } 283 - }, 284 - { 285 - name: "Inaba Tewi", 286 - img: "yqNfNje.png", 287 - opts: { 288 - series: [0, 9, 10, 11, 20], 289 - stage: [4] 290 - } 291 - }, 292 - { 293 - name: "Reisen Udongein Inaba", 294 - img: "PrYzRcC.png", 295 - opts: { 296 - series: [0, 9, 10, 11, 16, 20, 24, 25], 297 - stage: [4] 298 - } 299 - }, 300 - { 301 - name: "Yagokoro Eirin", 302 - img: "ceo4DhK.png", 303 - opts: { 304 - series: [0, 9, 11, 20], 305 - stage: [5] 306 - } 307 - }, 308 - { 309 - name: "Houraisan Kaguya", 310 - img: "2YDuTk3.png", 311 - opts: { 312 - series: [0, 9, 11, 20], 313 - stage: [5] 314 - } 315 - }, 316 - { 317 - name: "Fujiwara no Mokou", 318 - img: "3zo4VKV.png", 319 - opts: { 320 - series: [0, 9, 11, 20, 22, 23, 25], 321 - stage: [6] 322 - } 323 - }, 324 - { 325 - name: "Shameimaru Aya", 326 - img: "8TLXMST.png", 327 - opts: { 328 - series: [0, 10, 11, 12, 13, 16, 20, 22, 26], 329 - stage: [3] 330 - } 331 - }, 332 - { 333 - name: "Medicine Melancholy", 334 - img: "IImsp7K.png", 335 - opts: { 336 - series: [0, 10, 11, 20], 337 - stage: [3] 338 - } 339 - }, 340 - { 341 - name: "Kazami Yuuka", 342 - img: "MZXJQq5.png", 343 - opts: { 344 - series: [0, 10, 11, 20], 345 - stage: [4] 346 - } 347 - }, 348 - { 349 - name: "Onozuka Komachi", 350 - img: "aX4WIH8.png", 351 - opts: { 352 - series: [0, 10, 11, 12, 16, 20], 353 - stage: [4] 354 - } 355 - }, 356 - { 357 - name: "Shiki Eiki, Yamaxanadu", 358 - img: "nPBvatH.png", 359 - opts: { 360 - series: [0, 10, 11, 20], 361 - stage: [5] 362 - } 363 - }, 364 - { 365 - name: "Aki Shizuha", 366 - img: "3pDRgvR.png", 367 - opts: { 368 - series: [13, 17, 20], 369 - stage: [0] 370 - } 371 - }, 372 - { 373 - name: "Aki Minoriko", 374 - img: "bV0DaN7.png", 375 - opts: { 376 - series: [13, 17, 20], 377 - stage: [0] 378 - } 379 - }, 380 - { 381 - name: "Kagiyama Hina", 382 - img: "J11NjNj.png", 383 - opts: { 384 - series: [13, 17, 20], 385 - stage: [1] 386 - } 387 - }, 388 - { 389 - name: "Kawashiro Nitori", 390 - img: "4Ufced2.png", 391 - opts: { 392 - series: [13, 17, 20, 22, 23, 25], 393 - stage: [2] 394 - } 395 - }, 396 - { 397 - name: "Inubashiri Momiji", 398 - img: "qGMjnYk.png", 399 - opts: { 400 - series: [13, 17, 22], 401 - stage: [3] 402 - } 403 - }, 404 - { 405 - name: "Kochiya Sanae", 406 - img: "ATTRSWU.png", 407 - opts: { 408 - series: [13, 14, 15, 16, 17, 19, 20, 22, 24], 409 - stage: [4, 6] 410 - } 411 - }, 412 - { 413 - name: "Yasaka Kanako", 414 - img: "nQ78Lz7.png", 415 - opts: { 416 - series: [13, 16, 17, 20, 22], 417 - stage: [5, 6] 418 - } 419 - }, 420 - { 421 - name: "Moriya Suwako", 422 - img: "yJaD5ZV.png", 423 - opts: { 424 - series: [13, 14, 16, 17, 20, 22], 425 - stage: [6] 426 - } 427 - }, 428 - { 429 - name: "Nagae Iku", 430 - img: "xgAlECj.png", 431 - opts: { 432 - series: [12, 16, 17, 20], 433 - stage: [4] 434 - } 435 - }, 436 - { 437 - name: "Hinanawi Tenshi", 438 - img: "tZLYivt.png", 439 - opts: { 440 - series: [12, 16, 17, 20, 22, 25], 441 - stage: [5] 442 - } 443 - }, 444 - { 445 - name: "Kisume", 446 - img: "VgJgaEf.png", 447 - opts: { 448 - series: [14, 17], 449 - stage: [0] 450 - } 451 - }, 452 - { 453 - name: "Kurodani Yamame", 454 - img: "sqgJ2St.png", 455 - opts: { 456 - series: [14, 17, 20], 457 - stage: [0] 458 - } 459 - }, 460 - { 461 - name: "Mizuhashi Parsee", 462 - img: "lkoAJod.png", 463 - opts: { 464 - series: [14, 17, 20], 465 - stage: [1] 466 - } 467 - }, 468 - { 469 - name: "Hoshiguma Yuugi", 470 - img: "tDO653L.png", 471 - opts: { 472 - series: [14, 17, 20], 473 - stage: [2] 474 - } 475 - }, 476 - { 477 - name: "Komeiji Satori", 478 - img: "dup7Nt6.png", 479 - opts: { 480 - series: [14, 17, 20], 481 - stage: [3] 482 - } 483 - }, 484 - { 485 - name: "Kaenbyou Rin (Orin)", 486 - img: "uQjbw1W.png", 487 - opts: { 488 - series: [14, 17, 20], 489 - stage: [3, 4, 5] 490 - } 491 - }, 492 - { 493 - name: "Reiuji Utsuho (Okuu)", 494 - img: "DfdaXPW.png", 495 - opts: { 496 - series: [14, 16, 17, 20], 497 - stage: [5] 498 - } 499 - }, 500 - { 501 - name: "Komeiji Koishi", 502 - img: "wVCcens.png", 503 - opts: { 504 - series: [14, 17, 20, 23, 25], 505 - stage: [6] 506 - } 507 - }, 508 - { 509 - name: "Nazrin", 510 - img: "EpHQbiY.png", 511 - opts: { 512 - series: [15, 17, 20], 513 - stage: [0, 4] 514 - } 515 - }, 516 - { 517 - name: "Tatara Kogasa", 518 - img: "kJbv4dc.png", 519 - opts: { 520 - series: [15, 17, 20], 521 - stage: [1, 6] 522 - } 523 - }, 524 - { 525 - name: "Kumoi Ichirin", 526 - img: "Fyn5yVx.png", 527 - opts: { 528 - series: [15, 17, 20, 23, 25], 529 - stage: [2] 530 - } 531 - }, 532 - { 533 - name: "Murasa Minamitsu", 534 - img: "39KYpvW.png", 535 - opts: { 536 - series: [15, 17, 20], 537 - stage: [3] 538 - } 539 - }, 540 - { 541 - name: "Toramaru Shou", 542 - img: "8bMDDAo.png", 543 - opts: { 544 - series: [15, 17, 20], 545 - stage: [4] 546 - } 547 - }, 548 - { 549 - name: "Hijiri Byakuren", 550 - img: "2ppPxny.png", 551 - opts: { 552 - series: [15, 17, 20, 22, 23, 25], 553 - stage: [5] 554 - } 555 - }, 556 - { 557 - name: "Houjuu Nue", 558 - img: "zL4S8Mj.png", 559 - opts: { 560 - series: [15, 17, 19, 20], 561 - stage: [3, 5, 6] 562 - } 563 - }, 564 - { 565 - name: "Himekaidou Hatate", 566 - img: "LgvoTaJ.png", 567 - opts: { 568 - series: [17, 20, 22], 569 - stage: [6] 570 - } 571 - }, 572 - { 573 - name: "Sunny Milk", 574 - img: "VbqXiB6.png", 575 - opts: { 576 - series: [0, 18, 20], 577 - stage: [0, 1, 2] 578 - } 579 - }, 580 - { 581 - name: "Luna Child", 582 - img: "OBqgP48.png", 583 - opts: { 584 - series: [0, 18, 20], 585 - stage: [0, 1, 2] 586 - } 587 - }, 588 - { 589 - name: "Star Sapphire", 590 - img: "sNw61ap.png", 591 - opts: { 592 - series: [0, 18, 20], 593 - stage: [0, 1, 2] 594 - } 595 - }, 596 - { 597 - name: "Kasodani Kyouko", 598 - img: "sLiqEBA.png", 599 - opts: { 600 - series: [19, 20, 22], 601 - stage: [1] 602 - } 603 - }, 604 - { 605 - name: "Miyako Yoshika", 606 - img: "6jq6eh6.png", 607 - opts: { 608 - series: [19, 20, 22], 609 - stage: [2, 3] 610 - } 611 - }, 612 - { 613 - name: "Kaku Seiga", 614 - img: "090hLPL.png", 615 - opts: { 616 - series: [19, 20, 22], 617 - stage: [3] 618 - } 619 - }, 620 - { 621 - name: "Soga no Tojiko", 622 - img: "y0UXwFO.png", 623 - opts: { 624 - series: [19, 20], 625 - stage: [4] 626 - } 627 - }, 628 - { 629 - name: "Mononobe no Futo", 630 - img: "WTZ97LE.png", 631 - opts: { 632 - series: [19, 20, 22, 23, 25], 633 - stage: [4] 634 - } 635 - }, 636 - { 637 - name: "Toyosatomimi no Miko", 638 - img: "3Xiqd22.png", 639 - opts: { 640 - series: [19, 20, 22, 23, 25], 641 - stage: [5] 642 - } 643 - }, 644 - { 645 - name: "Futatsuiwa Mamizou", 646 - img: "gMpWdmA.png", 647 - opts: { 648 - series: [19, 20, 22, 23, 25], 649 - stage: [6] 650 - } 651 - }, 652 - { 653 - name: "Hata no Kokoro", 654 - img: "fxCGmUk.png", 655 - opts: { 656 - series: [0, 20, 23, 25], 657 - stage: [5] 658 - } 659 - }, 660 - { 661 - name: "Wakasagihime", 662 - img: "brWCLVx.png", 663 - opts: { 664 - series: [21, 22], 665 - stage: [0] 666 - } 667 - }, 668 - { 669 - name: "Sekibanki", 670 - img: "VAMLiJD.png", 671 - opts: { 672 - series: [21, 22], 673 - stage: [1] 674 - } 675 - }, 676 - { 677 - name: "Imaizumi Kagerou", 678 - img: "b5UMjD8.png", 679 - opts: { 680 - series: [21, 22], 681 - stage: [2] 682 - } 683 - }, 684 - { 685 - name: "Tsukumo Benben", 686 - img: "vWNeMaH.png", 687 - opts: { 688 - series: [21, 22], 689 - stage: [3, 6] 690 - } 691 - }, 692 - { 693 - name: "Tsukumo Yatsuhashi", 694 - img: "EJFQHQN.png", 695 - opts: { 696 - series: [21, 22], 697 - stage: [3, 6] 698 - } 699 - }, 700 - { 701 - name: "Kijin Seija", 702 - img: "16RUacj.png", 703 - opts: { 704 - series: [21, 22], 705 - stage: [4, 5] 706 - } 707 - }, 708 - { 709 - name: "Sukuna Shinmyoumaru", 710 - img: "Zl2tN7W.png", 711 - opts: { 712 - series: [21, 22, 23, 25], 713 - stage: [5] 714 - } 715 - }, 716 - { 717 - name: "Horikawa Raiko", 718 - img: "SLLEccR.png", 719 - opts: { 720 - series: [21, 22, 25], 721 - stage: [6] 722 - } 723 - }, 724 - { 725 - name: "Usami Sumireko", 726 - img: "mc7ICW6.png", 727 - opts: { 728 - series: [23, 25], 729 - stage: [5] 730 - } 731 - }, 732 - { 733 - name: "Seiran", 734 - img: "0ra00WG.png", 735 - opts: { 736 - series: [24], 737 - stage: [0] 738 - } 739 - }, 740 - { 741 - name: "Ringo", 742 - img: "xQOsFlZ.png", 743 - opts: { 744 - series: [24], 745 - stage: [1] 746 - } 747 - }, 748 - { 749 - name: "Doremy Sweet", 750 - img: "rGS7dyn.png", 751 - opts: { 752 - series: [24, 25], 753 - stage: [2, 6] 754 - } 755 - }, 756 - { 757 - name: "Kishin Sagume", 758 - img: "HLT338X.png", 759 - opts: { 760 - series: [24], 761 - stage: [3] 762 - } 763 - }, 764 - { 765 - name: "Clownpiece", 766 - img: "9Jje7ZQ.jpg", 767 - opts: { 768 - series: [24], 769 - stage: [4] 770 - } 771 - }, 772 - { 773 - name: "Junko", 774 - img: "NsfLZjY.jpg", 775 - opts: { 776 - series: [24], 777 - stage: [5, 6] 778 - } 779 - }, 780 - { 781 - name: "Hecatia Lapislazuli", 782 - img: "EH3Ulol.png", 783 - opts: { 784 - series: [24], 785 - stage: [6] 786 - } 787 - }, 788 - { 789 - name: "Eternity Larva", 790 - img: "ql5KxJH.png", 791 - opts: { 792 - series: [26], 793 - stage: [0] 794 - } 795 - }, 796 - { 797 - name: "Sakata Nemuno", 798 - img: "gYFhJ3Y.png", 799 - opts: { 800 - series: [26], 801 - stage: [1] 802 - } 803 - }, 804 - { 805 - name: "Komano Aunn", 806 - img: "wWUStkF.png", 807 - opts: { 808 - series: [26], 809 - stage: [2] 810 - } 811 - }, 812 - { 813 - name: "Yatadera Narumi", 814 - img: "wrPVhAo.png", 815 - opts: { 816 - series: [26], 817 - stage: [3] 818 - } 819 - }, 820 - { 821 - name: "Nishida Satono", 822 - img: "8w9gAy2.png", 823 - opts: { 824 - series: [26], 825 - stage: [4] 826 - } 827 - }, 828 - { 829 - name: "Teireida Mai", 830 - img: "DesAqAC.png", 831 - opts: { 832 - series: [26], 833 - stage: [4] 834 - } 835 - }, 836 - { 837 - name: "Matara Okina", 838 - img: "icWvMyo.png", 839 - opts: { 840 - series: [26], 841 - stage: [5, 6] 842 - } 843 - }, 844 - { 845 - name: "Yorigami Shion", 846 - img: "LenxXR4.png", 847 - opts: { 848 - series: [25], 849 - stage: [5] 850 - } 851 - }, 852 - { 853 - name: "Yorigami Joon", 854 - img: "0aPaTm8.png", 855 - opts: { 856 - series: [25], 857 - stage: [5] 858 - } 859 - }, 860 - { 861 - name: "Hieda no Akyuu", 862 - img: "ogONuLZ.png", 863 - opts: { 864 - series: [0], 865 - stage: [] 866 - } 867 - }, 868 - { 869 - name: "Tokiko", 870 - img: "Y4maOc8.png", 871 - opts: { 872 - series: [0], 873 - stage: [] 874 - } 875 - }, 876 - { 877 - name: "Reisen (Manga)", 878 - img: "cWjCo2j.png", 879 - opts: { 880 - series: [0], 881 - stage: [] 882 - } 883 - }, 884 - { 885 - name: "Watatsuki no Toyohime", 886 - img: "uEBxsEX.png", 887 - opts: { 888 - series: [0], 889 - stage: [] 890 - } 891 - }, 892 - { 893 - name: "Watatsuki no Yorihime", 894 - img: "Txu2P7S.png", 895 - opts: { 896 - series: [0], 897 - stage: [] 898 - } 899 - }, 900 - { 901 - name: "Maribel Hearn", 902 - img: "XUI9vPo.png", 903 - opts: { 904 - series: [0], 905 - stage: [] 906 - } 907 - }, 908 - { 909 - name: "Usami Renko", 910 - img: "1P5EXRt.png", 911 - opts: { 912 - series: [0], 913 - stage: [] 914 - } 915 - }, 916 - { 917 - name: "Ibaraki Kasen", 918 - img: "dQHnPPe.png", 919 - opts: { 920 - series: [0, 23], 921 - stage: [4] 922 - } 923 - }, 924 - { 925 - name: "Motoori Kosuzu", 926 - img: "jEsJJo8.png", 927 - opts: { 928 - series: [0], 929 - stage: [] 930 - } 931 - }, 932 - { 933 - name: "Hakurei Reimu (PC-98)", 934 - img: "IZsGAMS.png", 935 - opts: { 936 - series: [1, 2, 3, 4, 5], 937 - stage: [3], 938 - pc98: true 939 - } 940 - }, 941 - { 942 - name: "Shingyoku (Female)", 943 - img: "KuPiR2k.png", 944 - opts: { 945 - series: [1], 946 - stage: [0] 947 - } 948 - }, 949 - { 950 - name: "Mima", 951 - img: "odH03t2.png", 952 - opts: { 953 - series: [1, 2, 3, 5], 954 - stage: [2, 4, 5] 955 - } 956 - }, 957 - { 958 - name: "Elis", 959 - img: "ytnL1xd.png", 960 - opts: { 961 - series: [1], 962 - stage: [4] 963 - } 964 - }, 965 - { 966 - name: "Kikuri", 967 - img: "fX2Kqik.png", 968 - opts: { 969 - series: [1], 970 - stage: [4] 971 - } 972 - }, 973 - { 974 - name: "Sariel", 975 - img: "Wyc7YFw.png", 976 - opts: { 977 - series: [1], 978 - stage: [5] 979 - } 980 - }, 981 - { 982 - name: "Konngara", 983 - img: "dg9jLHv.png", 984 - opts: { 985 - series: [1], 986 - stage: [5] 987 - } 988 - }, 989 - { 990 - name: "Rika", 991 - img: "02Xb4pU.png", 992 - opts: { 993 - series: [2], 994 - stage: [0, 6] 995 - } 996 - }, 997 - { 998 - name: "Meira", 999 - img: "p529JgT.png", 1000 - opts: { 1001 - series: [2], 1002 - stage: [1] 1003 - } 1004 - }, 1005 - { 1006 - name: "Kirisame Marisa (PC-98)", 1007 - img: "wxE7cBm.png", 1008 - opts: { 1009 - series: [2, 3, 4, 5], 1010 - stage: [3], 1011 - pc98: true 1012 - } 1013 - }, 1014 - { 1015 - name: "Ellen", 1016 - img: "3iNNL0c.png", 1017 - opts: { 1018 - series: [3], 1019 - stage: [] 1020 - } 1021 - }, 1022 - { 1023 - name: "Kotohime", 1024 - img: "kRSGtpq.png", 1025 - opts: { 1026 - series: [3], 1027 - stage: [] 1028 - } 1029 - }, 1030 - { 1031 - name: "Kana Anaberal", 1032 - img: "rBvKMk5.png", 1033 - opts: { 1034 - series: [3], 1035 - stage: [] 1036 - } 1037 - }, 1038 - { 1039 - name: "Asakura Rikako", 1040 - img: "VIf5gUK.png", 1041 - opts: { 1042 - series: [3], 1043 - stage: [] 1044 - } 1045 - }, 1046 - { 1047 - name: "Kitashirakawa Chiyuri", 1048 - img: "tZFBycy.png", 1049 - opts: { 1050 - series: [3], 1051 - stage: [4] 1052 - } 1053 - }, 1054 - { 1055 - name: "Okazaki Yumemi", 1056 - img: "c9rnG3n.png", 1057 - opts: { 1058 - series: [3], 1059 - stage: [5] 1060 - } 1061 - }, 1062 - { 1063 - name: "Ruukoto", 1064 - img: "dko67SJ.png", 1065 - opts: { 1066 - series: [3], 1067 - stage: [] 1068 - } 1069 - }, 1070 - { 1071 - name: "Orange", 1072 - img: "m8wXE5U.png", 1073 - opts: { 1074 - series: [4], 1075 - stage: [0] 1076 - } 1077 - }, 1078 - { 1079 - name: "Kurumi", 1080 - img: "0rvq1ph.png", 1081 - opts: { 1082 - series: [4], 1083 - stage: [1] 1084 - } 1085 - }, 1086 - { 1087 - name: "Elly", 1088 - img: "iIPftHn.png", 1089 - opts: { 1090 - series: [4], 1091 - stage: [2] 1092 - } 1093 - }, 1094 - { 1095 - name: "Yuuka (PC-98)", 1096 - img: "ivUSwxp.png", 1097 - opts: { 1098 - series: [4, 5], 1099 - stage: [4, 5], 1100 - pc98: true 1101 - } 1102 - }, 1103 - { 1104 - name: "Mugetsu", 1105 - img: "bYA9E16.png", 1106 - opts: { 1107 - series: [4], 1108 - stage: [6] 1109 - } 1110 - }, 1111 - { 1112 - name: "Gengetsu", 1113 - img: "TIOTtV9.png", 1114 - opts: { 1115 - series: [4], 1116 - stage: [6] 1117 - } 1118 - }, 1119 - { 1120 - name: "Sara", 1121 - img: "2QUbCrU.png", 1122 - opts: { 1123 - series: [5], 1124 - stage: [0] 1125 - } 1126 - }, 1127 - { 1128 - name: "Louise", 1129 - img: "nDM5aB6.png", 1130 - opts: { 1131 - series: [5], 1132 - stage: [1, 3] 1133 - } 1134 - }, 1135 - { 1136 - name: "Alice (PC-98)", 1137 - img: "KaBuRTW.png", 1138 - opts: { 1139 - series: [5], 1140 - stage: [2, 6], 1141 - pc98: true 1142 - } 1143 - }, 1144 - { 1145 - name: "Yuki", 1146 - img: "FfcmDgp.png", 1147 - opts: { 1148 - series: [5], 1149 - stage: [3] 1150 - } 1151 - }, 1152 - { 1153 - name: "Mai", 1154 - img: "r6w7TX1.png", 1155 - opts: { 1156 - series: [5], 1157 - stage: [3] 1158 - } 1159 - }, 1160 - { 1161 - name: "Yumeko", 1162 - img: "PcPqkdO.png", 1163 - opts: { 1164 - series: [5], 1165 - stage: [4] 1166 - } 1167 - }, 1168 - { 1169 - name: "Shinki", 1170 - img: "gPE95S7.png", 1171 - opts: { 1172 - series: [5], 1173 - stage: [5] 1174 - } 1175 - }, 1176 - { 1177 - name: "Mimi-chan", 1178 - img: "zBl2zlv.png", 1179 - opts: { 1180 - series: [3], 1181 - stage: [], 1182 - notgirl: true 1183 - } 1184 - }, 1185 - { 1186 - name: "Unzan", 1187 - img: "r5eWREh.png", 1188 - opts: { 1189 - series: [15, 17, 20, 23, 25], 1190 - stage: [2], 1191 - notgirl: true 1192 - } 1193 - }, 1194 - { 1195 - name: "Genji", 1196 - img: "LoUqOuH.png", 1197 - opts: { 1198 - series: [2, 3, 4, 5], 1199 - stage: [], 1200 - notgirl: true 1201 - } 1202 - }, 1203 - { 1204 - name: "Shingyoku (Male)", 1205 - img: "a5uwlgN.png", 1206 - opts: { 1207 - series: [1], 1208 - stage: [0], 1209 - notgirl: true 1210 - } 1211 - }, 1212 - { 1213 - name: "YuugenMagan", 1214 - img: "IOW8GdU.png", 1215 - opts: { 1216 - series: [1], 1217 - stage: [2], 1218 - notgirl: true 1219 - } 1220 - }, 1221 - { 1222 - name: "Evil Eye Sigma", 1223 - img: "rAFUMwE.png", 1224 - opts: { 1225 - series: [2], 1226 - stage: [6], 1227 - notgirl: true 1228 - } 1229 - }, 1230 - { 1231 - name: "Great Catfish", 1232 - img: "BgRi9Oh.png", 1233 - opts: { 1234 - series: [16, 25], 1235 - stage: [5], 1236 - notgirl: true 1237 - } 1238 - }, 1239 - { 1240 - name: "Morichika Rinnosuke", 1241 - img: "ITUhsGj.png", 1242 - opts: { 1243 - series: [0, 20], 1244 - stage: [], 1245 - notgirl: true 1246 - } 1247 - }, 1248 - { 1249 - name: "Fortune Teller", 1250 - img: "BYot23O.png", 1251 - opts: { 1252 - series: [0], 1253 - stage: [], 1254 - notgirl: true 1255 - } 1256 - }, 1257 - { 1258 - name: "Hisoutensoku", 1259 - img: "P4JZ2it.png", 1260 - opts: { 1261 - series: [16], 1262 - stage: [], 1263 - notgirl: true 1264 - } 1265 - } 1266 - ];
src/defaultL.jpg src/assets/defaultL.jpg
src/defaultR.jpg src/assets/defaultR.jpg
+23
src/js/data.js
··· 1 + /** 2 + * @typedef {{name: string, key: string, tooltip?: string, checked?: boolean, sub?: {name: string, tooltip?: string, checked?: string}[]}[]} Options 3 + * @typedef {{name: string, img: string, opts: Object<string, boolean|number[]}[]} CharacterData 4 + */ 5 + 6 + /** 7 + * Data set. Characters will be removed from the sorting array based on selected options, working down the array. 8 + * 9 + * @type {Object.<string, {options: Options, characterData: CharacterData}>} 10 + */ 11 + const dataSet = {}; 12 + 13 + /** 14 + * Data set version, in YYYY-MM-DD form. 15 + * 16 + * @example '2018-02-20' 17 + */ 18 + let dataSetVersion = ''; 19 + 20 + /** 21 + * Image root, will be appended to the start of every image URL. 22 + */ 23 + const imageRoot = 'https://i.imgur.com/';
+1185
src/js/data/2017-05-01.js
··· 1 + dataSetVersion = '2017-05-01'; // Change this when creating a new data set version. YYYY-MM-DD format. 2 + dataSet[dataSetVersion] = {}; 3 + 4 + dataSet[dataSetVersion].options = [ 5 + { 6 + name: 'Filter by Series Entry', 7 + key: 'series', 8 + tooltip: 'Check this to restrict to certain series.', 9 + checked: false, 10 + sub: [ 11 + { name: 'Books and CDs' }, 12 + { name: 'The Highly Responsive to Prayers', tooltip: '01 - Reiiden' }, 13 + { name: 'The Story of Eastern Wonderland', tooltip: '02 - Fuumaroku' }, 14 + { name: 'Phantasmagoria of Dim.Dream', tooltip: '03 - Yumejikuu' }, 15 + { name: 'Lotus Land Story', tooltip: '04 - Gensoukyou' }, 16 + { name: 'Mystic Square', tooltip: '05 - Kaikidan' }, 17 + { name: 'Embodiment of Scarlet Devil', tooltip: '06 - Koumakan' }, 18 + { name: 'Perfect Cherry Blossom', tooltip: '07 - Youyoumu' }, 19 + { name: 'Immaterial and Missing Power', tooltip: '07.5 - Suimusou' }, 20 + { name: 'Imperishable Night', tooltip: '08 - Eiyashou' }, 21 + { name: 'Phantasmagoria of Flower View', tooltip: '09 - Kaeidzuka' }, 22 + { name: 'Shoot the Bullet', tooltip: '09.5 - Bunkachou' }, 23 + { name: 'Mountain of Faith', tooltip: '10 - Fuujinroku' }, 24 + { name: 'Scarlet Weather Rhapsody', tooltip: '10.5 - Hisouten' }, 25 + { name: 'Subterranean Animism', tooltip: '11 - Chireiden' }, 26 + { name: 'Undefined Fantastic Object', tooltip: '12 - Seirensen' }, 27 + { name: 'Touhou Hisoutensoku', tooltip: '12.3 - Hisoutensoku' }, 28 + { name: 'Double Spoiler', tooltip: '12.5 - Bunkachou' }, 29 + { name: 'Great Fairy Wars', tooltip: '12.8 - Daisensou' }, 30 + { name: 'Ten Desires', tooltip: '13 - Shinreibyou' }, 31 + { name: 'Hopeless Masquerade', tooltip: '13.5 - Shinkirou' }, 32 + { name: 'Double Dealing Character', tooltip: '14 - Kishinjou' }, 33 + { name: 'Impossible Spell Card', tooltip: '14.3 - Amanojaku' }, 34 + { name: 'Urban Legend in Limbo', tooltip: '14.5 - Shinpiroku' }, 35 + { name: 'Legacy of Lunatic Kingdom', tooltip: '15 - Kanjuden' }, 36 + ] 37 + }, 38 + { 39 + name: 'Filter by Stage Enemy Appearances', 40 + key: 'stage', 41 + tooltip: 'Check this to restrict to characters that appear in certain stages as enemies.', 42 + checked: false, 43 + sub: [ 44 + { name: 'Stage 1' }, 45 + { name: 'Stage 2' }, 46 + { name: 'Stage 3' }, 47 + { name: 'Stage 4' }, 48 + { name: 'Stage 5/Penultimate', tooltip: 'Stage 4 in 5-stage games, and Stage 8 in 9-stage games.' 49 + }, 50 + { name: 'Stage 6/Final' }, 51 + { name: 'Stage EX/Phantasm' } 52 + ] 53 + }, 54 + { 55 + name: 'Remove PC-98 Duplicates', 56 + key: 'pc98', 57 + tooltip: 'Check this to remove PC-98 characters with a Windows counterpart.', 58 + }, 59 + { 60 + name: 'Remove Non-Girls', 61 + key: 'notgirl', 62 + tooltip: 'Check this to remove all non-female characters.', 63 + } 64 + ]; 65 + 66 + dataSet[dataSetVersion].characterData = [ 67 + { 68 + name: 'Hakurei Reimu', 69 + img: 'c5DqpgX.png', 70 + opts: { 71 + series: [0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 20, 21, 22, 23, 24], 72 + stage: [3] 73 + } 74 + }, 75 + { 76 + name: 'Kirisame Marisa', 77 + img: 'tJnkSzK.png', 78 + opts: { 79 + series: [0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24], 80 + stage: [3, 6] 81 + } 82 + }, 83 + { 84 + name: 'Rumia', 85 + img: '0YT7QlS.png', 86 + opts: { 87 + series: [0, 6, 11, 20], 88 + stage: [0] 89 + } 90 + }, 91 + { 92 + name: 'Daiyousei', 93 + img: 'NWlZud3.png', 94 + opts: { 95 + series: [0, 6], 96 + stage: [1, 6] 97 + } 98 + }, 99 + { 100 + name: 'Cirno', 101 + img: 'qdveFSy.png', 102 + opts: { 103 + series: [0, 6, 7, 10, 11, 16, 18, 20, 21, 22], 104 + stage: [1] 105 + } 106 + }, 107 + { 108 + name: 'Hong Meiling', 109 + img: 'ptGp0x4.png', 110 + opts: { 111 + series: [0, 6, 8, 11, 16], 112 + stage: [2] 113 + } 114 + }, 115 + { 116 + name: 'Koakuma', 117 + img: 'vBKdDm4.png', 118 + opts: { 119 + series: [0, 6], 120 + stage: [3] 121 + } 122 + }, 123 + { 124 + name: 'Patchouli Knowledge', 125 + img: 'A7ZnuHo.png', 126 + opts: { 127 + series: [0, 6, 8, 11, 12, 16, 20], 128 + stage: [3, 6] 129 + } 130 + }, 131 + { 132 + name: 'Izayoi Sakuya', 133 + img: 'sgZPf11.png', 134 + opts: { 135 + series: [0, 6, 7, 8, 9, 10, 11, 12, 16, 20, 21, 22], 136 + stage: [4, 5] 137 + } 138 + }, 139 + { 140 + name: 'Remilia Scarlet', 141 + img: '8UX7hKE.png', 142 + opts: { 143 + series: [0, 6, 8, 9, 11, 12, 16, 20, 22], 144 + stage: [5] 145 + } 146 + }, 147 + { 148 + name: 'Flandre Scarlet', 149 + img: 'OhaDcnc.png', 150 + opts: { 151 + series: [0, 6, 11], 152 + stage: [6] 153 + } 154 + }, 155 + { 156 + name: 'Letty Whiterock', 157 + img: 'MgzqjFK.png', 158 + opts: { 159 + series: [0, 7, 11, 20], 160 + stage: [0] 161 + } 162 + }, 163 + { 164 + name: 'Chen', 165 + img: 'ohmetZh.png', 166 + opts: { 167 + series: [0, 7, 8, 11, 12, 16, 20], 168 + stage: [1, 6] 169 + } 170 + }, 171 + { 172 + name: 'Alice Margatroid', 173 + img: 'aDIf0pN.png', 174 + opts: { 175 + series: [0, 7, 8, 9, 11, 12, 16, 20], 176 + stage: [2] 177 + } 178 + }, 179 + { 180 + name: 'Lily White', 181 + img: '2Pr8b2N.png', 182 + opts: { 183 + series: [0, 7, 10, 20], 184 + stage: [3, 6] 185 + } 186 + }, 187 + { 188 + name: 'Lunasa Prismriver', 189 + img: 'htOMdDQ.png', 190 + opts: { 191 + series: [0, 7, 10, 20], 192 + stage: [3] 193 + } 194 + }, 195 + { 196 + name: 'Merlin Prismriver', 197 + img: 'PrRPujP.png', 198 + opts: { 199 + series: [0, 7, 10, 20], 200 + stage: [3] 201 + } 202 + }, 203 + { 204 + name: 'Lyrica Prismriver', 205 + img: 'ze79bFC.png', 206 + opts: { 207 + series: [0, 7, 10, 20], 208 + stage: [3] 209 + } 210 + }, 211 + { 212 + name: 'Konpaku Youmu', 213 + img: 'WMjyRLJ.png', 214 + opts: { 215 + series: [0, 7, 8, 9, 10, 11, 12, 16, 19, 20, 22], 216 + stage: [4, 5] 217 + } 218 + }, 219 + { 220 + name: 'Saigyouji Yuyuko', 221 + img: 'VT9mTGb.png', 222 + opts: { 223 + series: [0, 7, 8, 9, 11, 12, 16, 19, 20, 22], 224 + stage: [0, 5] 225 + } 226 + }, 227 + { 228 + name: 'Yakumo Ran', 229 + img: 'rshnJPV.png', 230 + opts: { 231 + series: [0, 7, 8, 9, 11, 12, 16, 20], 232 + stage: [6] 233 + } 234 + }, 235 + { 236 + name: 'Yakumo Yukari', 237 + img: 'qsceD4I.png', 238 + opts: { 239 + series: [0, 7, 8, 9, 11, 12, 16, 20, 22], 240 + stage: [6] 241 + } 242 + }, 243 + { 244 + name: 'Ibuki Suika', 245 + img: 'pLdMjQ3.png', 246 + opts: { 247 + series: [0, 8, 11, 12, 16, 17, 20, 22], 248 + stage: [5] 249 + } 250 + }, 251 + { 252 + name: 'Wriggle Nightbug', 253 + img: '8DLUAPf.png', 254 + opts: { 255 + series: [0, 9, 11, 20], 256 + stage: [0] 257 + } 258 + }, 259 + { 260 + name: 'Mystia Lorelei', 261 + img: '6KyhLqE.png', 262 + opts: { 263 + series: [0, 9, 10, 11, 20], 264 + stage: [1] 265 + } 266 + }, 267 + { 268 + name: 'Kamishirasawa Keine', 269 + img: '99w0Chm.png', 270 + opts: { 271 + series: [0, 9, 11, 20, 22], 272 + stage: [2, 6] 273 + } 274 + }, 275 + { 276 + name: 'Inaba Tewi', 277 + img: 'yqNfNje.png', 278 + opts: { 279 + series: [0, 9, 10, 11, 20], 280 + stage: [4] 281 + } 282 + }, 283 + { 284 + name: 'Reisen Udongein Inaba', 285 + img: 'PrYzRcC.png', 286 + opts: { 287 + series: [0, 9, 10, 11, 16, 20, 24], 288 + stage: [4] 289 + } 290 + }, 291 + { 292 + name: 'Yagokoro Eirin', 293 + img: 'ceo4DhK.png', 294 + opts: { 295 + series: [0, 9, 11, 20], 296 + stage: [5] 297 + } 298 + }, 299 + { 300 + name: 'Houraisan Kaguya', 301 + img: '2YDuTk3.png', 302 + opts: { 303 + series: [0, 9, 11, 20], 304 + stage: [5] 305 + } 306 + }, 307 + { 308 + name: 'Fujiwara no Mokou', 309 + img: '3zo4VKV.png', 310 + opts: { 311 + series: [0, 9, 11, 20, 22, 23], 312 + stage: [6] 313 + } 314 + }, 315 + { 316 + name: 'Shameimaru Aya', 317 + img: '8TLXMST.png', 318 + opts: { 319 + series: [0, 10, 11, 12, 13, 16, 20, 22], 320 + stage: [3] 321 + } 322 + }, 323 + { 324 + name: 'Medicine Melancholy', 325 + img: 'IImsp7K.png', 326 + opts: { 327 + series: [0, 10, 11, 20], 328 + stage: [3] 329 + } 330 + }, 331 + { 332 + name: 'Kazami Yuuka', 333 + img: 'MZXJQq5.png', 334 + opts: { 335 + series: [0, 10, 11, 20], 336 + stage: [4] 337 + } 338 + }, 339 + { 340 + name: 'Onozuka Komachi', 341 + img: 'aX4WIH8.png', 342 + opts: { 343 + series: [0, 10, 11, 12, 16, 20], 344 + stage: [4] 345 + } 346 + }, 347 + { 348 + name: 'Shiki Eiki, Yamaxanadu', 349 + img: 'nPBvatH.png', 350 + opts: { 351 + series: [0, 10, 11, 20], 352 + stage: [5] 353 + } 354 + }, 355 + { 356 + name: 'Aki Shizuha', 357 + img: '3pDRgvR.png', 358 + opts: { 359 + series: [13, 17, 20], 360 + stage: [0] 361 + } 362 + }, 363 + { 364 + name: 'Aki Minoriko', 365 + img: 'bV0DaN7.png', 366 + opts: { 367 + series: [13, 17, 20], 368 + stage: [0] 369 + } 370 + }, 371 + { 372 + name: 'Kagiyama Hina', 373 + img: 'J11NjNj.png', 374 + opts: { 375 + series: [13, 17, 20], 376 + stage: [1] 377 + } 378 + }, 379 + { 380 + name: 'Kawashiro Nitori', 381 + img: '4Ufced2.png', 382 + opts: { 383 + series: [13, 17, 20, 22, 23], 384 + stage: [2] 385 + } 386 + }, 387 + { 388 + name: 'Inubashiri Momiji', 389 + img: 'qGMjnYk.png', 390 + opts: { 391 + series: [13, 17, 22], 392 + stage: [3] 393 + } 394 + }, 395 + { 396 + name: 'Kochiya Sanae', 397 + img: 'ATTRSWU.png', 398 + opts: { 399 + series: [13, 14, 15, 16, 17, 19, 20, 22, 24], 400 + stage: [4, 6] 401 + } 402 + }, 403 + { 404 + name: 'Yasaka Kanako', 405 + img: 'nQ78Lz7.png', 406 + opts: { 407 + series: [13, 16, 17, 20, 22], 408 + stage: [5, 6] 409 + } 410 + }, 411 + { 412 + name: 'Moriya Suwako', 413 + img: 'yJaD5ZV.png', 414 + opts: { 415 + series: [13, 14, 16, 17, 20, 22], 416 + stage: [6] 417 + } 418 + }, 419 + { 420 + name: 'Nagae Iku', 421 + img: 'xgAlECj.png', 422 + opts: { 423 + series: [12, 16, 17, 20], 424 + stage: [4] 425 + } 426 + }, 427 + { 428 + name: 'Hinanawi Tenshi', 429 + img: 'tZLYivt.png', 430 + opts: { 431 + series: [12, 16, 17, 20, 22], 432 + stage: [5] 433 + } 434 + }, 435 + { 436 + name: 'Kisume', 437 + img: 'VgJgaEf.png', 438 + opts: { 439 + series: [14, 17], 440 + stage: [0] 441 + } 442 + }, 443 + { 444 + name: 'Kurodani Yamame', 445 + img: 'sqgJ2St.png', 446 + opts: { 447 + series: [14, 17, 20], 448 + stage: [0] 449 + } 450 + }, 451 + { 452 + name: 'Mizuhashi Parsee', 453 + img: 'lkoAJod.png', 454 + opts: { 455 + series: [14, 17, 20], 456 + stage: [1] 457 + } 458 + }, 459 + { 460 + name: 'Hoshiguma Yuugi', 461 + img: 'tDO653L.png', 462 + opts: { 463 + series: [14, 17, 20], 464 + stage: [2] 465 + } 466 + }, 467 + { 468 + name: 'Komeiji Satori', 469 + img: 'dup7Nt6.png', 470 + opts: { 471 + series: [14, 17, 20], 472 + stage: [3] 473 + } 474 + }, 475 + { 476 + name: 'Kaenbyou Rin (Orin)', 477 + img: 'uQjbw1W.png', 478 + opts: { 479 + series: [14, 17, 20], 480 + stage: [3, 4, 5] 481 + } 482 + }, 483 + { 484 + name: 'Reiuji Utsuho (Okuu)', 485 + img: 'DfdaXPW.png', 486 + opts: { 487 + series: [14, 16, 17, 20], 488 + stage: [5] 489 + } 490 + }, 491 + { 492 + name: 'Komeiji Koishi', 493 + img: 'wVCcens.png', 494 + opts: { 495 + series: [14, 17, 20, 23], 496 + stage: [6] 497 + } 498 + }, 499 + { 500 + name: 'Nazrin', 501 + img: 'EpHQbiY.png', 502 + opts: { 503 + series: [15, 17, 20], 504 + stage: [0, 4] 505 + } 506 + }, 507 + { 508 + name: 'Tatara Kogasa', 509 + img: 'kJbv4dc.png', 510 + opts: { 511 + series: [15, 17, 20], 512 + stage: [1, 6] 513 + } 514 + }, 515 + { 516 + name: 'Kumoi Ichirin', 517 + img: 'Fyn5yVx.png', 518 + opts: { 519 + series: [15, 17, 20, 23], 520 + stage: [2] 521 + } 522 + }, 523 + { 524 + name: 'Murasa Minamitsu', 525 + img: '39KYpvW.png', 526 + opts: { 527 + series: [15, 17, 20], 528 + stage: [3] 529 + } 530 + }, 531 + { 532 + name: 'Toramaru Shou', 533 + img: '8bMDDAo.png', 534 + opts: { 535 + series: [15, 17, 20], 536 + stage: [4] 537 + } 538 + }, 539 + { 540 + name: 'Hijiri Byakuren', 541 + img: '2ppPxny.png', 542 + opts: { 543 + series: [15, 17, 20, 22, 23], 544 + stage: [5] 545 + } 546 + }, 547 + { 548 + name: 'Houjuu Nue', 549 + img: 'zL4S8Mj.png', 550 + opts: { 551 + series: [15, 17, 19, 20], 552 + stage: [3, 5, 6] 553 + } 554 + }, 555 + { 556 + name: 'Himekaidou Hatate', 557 + img: 'LgvoTaJ.png', 558 + opts: { 559 + series: [17, 20, 22], 560 + stage: [6] 561 + } 562 + }, 563 + { 564 + name: 'Sunny Milk', 565 + img: 'VbqXiB6.png', 566 + opts: { 567 + series: [0, 18, 20], 568 + stage: [0, 1, 2] 569 + } 570 + }, 571 + { 572 + name: 'Luna Child', 573 + img: 'OBqgP48.png', 574 + opts: { 575 + series: [0, 18, 20], 576 + stage: [0, 1, 2] 577 + } 578 + }, 579 + { 580 + name: 'Star Sapphire', 581 + img: 'sNw61ap.png', 582 + opts: { 583 + series: [0, 18, 20], 584 + stage: [0, 1, 2] 585 + } 586 + }, 587 + { 588 + name: 'Kasodani Kyouko', 589 + img: 'sLiqEBA.png', 590 + opts: { 591 + series: [19, 20, 22], 592 + stage: [1] 593 + } 594 + }, 595 + { 596 + name: 'Miyako Yoshika', 597 + img: '6jq6eh6.png', 598 + opts: { 599 + series: [19, 20, 22], 600 + stage: [2, 3] 601 + } 602 + }, 603 + { 604 + name: 'Kaku Seiga', 605 + img: '090hLPL.png', 606 + opts: { 607 + series: [19, 20, 22], 608 + stage: [3] 609 + } 610 + }, 611 + { 612 + name: 'Soga no Tojiko', 613 + img: 'y0UXwFO.png', 614 + opts: { 615 + series: [19, 20], 616 + stage: [4] 617 + } 618 + }, 619 + { 620 + name: 'Mononobe no Futo', 621 + img: 'WTZ97LE.png', 622 + opts: { 623 + series: [19, 20, 22, 23], 624 + stage: [4] 625 + } 626 + }, 627 + { 628 + name: 'Toyosatomimi no Miko', 629 + img: '3Xiqd22.png', 630 + opts: { 631 + series: [19, 20, 22, 23], 632 + stage: [5] 633 + } 634 + }, 635 + { 636 + name: 'Futatsuiwa Mamizou', 637 + img: 'gMpWdmA.png', 638 + opts: { 639 + series: [19, 20, 22, 23], 640 + stage: [6] 641 + } 642 + }, 643 + { 644 + name: 'Hata no Kokoro', 645 + img: 'fxCGmUk.png', 646 + opts: { 647 + series: [0, 20, 23], 648 + stage: [5] 649 + } 650 + }, 651 + { 652 + name: 'Wakasagihime', 653 + img: 'brWCLVx.png', 654 + opts: { 655 + series: [21, 22], 656 + stage: [0] 657 + } 658 + }, 659 + { 660 + name: 'Sekibanki', 661 + img: 'VAMLiJD.png', 662 + opts: { 663 + series: [21, 22], 664 + stage: [1] 665 + } 666 + }, 667 + { 668 + name: 'Imaizumi Kagerou', 669 + img: 'b5UMjD8.png', 670 + opts: { 671 + series: [21, 22], 672 + stage: [2] 673 + } 674 + }, 675 + { 676 + name: 'Tsukumo Benben', 677 + img: 'vWNeMaH.png', 678 + opts: { 679 + series: [21, 22], 680 + stage: [3, 6] 681 + } 682 + }, 683 + { 684 + name: 'Tsukumo Yatsuhashi', 685 + img: 'EJFQHQN.png', 686 + opts: { 687 + series: [21, 22], 688 + stage: [3, 6] 689 + } 690 + }, 691 + { 692 + name: 'Kijin Seija', 693 + img: '16RUacj.png', 694 + opts: { 695 + series: [21, 22], 696 + stage: [4, 5] 697 + } 698 + }, 699 + { 700 + name: 'Sukuna Shinmyoumaru', 701 + img: 'Zl2tN7W.png', 702 + opts: { 703 + series: [21, 22, 23], 704 + stage: [5] 705 + } 706 + }, 707 + { 708 + name: 'Horikawa Raiko', 709 + img: 'SLLEccR.png', 710 + opts: { 711 + series: [21, 22], 712 + stage: [6] 713 + } 714 + }, 715 + { 716 + name: 'Usami Sumireko', 717 + img: 'mc7ICW6.png', 718 + opts: { 719 + series: [23], 720 + stage: [5] 721 + } 722 + }, 723 + { 724 + name: 'Seiran', 725 + img: '0ra00WG.png', 726 + opts: { 727 + series: [24], 728 + stage: [0] 729 + } 730 + }, 731 + { 732 + name: 'Ringo', 733 + img: 'xQOsFlZ.png', 734 + opts: { 735 + series: [24], 736 + stage: [1] 737 + } 738 + }, 739 + { 740 + name: 'Doremy Sweet', 741 + img: 'rGS7dyn.png', 742 + opts: { 743 + series: [24], 744 + stage: [2, 6] 745 + } 746 + }, 747 + { 748 + name: 'Kishin Sagume', 749 + img: 'HLT338X.png', 750 + opts: { 751 + series: [24], 752 + stage: [3] 753 + } 754 + }, 755 + { 756 + name: 'Clownpiece', 757 + img: '9Jje7ZQ.jpg', 758 + opts: { 759 + series: [24], 760 + stage: [4] 761 + } 762 + }, 763 + { 764 + name: 'Junko', 765 + img: 'NsfLZjY.jpg', 766 + opts: { 767 + series: [24], 768 + stage: [5, 6] 769 + } 770 + }, 771 + { 772 + name: 'Hecatia Lapislazuli', 773 + img: 'EH3Ulol.png', 774 + opts: { 775 + series: [24], 776 + stage: [6] 777 + } 778 + }, 779 + { 780 + name: 'Hieda no Akyuu', 781 + img: 'ogONuLZ.png', 782 + opts: { 783 + series: [0], 784 + stage: [] 785 + } 786 + }, 787 + { 788 + name: 'Tokiko', 789 + img: 'Y4maOc8.png', 790 + opts: { 791 + series: [0], 792 + stage: [] 793 + } 794 + }, 795 + { 796 + name: 'Reisen (Manga)', 797 + img: 'cWjCo2j.png', 798 + opts: { 799 + series: [0], 800 + stage: [] 801 + } 802 + }, 803 + { 804 + name: 'Watatsuki no Toyohime', 805 + img: 'uEBxsEX.png', 806 + opts: { 807 + series: [0], 808 + stage: [] 809 + } 810 + }, 811 + { 812 + name: 'Watatsuki no Yorihime', 813 + img: 'Txu2P7S.png', 814 + opts: { 815 + series: [0], 816 + stage: [] 817 + } 818 + }, 819 + { 820 + name: 'Maribel Hearn', 821 + img: 'XUI9vPo.png', 822 + opts: { 823 + series: [0], 824 + stage: [] 825 + } 826 + }, 827 + { 828 + name: 'Usami Renko', 829 + img: '1P5EXRt.png', 830 + opts: { 831 + series: [0], 832 + stage: [] 833 + } 834 + }, 835 + { 836 + name: 'Ibaraki Kasen', 837 + img: 'dQHnPPe.png', 838 + opts: { 839 + series: [0, 23], 840 + stage: [4] 841 + } 842 + }, 843 + { 844 + name: 'Motoori Kosuzu', 845 + img: 'jEsJJo8.png', 846 + opts: { 847 + series: [0], 848 + stage: [] 849 + } 850 + }, 851 + { 852 + name: 'Hakurei Reimu (PC-98)', 853 + img: 'IZsGAMS.png', 854 + opts: { 855 + series: [1, 2, 3, 4, 5], 856 + stage: [3], 857 + pc98: true 858 + } 859 + }, 860 + { 861 + name: 'Shingyoku (Female)', 862 + img: 'KuPiR2k.png', 863 + opts: { 864 + series: [1], 865 + stage: [0] 866 + } 867 + }, 868 + { 869 + name: 'Mima', 870 + img: 'odH03t2.png', 871 + opts: { 872 + series: [1, 2, 3, 5], 873 + stage: [2, 4, 5] 874 + } 875 + }, 876 + { 877 + name: 'Elis', 878 + img: 'ytnL1xd.png', 879 + opts: { 880 + series: [1], 881 + stage: [4] 882 + } 883 + }, 884 + { 885 + name: 'Kikuri', 886 + img: 'fX2Kqik.png', 887 + opts: { 888 + series: [1], 889 + stage: [4] 890 + } 891 + }, 892 + { 893 + name: 'Sariel', 894 + img: 'Wyc7YFw.png', 895 + opts: { 896 + series: [1], 897 + stage: [5] 898 + } 899 + }, 900 + { 901 + name: 'Konngara', 902 + img: 'dg9jLHv.png', 903 + opts: { 904 + series: [1], 905 + stage: [5] 906 + } 907 + }, 908 + { 909 + name: 'Rika', 910 + img: '02Xb4pU.png', 911 + opts: { 912 + series: [2], 913 + stage: [0, 6] 914 + } 915 + }, 916 + { 917 + name: 'Meira', 918 + img: 'p529JgT.png', 919 + opts: { 920 + series: [2], 921 + stage: [1] 922 + } 923 + }, 924 + { 925 + name: 'Kirisame Marisa (PC-98)', 926 + img: 'wxE7cBm.png', 927 + opts: { 928 + series: [2, 3, 4, 5], 929 + stage: [3], 930 + pc98: true 931 + } 932 + }, 933 + { 934 + name: 'Ellen', 935 + img: '3iNNL0c.png', 936 + opts: { 937 + series: [3], 938 + stage: [] 939 + } 940 + }, 941 + { 942 + name: 'Kotohime', 943 + img: 'kRSGtpq.png', 944 + opts: { 945 + series: [3], 946 + stage: [] 947 + } 948 + }, 949 + { 950 + name: 'Kana Anaberal', 951 + img: 'rBvKMk5.png', 952 + opts: { 953 + series: [3], 954 + stage: [] 955 + } 956 + }, 957 + { 958 + name: 'Asakura Rikako', 959 + img: 'VIf5gUK.png', 960 + opts: { 961 + series: [3], 962 + stage: [] 963 + } 964 + }, 965 + { 966 + name: 'Kitashirakawa Chiyuri', 967 + img: 'tZFBycy.png', 968 + opts: { 969 + series: [3], 970 + stage: [4] 971 + } 972 + }, 973 + { 974 + name: 'Okazaki Yumemi', 975 + img: 'c9rnG3n.png', 976 + opts: { 977 + series: [3], 978 + stage: [5] 979 + } 980 + }, 981 + { 982 + name: 'Ruukoto', 983 + img: 'dko67SJ.png', 984 + opts: { 985 + series: [3], 986 + stage: [] 987 + } 988 + }, 989 + { 990 + name: 'Orange', 991 + img: 'm8wXE5U.png', 992 + opts: { 993 + series: [4], 994 + stage: [0] 995 + } 996 + }, 997 + { 998 + name: 'Kurumi', 999 + img: '0rvq1ph.png', 1000 + opts: { 1001 + series: [4], 1002 + stage: [1] 1003 + } 1004 + }, 1005 + { 1006 + name: 'Elly', 1007 + img: 'iIPftHn.png', 1008 + opts: { 1009 + series: [4], 1010 + stage: [2] 1011 + } 1012 + }, 1013 + { 1014 + name: 'Yuuka (PC-98)', 1015 + img: 'ivUSwxp.png', 1016 + opts: { 1017 + series: [4, 5], 1018 + stage: [4, 5], 1019 + pc98: true 1020 + } 1021 + }, 1022 + { 1023 + name: 'Mugetsu', 1024 + img: 'bYA9E16.png', 1025 + opts: { 1026 + series: [4], 1027 + stage: [6] 1028 + } 1029 + }, 1030 + { 1031 + name: 'Gengetsu', 1032 + img: 'TIOTtV9.png', 1033 + opts: { 1034 + series: [4], 1035 + stage: [6] 1036 + } 1037 + }, 1038 + { 1039 + name: 'Sara', 1040 + img: '2QUbCrU.png', 1041 + opts: { 1042 + series: [5], 1043 + stage: [0] 1044 + } 1045 + }, 1046 + { 1047 + name: 'Louise', 1048 + img: 'nDM5aB6.png', 1049 + opts: { 1050 + series: [5], 1051 + stage: [1, 3] 1052 + } 1053 + }, 1054 + { 1055 + name: 'Alice (PC-98)', 1056 + img: 'KaBuRTW.png', 1057 + opts: { 1058 + series: [5], 1059 + stage: [2, 6], 1060 + pc98: true 1061 + } 1062 + }, 1063 + { 1064 + name: 'Yuki', 1065 + img: 'FfcmDgp.png', 1066 + opts: { 1067 + series: [5], 1068 + stage: [3] 1069 + } 1070 + }, 1071 + { 1072 + name: 'Mai', 1073 + img: 'r6w7TX1.png', 1074 + opts: { 1075 + series: [5], 1076 + stage: [3] 1077 + } 1078 + }, 1079 + { 1080 + name: 'Yumeko', 1081 + img: 'PcPqkdO.png', 1082 + opts: { 1083 + series: [5], 1084 + stage: [4] 1085 + } 1086 + }, 1087 + { 1088 + name: 'Shinki', 1089 + img: 'gPE95S7.png', 1090 + opts: { 1091 + series: [5], 1092 + stage: [5] 1093 + } 1094 + }, 1095 + { 1096 + name: 'Mimi-chan', 1097 + img: 'zBl2zlv.png', 1098 + opts: { 1099 + series: [3], 1100 + stage: [], 1101 + notgirl: true 1102 + } 1103 + }, 1104 + { 1105 + name: 'Unzan', 1106 + img: 'r5eWREh.png', 1107 + opts: { 1108 + series: [15, 17, 20, 23], 1109 + stage: [2], 1110 + notgirl: true 1111 + } 1112 + }, 1113 + { 1114 + name: 'Genji', 1115 + img: 'LoUqOuH.png', 1116 + opts: { 1117 + series: [2, 3, 4, 5], 1118 + stage: [], 1119 + notgirl: true 1120 + } 1121 + }, 1122 + { 1123 + name: 'Shingyoku (Male)', 1124 + img: 'a5uwlgN.png', 1125 + opts: { 1126 + series: [1], 1127 + stage: [0], 1128 + notgirl: true 1129 + } 1130 + }, 1131 + { 1132 + name: 'YuugenMagan', 1133 + img: 'IOW8GdU.png', 1134 + opts: { 1135 + series: [1], 1136 + stage: [2], 1137 + notgirl: true 1138 + } 1139 + }, 1140 + { 1141 + name: 'Evil Eye Sigma', 1142 + img: 'rAFUMwE.png', 1143 + opts: { 1144 + series: [2], 1145 + stage: [6], 1146 + notgirl: true 1147 + } 1148 + }, 1149 + { 1150 + name: 'Great Catfish', 1151 + img: 'BgRi9Oh.png', 1152 + opts: { 1153 + series: [16], 1154 + stage: [5], 1155 + notgirl: true 1156 + } 1157 + }, 1158 + { 1159 + name: 'Morichika Rinnosuke', 1160 + img: 'ITUhsGj.png', 1161 + opts: { 1162 + series: [0, 20], 1163 + stage: [], 1164 + notgirl: true 1165 + } 1166 + }, 1167 + { 1168 + name: 'Fortune Teller', 1169 + img: 'BYot23O.png', 1170 + opts: { 1171 + series: [0], 1172 + stage: [], 1173 + notgirl: true 1174 + } 1175 + }, 1176 + { 1177 + name: 'Hisoutensoku', 1178 + img: 'P4JZ2it.png', 1179 + opts: { 1180 + series: [16], 1181 + stage: [], 1182 + notgirl: true 1183 + } 1184 + } 1185 + ];
+1259
src/js/data/2018-02-20.js
··· 1 + dataSetVersion = '2018-02-20'; // Change this when creating a new data set version. YYYY-MM-DD format. 2 + dataSet[dataSetVersion] = {}; 3 + 4 + dataSet[dataSetVersion].options = [ 5 + { 6 + name: 'Filter by Series Entry', 7 + key: 'series', 8 + tooltip: 'Check this to restrict to certain series.', 9 + checked: false, 10 + sub: [ 11 + { name: 'Books and CDs' }, 12 + { name: 'The Highly Responsive to Prayers', tooltip: '01 - Reiiden' }, 13 + { name: 'The Story of Eastern Wonderland', tooltip: '02 - Fuumaroku' }, 14 + { name: 'Phantasmagoria of Dim.Dream', tooltip: '03 - Yumejikuu' }, 15 + { name: 'Lotus Land Story', tooltip: '04 - Gensoukyou' }, 16 + { name: 'Mystic Square', tooltip: '05 - Kaikidan' }, 17 + { name: 'Embodiment of Scarlet Devil', tooltip: '06 - Koumakan' }, 18 + { name: 'Perfect Cherry Blossom', tooltip: '07 - Youyoumu' }, 19 + { name: 'Immaterial and Missing Power', tooltip: '07.5 - Suimusou' }, 20 + { name: 'Imperishable Night', tooltip: '08 - Eiyashou' }, 21 + { name: 'Phantasmagoria of Flower View', tooltip: '09 - Kaeidzuka' }, 22 + { name: 'Shoot the Bullet', tooltip: '09.5 - Bunkachou' }, 23 + { name: 'Mountain of Faith', tooltip: '10 - Fuujinroku' }, 24 + { name: 'Scarlet Weather Rhapsody', tooltip: '10.5 - Hisouten' }, 25 + { name: 'Subterranean Animism', tooltip: '11 - Chireiden' }, 26 + { name: 'Undefined Fantastic Object', tooltip: '12 - Seirensen' }, 27 + { name: 'Touhou Hisoutensoku', tooltip: '12.3 - Hisoutensoku' }, 28 + { name: 'Double Spoiler', tooltip: '12.5 - Bunkachou' }, 29 + { name: 'Great Fairy Wars', tooltip: '12.8 - Daisensou' }, 30 + { name: 'Ten Desires', tooltip: '13 - Shinreibyou' }, 31 + { name: 'Hopeless Masquerade', tooltip: '13.5 - Shinkirou' }, 32 + { name: 'Double Dealing Character', tooltip: '14 - Kishinjou' }, 33 + { name: 'Impossible Spell Card', tooltip: '14.3 - Amanojaku' }, 34 + { name: 'Urban Legend in Limbo', tooltip: '14.5 - Shinpiroku' }, 35 + { name: 'Legacy of Lunatic Kingdom', tooltip: '15 - Kanjuden' }, 36 + { name: 'Antinomy of Common Flowers', tooltip: '15.5 - Hyouibana' }, 37 + { name: 'Hidden Star in Four Seasons', tooltip: '16 - Tenkuushou' } 38 + ] 39 + }, 40 + { 41 + name: 'Filter by Stage Enemy Appearances', 42 + key: 'stage', 43 + tooltip: 'Check this to restrict to characters that appear in certain stages as enemies.', 44 + checked: false, 45 + sub: [ 46 + { name: 'Stage 1' }, 47 + { name: 'Stage 2' }, 48 + { name: 'Stage 3' }, 49 + { name: 'Stage 4' }, 50 + { name: 'Stage 5/Penultimate', tooltip: 'Stage 4 in 5-stage games, and Stage 8 in 9-stage games.' 51 + }, 52 + { name: 'Stage 6/Final' }, 53 + { name: 'Stage EX/Phantasm' } 54 + ] 55 + }, 56 + { 57 + name: 'Remove PC-98 Duplicates', 58 + key: 'pc98', 59 + tooltip: 'Check this to remove PC-98 characters with a Windows counterpart.', 60 + }, 61 + { 62 + name: 'Remove Non-Girls', 63 + key: 'notgirl', 64 + tooltip: 'Check this to remove all non-female characters.', 65 + } 66 + ]; 67 + 68 + dataSet[dataSetVersion].characterData = [ 69 + { 70 + name: 'Hakurei Reimu', 71 + img: 'c5DqpgX.png', 72 + opts: { 73 + series: [0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 20, 21, 22, 23, 24, 25, 26], 74 + stage: [3] 75 + } 76 + }, 77 + { 78 + name: 'Kirisame Marisa', 79 + img: 'tJnkSzK.png', 80 + opts: { 81 + series: [0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26], 82 + stage: [3, 6] 83 + } 84 + }, 85 + { 86 + name: 'Rumia', 87 + img: '0YT7QlS.png', 88 + opts: { 89 + series: [0, 6, 11, 20], 90 + stage: [0] 91 + } 92 + }, 93 + { 94 + name: 'Daiyousei', 95 + img: 'NWlZud3.png', 96 + opts: { 97 + series: [0, 6], 98 + stage: [1, 6] 99 + } 100 + }, 101 + { 102 + name: 'Cirno', 103 + img: 'qdveFSy.png', 104 + opts: { 105 + series: [0, 6, 7, 10, 11, 16, 18, 20, 21, 22, 26], 106 + stage: [1] 107 + } 108 + }, 109 + { 110 + name: 'Hong Meiling', 111 + img: 'ptGp0x4.png', 112 + opts: { 113 + series: [0, 6, 8, 11, 16], 114 + stage: [2] 115 + } 116 + }, 117 + { 118 + name: 'Koakuma', 119 + img: 'vBKdDm4.png', 120 + opts: { 121 + series: [0, 6], 122 + stage: [3] 123 + } 124 + }, 125 + { 126 + name: 'Patchouli Knowledge', 127 + img: 'A7ZnuHo.png', 128 + opts: { 129 + series: [0, 6, 8, 11, 12, 16, 20], 130 + stage: [3, 6] 131 + } 132 + }, 133 + { 134 + name: 'Izayoi Sakuya', 135 + img: 'sgZPf11.png', 136 + opts: { 137 + series: [0, 6, 7, 8, 9, 10, 11, 12, 16, 20, 21, 22], 138 + stage: [4, 5] 139 + } 140 + }, 141 + { 142 + name: 'Remilia Scarlet', 143 + img: '8UX7hKE.png', 144 + opts: { 145 + series: [0, 6, 8, 9, 11, 12, 16, 20, 22], 146 + stage: [5] 147 + } 148 + }, 149 + { 150 + name: 'Flandre Scarlet', 151 + img: 'OhaDcnc.png', 152 + opts: { 153 + series: [0, 6, 11], 154 + stage: [6] 155 + } 156 + }, 157 + { 158 + name: 'Letty Whiterock', 159 + img: 'MgzqjFK.png', 160 + opts: { 161 + series: [0, 7, 11, 20], 162 + stage: [0] 163 + } 164 + }, 165 + { 166 + name: 'Chen', 167 + img: 'ohmetZh.png', 168 + opts: { 169 + series: [0, 7, 8, 11, 12, 16, 20], 170 + stage: [1, 6] 171 + } 172 + }, 173 + { 174 + name: 'Alice Margatroid', 175 + img: 'aDIf0pN.png', 176 + opts: { 177 + series: [0, 7, 8, 9, 11, 12, 16, 20], 178 + stage: [2] 179 + } 180 + }, 181 + { 182 + name: 'Lily White', 183 + img: '2Pr8b2N.png', 184 + opts: { 185 + series: [0, 7, 10, 20, 26], 186 + stage: [3, 6] 187 + } 188 + }, 189 + { 190 + name: 'Lunasa Prismriver', 191 + img: 'htOMdDQ.png', 192 + opts: { 193 + series: [0, 7, 10, 20, 25], 194 + stage: [3] 195 + } 196 + }, 197 + { 198 + name: 'Merlin Prismriver', 199 + img: 'PrRPujP.png', 200 + opts: { 201 + series: [0, 7, 10, 20, 25], 202 + stage: [3] 203 + } 204 + }, 205 + { 206 + name: 'Lyrica Prismriver', 207 + img: 'ze79bFC.png', 208 + opts: { 209 + series: [0, 7, 10, 20, 25], 210 + stage: [3] 211 + } 212 + }, 213 + { 214 + name: 'Konpaku Youmu', 215 + img: 'WMjyRLJ.png', 216 + opts: { 217 + series: [0, 7, 8, 9, 10, 11, 12, 16, 19, 20, 22], 218 + stage: [4, 5] 219 + } 220 + }, 221 + { 222 + name: 'Saigyouji Yuyuko', 223 + img: 'VT9mTGb.png', 224 + opts: { 225 + series: [0, 7, 8, 9, 11, 12, 16, 19, 20, 22], 226 + stage: [0, 5] 227 + } 228 + }, 229 + { 230 + name: 'Yakumo Ran', 231 + img: 'rshnJPV.png', 232 + opts: { 233 + series: [0, 7, 8, 9, 11, 12, 16, 20], 234 + stage: [6] 235 + } 236 + }, 237 + { 238 + name: 'Yakumo Yukari', 239 + img: 'qsceD4I.png', 240 + opts: { 241 + series: [0, 7, 8, 9, 11, 12, 16, 20, 22, 25], 242 + stage: [6] 243 + } 244 + }, 245 + { 246 + name: 'Ibuki Suika', 247 + img: 'pLdMjQ3.png', 248 + opts: { 249 + series: [0, 8, 11, 12, 16, 17, 20, 22], 250 + stage: [5] 251 + } 252 + }, 253 + { 254 + name: 'Wriggle Nightbug', 255 + img: '8DLUAPf.png', 256 + opts: { 257 + series: [0, 9, 11, 20], 258 + stage: [0] 259 + } 260 + }, 261 + { 262 + name: 'Mystia Lorelei', 263 + img: '6KyhLqE.png', 264 + opts: { 265 + series: [0, 9, 10, 11, 20], 266 + stage: [1] 267 + } 268 + }, 269 + { 270 + name: 'Kamishirasawa Keine', 271 + img: '99w0Chm.png', 272 + opts: { 273 + series: [0, 9, 11, 20, 22], 274 + stage: [2, 6] 275 + } 276 + }, 277 + { 278 + name: 'Inaba Tewi', 279 + img: 'yqNfNje.png', 280 + opts: { 281 + series: [0, 9, 10, 11, 20], 282 + stage: [4] 283 + } 284 + }, 285 + { 286 + name: 'Reisen Udongein Inaba', 287 + img: 'PrYzRcC.png', 288 + opts: { 289 + series: [0, 9, 10, 11, 16, 20, 24, 25], 290 + stage: [4] 291 + } 292 + }, 293 + { 294 + name: 'Yagokoro Eirin', 295 + img: 'ceo4DhK.png', 296 + opts: { 297 + series: [0, 9, 11, 20], 298 + stage: [5] 299 + } 300 + }, 301 + { 302 + name: 'Houraisan Kaguya', 303 + img: '2YDuTk3.png', 304 + opts: { 305 + series: [0, 9, 11, 20], 306 + stage: [5] 307 + } 308 + }, 309 + { 310 + name: 'Fujiwara no Mokou', 311 + img: '3zo4VKV.png', 312 + opts: { 313 + series: [0, 9, 11, 20, 22, 23, 25], 314 + stage: [6] 315 + } 316 + }, 317 + { 318 + name: 'Shameimaru Aya', 319 + img: '8TLXMST.png', 320 + opts: { 321 + series: [0, 10, 11, 12, 13, 16, 20, 22, 26], 322 + stage: [3] 323 + } 324 + }, 325 + { 326 + name: 'Medicine Melancholy', 327 + img: 'IImsp7K.png', 328 + opts: { 329 + series: [0, 10, 11, 20], 330 + stage: [3] 331 + } 332 + }, 333 + { 334 + name: 'Kazami Yuuka', 335 + img: 'MZXJQq5.png', 336 + opts: { 337 + series: [0, 10, 11, 20], 338 + stage: [4] 339 + } 340 + }, 341 + { 342 + name: 'Onozuka Komachi', 343 + img: 'aX4WIH8.png', 344 + opts: { 345 + series: [0, 10, 11, 12, 16, 20], 346 + stage: [4] 347 + } 348 + }, 349 + { 350 + name: 'Shiki Eiki, Yamaxanadu', 351 + img: 'nPBvatH.png', 352 + opts: { 353 + series: [0, 10, 11, 20], 354 + stage: [5] 355 + } 356 + }, 357 + { 358 + name: 'Aki Shizuha', 359 + img: '3pDRgvR.png', 360 + opts: { 361 + series: [13, 17, 20], 362 + stage: [0] 363 + } 364 + }, 365 + { 366 + name: 'Aki Minoriko', 367 + img: 'bV0DaN7.png', 368 + opts: { 369 + series: [13, 17, 20], 370 + stage: [0] 371 + } 372 + }, 373 + { 374 + name: 'Kagiyama Hina', 375 + img: 'J11NjNj.png', 376 + opts: { 377 + series: [13, 17, 20], 378 + stage: [1] 379 + } 380 + }, 381 + { 382 + name: 'Kawashiro Nitori', 383 + img: '4Ufced2.png', 384 + opts: { 385 + series: [13, 17, 20, 22, 23, 25], 386 + stage: [2] 387 + } 388 + }, 389 + { 390 + name: 'Inubashiri Momiji', 391 + img: 'qGMjnYk.png', 392 + opts: { 393 + series: [13, 17, 22], 394 + stage: [3] 395 + } 396 + }, 397 + { 398 + name: 'Kochiya Sanae', 399 + img: 'ATTRSWU.png', 400 + opts: { 401 + series: [13, 14, 15, 16, 17, 19, 20, 22, 24], 402 + stage: [4, 6] 403 + } 404 + }, 405 + { 406 + name: 'Yasaka Kanako', 407 + img: 'nQ78Lz7.png', 408 + opts: { 409 + series: [13, 16, 17, 20, 22], 410 + stage: [5, 6] 411 + } 412 + }, 413 + { 414 + name: 'Moriya Suwako', 415 + img: 'yJaD5ZV.png', 416 + opts: { 417 + series: [13, 14, 16, 17, 20, 22], 418 + stage: [6] 419 + } 420 + }, 421 + { 422 + name: 'Nagae Iku', 423 + img: 'xgAlECj.png', 424 + opts: { 425 + series: [12, 16, 17, 20], 426 + stage: [4] 427 + } 428 + }, 429 + { 430 + name: 'Hinanawi Tenshi', 431 + img: 'tZLYivt.png', 432 + opts: { 433 + series: [12, 16, 17, 20, 22, 25], 434 + stage: [5] 435 + } 436 + }, 437 + { 438 + name: 'Kisume', 439 + img: 'VgJgaEf.png', 440 + opts: { 441 + series: [14, 17], 442 + stage: [0] 443 + } 444 + }, 445 + { 446 + name: 'Kurodani Yamame', 447 + img: 'sqgJ2St.png', 448 + opts: { 449 + series: [14, 17, 20], 450 + stage: [0] 451 + } 452 + }, 453 + { 454 + name: 'Mizuhashi Parsee', 455 + img: 'lkoAJod.png', 456 + opts: { 457 + series: [14, 17, 20], 458 + stage: [1] 459 + } 460 + }, 461 + { 462 + name: 'Hoshiguma Yuugi', 463 + img: 'tDO653L.png', 464 + opts: { 465 + series: [14, 17, 20], 466 + stage: [2] 467 + } 468 + }, 469 + { 470 + name: 'Komeiji Satori', 471 + img: 'dup7Nt6.png', 472 + opts: { 473 + series: [14, 17, 20], 474 + stage: [3] 475 + } 476 + }, 477 + { 478 + name: 'Kaenbyou Rin (Orin)', 479 + img: 'uQjbw1W.png', 480 + opts: { 481 + series: [14, 17, 20], 482 + stage: [3, 4, 5] 483 + } 484 + }, 485 + { 486 + name: 'Reiuji Utsuho (Okuu)', 487 + img: 'DfdaXPW.png', 488 + opts: { 489 + series: [14, 16, 17, 20], 490 + stage: [5] 491 + } 492 + }, 493 + { 494 + name: 'Komeiji Koishi', 495 + img: 'wVCcens.png', 496 + opts: { 497 + series: [14, 17, 20, 23, 25], 498 + stage: [6] 499 + } 500 + }, 501 + { 502 + name: 'Nazrin', 503 + img: 'EpHQbiY.png', 504 + opts: { 505 + series: [15, 17, 20], 506 + stage: [0, 4] 507 + } 508 + }, 509 + { 510 + name: 'Tatara Kogasa', 511 + img: 'kJbv4dc.png', 512 + opts: { 513 + series: [15, 17, 20], 514 + stage: [1, 6] 515 + } 516 + }, 517 + { 518 + name: 'Kumoi Ichirin', 519 + img: 'Fyn5yVx.png', 520 + opts: { 521 + series: [15, 17, 20, 23, 25], 522 + stage: [2] 523 + } 524 + }, 525 + { 526 + name: 'Murasa Minamitsu', 527 + img: '39KYpvW.png', 528 + opts: { 529 + series: [15, 17, 20], 530 + stage: [3] 531 + } 532 + }, 533 + { 534 + name: 'Toramaru Shou', 535 + img: '8bMDDAo.png', 536 + opts: { 537 + series: [15, 17, 20], 538 + stage: [4] 539 + } 540 + }, 541 + { 542 + name: 'Hijiri Byakuren', 543 + img: '2ppPxny.png', 544 + opts: { 545 + series: [15, 17, 20, 22, 23, 25], 546 + stage: [5] 547 + } 548 + }, 549 + { 550 + name: 'Houjuu Nue', 551 + img: 'zL4S8Mj.png', 552 + opts: { 553 + series: [15, 17, 19, 20], 554 + stage: [3, 5, 6] 555 + } 556 + }, 557 + { 558 + name: 'Himekaidou Hatate', 559 + img: 'LgvoTaJ.png', 560 + opts: { 561 + series: [17, 20, 22], 562 + stage: [6] 563 + } 564 + }, 565 + { 566 + name: 'Sunny Milk', 567 + img: 'VbqXiB6.png', 568 + opts: { 569 + series: [0, 18, 20], 570 + stage: [0, 1, 2] 571 + } 572 + }, 573 + { 574 + name: 'Luna Child', 575 + img: 'OBqgP48.png', 576 + opts: { 577 + series: [0, 18, 20], 578 + stage: [0, 1, 2] 579 + } 580 + }, 581 + { 582 + name: 'Star Sapphire', 583 + img: 'sNw61ap.png', 584 + opts: { 585 + series: [0, 18, 20], 586 + stage: [0, 1, 2] 587 + } 588 + }, 589 + { 590 + name: 'Kasodani Kyouko', 591 + img: 'sLiqEBA.png', 592 + opts: { 593 + series: [19, 20, 22], 594 + stage: [1] 595 + } 596 + }, 597 + { 598 + name: 'Miyako Yoshika', 599 + img: '6jq6eh6.png', 600 + opts: { 601 + series: [19, 20, 22], 602 + stage: [2, 3] 603 + } 604 + }, 605 + { 606 + name: 'Kaku Seiga', 607 + img: '090hLPL.png', 608 + opts: { 609 + series: [19, 20, 22], 610 + stage: [3] 611 + } 612 + }, 613 + { 614 + name: 'Soga no Tojiko', 615 + img: 'y0UXwFO.png', 616 + opts: { 617 + series: [19, 20], 618 + stage: [4] 619 + } 620 + }, 621 + { 622 + name: 'Mononobe no Futo', 623 + img: 'WTZ97LE.png', 624 + opts: { 625 + series: [19, 20, 22, 23, 25], 626 + stage: [4] 627 + } 628 + }, 629 + { 630 + name: 'Toyosatomimi no Miko', 631 + img: '3Xiqd22.png', 632 + opts: { 633 + series: [19, 20, 22, 23, 25], 634 + stage: [5] 635 + } 636 + }, 637 + { 638 + name: 'Futatsuiwa Mamizou', 639 + img: 'gMpWdmA.png', 640 + opts: { 641 + series: [19, 20, 22, 23, 25], 642 + stage: [6] 643 + } 644 + }, 645 + { 646 + name: 'Hata no Kokoro', 647 + img: 'fxCGmUk.png', 648 + opts: { 649 + series: [0, 20, 23, 25], 650 + stage: [5] 651 + } 652 + }, 653 + { 654 + name: 'Wakasagihime', 655 + img: 'brWCLVx.png', 656 + opts: { 657 + series: [21, 22], 658 + stage: [0] 659 + } 660 + }, 661 + { 662 + name: 'Sekibanki', 663 + img: 'VAMLiJD.png', 664 + opts: { 665 + series: [21, 22], 666 + stage: [1] 667 + } 668 + }, 669 + { 670 + name: 'Imaizumi Kagerou', 671 + img: 'b5UMjD8.png', 672 + opts: { 673 + series: [21, 22], 674 + stage: [2] 675 + } 676 + }, 677 + { 678 + name: 'Tsukumo Benben', 679 + img: 'vWNeMaH.png', 680 + opts: { 681 + series: [21, 22], 682 + stage: [3, 6] 683 + } 684 + }, 685 + { 686 + name: 'Tsukumo Yatsuhashi', 687 + img: 'EJFQHQN.png', 688 + opts: { 689 + series: [21, 22], 690 + stage: [3, 6] 691 + } 692 + }, 693 + { 694 + name: 'Kijin Seija', 695 + img: '16RUacj.png', 696 + opts: { 697 + series: [21, 22], 698 + stage: [4, 5] 699 + } 700 + }, 701 + { 702 + name: 'Sukuna Shinmyoumaru', 703 + img: 'Zl2tN7W.png', 704 + opts: { 705 + series: [21, 22, 23, 25], 706 + stage: [5] 707 + } 708 + }, 709 + { 710 + name: 'Horikawa Raiko', 711 + img: 'SLLEccR.png', 712 + opts: { 713 + series: [21, 22, 25], 714 + stage: [6] 715 + } 716 + }, 717 + { 718 + name: 'Usami Sumireko', 719 + img: 'mc7ICW6.png', 720 + opts: { 721 + series: [23, 25], 722 + stage: [5] 723 + } 724 + }, 725 + { 726 + name: 'Seiran', 727 + img: '0ra00WG.png', 728 + opts: { 729 + series: [24], 730 + stage: [0] 731 + } 732 + }, 733 + { 734 + name: 'Ringo', 735 + img: 'xQOsFlZ.png', 736 + opts: { 737 + series: [24], 738 + stage: [1] 739 + } 740 + }, 741 + { 742 + name: 'Doremy Sweet', 743 + img: 'rGS7dyn.png', 744 + opts: { 745 + series: [24, 25], 746 + stage: [2, 6] 747 + } 748 + }, 749 + { 750 + name: 'Kishin Sagume', 751 + img: 'HLT338X.png', 752 + opts: { 753 + series: [24], 754 + stage: [3] 755 + } 756 + }, 757 + { 758 + name: 'Clownpiece', 759 + img: '9Jje7ZQ.jpg', 760 + opts: { 761 + series: [24], 762 + stage: [4] 763 + } 764 + }, 765 + { 766 + name: 'Junko', 767 + img: 'NsfLZjY.jpg', 768 + opts: { 769 + series: [24], 770 + stage: [5, 6] 771 + } 772 + }, 773 + { 774 + name: 'Hecatia Lapislazuli', 775 + img: 'EH3Ulol.png', 776 + opts: { 777 + series: [24], 778 + stage: [6] 779 + } 780 + }, 781 + { 782 + name: 'Eternity Larva', 783 + img: 'ql5KxJH.png', 784 + opts: { 785 + series: [26], 786 + stage: [0] 787 + } 788 + }, 789 + { 790 + name: 'Sakata Nemuno', 791 + img: 'gYFhJ3Y.png', 792 + opts: { 793 + series: [26], 794 + stage: [1] 795 + } 796 + }, 797 + { 798 + name: 'Komano Aunn', 799 + img: 'wWUStkF.png', 800 + opts: { 801 + series: [26], 802 + stage: [2] 803 + } 804 + }, 805 + { 806 + name: 'Yatadera Narumi', 807 + img: 'wrPVhAo.png', 808 + opts: { 809 + series: [26], 810 + stage: [3] 811 + } 812 + }, 813 + { 814 + name: 'Nishida Satono', 815 + img: '8w9gAy2.png', 816 + opts: { 817 + series: [26], 818 + stage: [4] 819 + } 820 + }, 821 + { 822 + name: 'Teireida Mai', 823 + img: 'DesAqAC.png', 824 + opts: { 825 + series: [26], 826 + stage: [4] 827 + } 828 + }, 829 + { 830 + name: 'Matara Okina', 831 + img: 'icWvMyo.png', 832 + opts: { 833 + series: [26], 834 + stage: [5, 6] 835 + } 836 + }, 837 + { 838 + name: 'Yorigami Shion', 839 + img: 'LenxXR4.png', 840 + opts: { 841 + series: [25], 842 + stage: [5] 843 + } 844 + }, 845 + { 846 + name: 'Yorigami Joon', 847 + img: '0aPaTm8.png', 848 + opts: { 849 + series: [25], 850 + stage: [5] 851 + } 852 + }, 853 + { 854 + name: 'Hieda no Akyuu', 855 + img: 'ogONuLZ.png', 856 + opts: { 857 + series: [0], 858 + stage: [] 859 + } 860 + }, 861 + { 862 + name: 'Tokiko', 863 + img: 'Y4maOc8.png', 864 + opts: { 865 + series: [0], 866 + stage: [] 867 + } 868 + }, 869 + { 870 + name: 'Reisen (Manga)', 871 + img: 'cWjCo2j.png', 872 + opts: { 873 + series: [0], 874 + stage: [] 875 + } 876 + }, 877 + { 878 + name: 'Watatsuki no Toyohime', 879 + img: 'uEBxsEX.png', 880 + opts: { 881 + series: [0], 882 + stage: [] 883 + } 884 + }, 885 + { 886 + name: 'Watatsuki no Yorihime', 887 + img: 'Txu2P7S.png', 888 + opts: { 889 + series: [0], 890 + stage: [] 891 + } 892 + }, 893 + { 894 + name: 'Maribel Hearn', 895 + img: 'XUI9vPo.png', 896 + opts: { 897 + series: [0], 898 + stage: [] 899 + } 900 + }, 901 + { 902 + name: 'Usami Renko', 903 + img: '1P5EXRt.png', 904 + opts: { 905 + series: [0], 906 + stage: [] 907 + } 908 + }, 909 + { 910 + name: 'Ibaraki Kasen', 911 + img: 'dQHnPPe.png', 912 + opts: { 913 + series: [0, 23], 914 + stage: [4] 915 + } 916 + }, 917 + { 918 + name: 'Motoori Kosuzu', 919 + img: 'jEsJJo8.png', 920 + opts: { 921 + series: [0], 922 + stage: [] 923 + } 924 + }, 925 + { 926 + name: 'Hakurei Reimu (PC-98)', 927 + img: 'IZsGAMS.png', 928 + opts: { 929 + series: [1, 2, 3, 4, 5], 930 + stage: [3], 931 + pc98: true 932 + } 933 + }, 934 + { 935 + name: 'Shingyoku (Female)', 936 + img: 'KuPiR2k.png', 937 + opts: { 938 + series: [1], 939 + stage: [0] 940 + } 941 + }, 942 + { 943 + name: 'Mima', 944 + img: 'odH03t2.png', 945 + opts: { 946 + series: [1, 2, 3, 5], 947 + stage: [2, 4, 5] 948 + } 949 + }, 950 + { 951 + name: 'Elis', 952 + img: 'ytnL1xd.png', 953 + opts: { 954 + series: [1], 955 + stage: [4] 956 + } 957 + }, 958 + { 959 + name: 'Kikuri', 960 + img: 'fX2Kqik.png', 961 + opts: { 962 + series: [1], 963 + stage: [4] 964 + } 965 + }, 966 + { 967 + name: 'Sariel', 968 + img: 'Wyc7YFw.png', 969 + opts: { 970 + series: [1], 971 + stage: [5] 972 + } 973 + }, 974 + { 975 + name: 'Konngara', 976 + img: 'dg9jLHv.png', 977 + opts: { 978 + series: [1], 979 + stage: [5] 980 + } 981 + }, 982 + { 983 + name: 'Rika', 984 + img: '02Xb4pU.png', 985 + opts: { 986 + series: [2], 987 + stage: [0, 6] 988 + } 989 + }, 990 + { 991 + name: 'Meira', 992 + img: 'p529JgT.png', 993 + opts: { 994 + series: [2], 995 + stage: [1] 996 + } 997 + }, 998 + { 999 + name: 'Kirisame Marisa (PC-98)', 1000 + img: 'wxE7cBm.png', 1001 + opts: { 1002 + series: [2, 3, 4, 5], 1003 + stage: [3], 1004 + pc98: true 1005 + } 1006 + }, 1007 + { 1008 + name: 'Ellen', 1009 + img: '3iNNL0c.png', 1010 + opts: { 1011 + series: [3], 1012 + stage: [] 1013 + } 1014 + }, 1015 + { 1016 + name: 'Kotohime', 1017 + img: 'kRSGtpq.png', 1018 + opts: { 1019 + series: [3], 1020 + stage: [] 1021 + } 1022 + }, 1023 + { 1024 + name: 'Kana Anaberal', 1025 + img: 'rBvKMk5.png', 1026 + opts: { 1027 + series: [3], 1028 + stage: [] 1029 + } 1030 + }, 1031 + { 1032 + name: 'Asakura Rikako', 1033 + img: 'VIf5gUK.png', 1034 + opts: { 1035 + series: [3], 1036 + stage: [] 1037 + } 1038 + }, 1039 + { 1040 + name: 'Kitashirakawa Chiyuri', 1041 + img: 'tZFBycy.png', 1042 + opts: { 1043 + series: [3], 1044 + stage: [4] 1045 + } 1046 + }, 1047 + { 1048 + name: 'Okazaki Yumemi', 1049 + img: 'c9rnG3n.png', 1050 + opts: { 1051 + series: [3], 1052 + stage: [5] 1053 + } 1054 + }, 1055 + { 1056 + name: 'Ruukoto', 1057 + img: 'dko67SJ.png', 1058 + opts: { 1059 + series: [3], 1060 + stage: [] 1061 + } 1062 + }, 1063 + { 1064 + name: 'Orange', 1065 + img: 'm8wXE5U.png', 1066 + opts: { 1067 + series: [4], 1068 + stage: [0] 1069 + } 1070 + }, 1071 + { 1072 + name: 'Kurumi', 1073 + img: '0rvq1ph.png', 1074 + opts: { 1075 + series: [4], 1076 + stage: [1] 1077 + } 1078 + }, 1079 + { 1080 + name: 'Elly', 1081 + img: 'iIPftHn.png', 1082 + opts: { 1083 + series: [4], 1084 + stage: [2] 1085 + } 1086 + }, 1087 + { 1088 + name: 'Yuuka (PC-98)', 1089 + img: 'ivUSwxp.png', 1090 + opts: { 1091 + series: [4, 5], 1092 + stage: [4, 5], 1093 + pc98: true 1094 + } 1095 + }, 1096 + { 1097 + name: 'Mugetsu', 1098 + img: 'bYA9E16.png', 1099 + opts: { 1100 + series: [4], 1101 + stage: [6] 1102 + } 1103 + }, 1104 + { 1105 + name: 'Gengetsu', 1106 + img: 'TIOTtV9.png', 1107 + opts: { 1108 + series: [4], 1109 + stage: [6] 1110 + } 1111 + }, 1112 + { 1113 + name: 'Sara', 1114 + img: '2QUbCrU.png', 1115 + opts: { 1116 + series: [5], 1117 + stage: [0] 1118 + } 1119 + }, 1120 + { 1121 + name: 'Louise', 1122 + img: 'nDM5aB6.png', 1123 + opts: { 1124 + series: [5], 1125 + stage: [1, 3] 1126 + } 1127 + }, 1128 + { 1129 + name: 'Alice (PC-98)', 1130 + img: 'KaBuRTW.png', 1131 + opts: { 1132 + series: [5], 1133 + stage: [2, 6], 1134 + pc98: true 1135 + } 1136 + }, 1137 + { 1138 + name: 'Yuki', 1139 + img: 'FfcmDgp.png', 1140 + opts: { 1141 + series: [5], 1142 + stage: [3] 1143 + } 1144 + }, 1145 + { 1146 + name: 'Mai', 1147 + img: 'r6w7TX1.png', 1148 + opts: { 1149 + series: [5], 1150 + stage: [3] 1151 + } 1152 + }, 1153 + { 1154 + name: 'Yumeko', 1155 + img: 'PcPqkdO.png', 1156 + opts: { 1157 + series: [5], 1158 + stage: [4] 1159 + } 1160 + }, 1161 + { 1162 + name: 'Shinki', 1163 + img: 'gPE95S7.png', 1164 + opts: { 1165 + series: [5], 1166 + stage: [5] 1167 + } 1168 + }, 1169 + { 1170 + name: 'Mimi-chan', 1171 + img: 'zBl2zlv.png', 1172 + opts: { 1173 + series: [3], 1174 + stage: [], 1175 + notgirl: true 1176 + } 1177 + }, 1178 + { 1179 + name: 'Unzan', 1180 + img: 'r5eWREh.png', 1181 + opts: { 1182 + series: [15, 17, 20, 23, 25], 1183 + stage: [2], 1184 + notgirl: true 1185 + } 1186 + }, 1187 + { 1188 + name: 'Genji', 1189 + img: 'LoUqOuH.png', 1190 + opts: { 1191 + series: [2, 3, 4, 5], 1192 + stage: [], 1193 + notgirl: true 1194 + } 1195 + }, 1196 + { 1197 + name: 'Shingyoku (Male)', 1198 + img: 'a5uwlgN.png', 1199 + opts: { 1200 + series: [1], 1201 + stage: [0], 1202 + notgirl: true 1203 + } 1204 + }, 1205 + { 1206 + name: 'YuugenMagan', 1207 + img: 'IOW8GdU.png', 1208 + opts: { 1209 + series: [1], 1210 + stage: [2], 1211 + notgirl: true 1212 + } 1213 + }, 1214 + { 1215 + name: 'Evil Eye Sigma', 1216 + img: 'rAFUMwE.png', 1217 + opts: { 1218 + series: [2], 1219 + stage: [6], 1220 + notgirl: true 1221 + } 1222 + }, 1223 + { 1224 + name: 'Great Catfish', 1225 + img: 'BgRi9Oh.png', 1226 + opts: { 1227 + series: [16, 25], 1228 + stage: [5], 1229 + notgirl: true 1230 + } 1231 + }, 1232 + { 1233 + name: 'Morichika Rinnosuke', 1234 + img: 'ITUhsGj.png', 1235 + opts: { 1236 + series: [0, 20], 1237 + stage: [], 1238 + notgirl: true 1239 + } 1240 + }, 1241 + { 1242 + name: 'Fortune Teller', 1243 + img: 'BYot23O.png', 1244 + opts: { 1245 + series: [0], 1246 + stage: [], 1247 + notgirl: true 1248 + } 1249 + }, 1250 + { 1251 + name: 'Hisoutensoku', 1252 + img: 'P4JZ2it.png', 1253 + opts: { 1254 + series: [16], 1255 + stage: [], 1256 + notgirl: true 1257 + } 1258 + } 1259 + ];
+1
src/js/lz-string.min.js
··· 1 + var LZString=function(){function o(o,r){if(!t[o]){t[o]={};for(var n=0;n<o.length;n++)t[o][o.charAt(n)]=n}return t[o][r]}var r=String.fromCharCode,n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$",t={},i={compressToBase64:function(o){if(null==o)return"";var r=i._compress(o,6,function(o){return n.charAt(o)});switch(r.length%4){default:case 0:return r;case 1:return r+"===";case 2:return r+"==";case 3:return r+"="}},decompressFromBase64:function(r){return null==r?"":""==r?null:i._decompress(r.length,32,function(e){return o(n,r.charAt(e))})},compressToUTF16:function(o){return null==o?"":i._compress(o,15,function(o){return r(o+32)})+" "},decompressFromUTF16:function(o){return null==o?"":""==o?null:i._decompress(o.length,16384,function(r){return o.charCodeAt(r)-32})},compressToUint8Array:function(o){for(var r=i.compress(o),n=new Uint8Array(2*r.length),e=0,t=r.length;t>e;e++){var s=r.charCodeAt(e);n[2*e]=s>>>8,n[2*e+1]=s%256}return n},decompressFromUint8Array:function(o){if(null===o||void 0===o)return i.decompress(o);for(var n=new Array(o.length/2),e=0,t=n.length;t>e;e++)n[e]=256*o[2*e]+o[2*e+1];var s=[];return n.forEach(function(o){s.push(r(o))}),i.decompress(s.join(""))},compressToEncodedURIComponent:function(o){return null==o?"":i._compress(o,6,function(o){return e.charAt(o)})},decompressFromEncodedURIComponent:function(r){return null==r?"":""==r?null:(r=r.replace(/ /g,"+"),i._decompress(r.length,32,function(n){return o(e,r.charAt(n))}))},compress:function(o){return i._compress(o,16,function(o){return r(o)})},_compress:function(o,r,n){if(null==o)return"";var e,t,i,s={},p={},u="",c="",a="",l=2,f=3,h=2,d=[],m=0,v=0;for(i=0;i<o.length;i+=1)if(u=o.charAt(i),Object.prototype.hasOwnProperty.call(s,u)||(s[u]=f++,p[u]=!0),c=a+u,Object.prototype.hasOwnProperty.call(s,c))a=c;else{if(Object.prototype.hasOwnProperty.call(p,a)){if(a.charCodeAt(0)<256){for(e=0;h>e;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++),s[c]=f++,a=String(u)}if(""!==a){if(Object.prototype.hasOwnProperty.call(p,a)){if(a.charCodeAt(0)<256){for(e=0;h>e;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++)}for(t=2,e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;for(;;){if(m<<=1,v==r-1){d.push(n(m));break}v++}return d.join("")},decompress:function(o){return null==o?"":""==o?null:i._decompress(o.length,32768,function(r){return o.charCodeAt(r)})},_decompress:function(o,n,e){var t,i,s,p,u,c,a,l,f=[],h=4,d=4,m=3,v="",w=[],A={val:e(0),position:n,index:1};for(i=0;3>i;i+=1)f[i]=i;for(p=0,c=Math.pow(2,2),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(t=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 2:return""}for(f[3]=l,s=l,w.push(l);;){if(A.index>o)return"";for(p=0,c=Math.pow(2,m),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(l=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 2:return w.join("")}if(0==h&&(h=Math.pow(2,m),m++),f[l])v=f[l];else{if(l!==d)return null;v=s+s.charAt(0)}w.push(v),f[d++]=s+v.charAt(0),h--,s=v,0==h&&(h=Math.pow(2,m),m++)}}};return i}();"function"==typeof define&&define.amd?define(function(){return LZString}):"undefined"!=typeof module&&null!=module&&(module.exports=LZString);
+248
src/js/main.js
··· 1 + /** @type {CharacterData} */ 2 + let characterData = []; 3 + /** @type {CharacterData} */ 4 + let characterDataToSort = []; 5 + 6 + /** @type {Options} */ 7 + let options = []; 8 + 9 + /** @type {(boolean|boolean[])[]} */ 10 + let optTaken = []; 11 + 12 + let prngSeed = ''; 13 + let timeTaken = 0; 14 + let leftIndex = 0; 15 + let rightIndex = 0; 16 + let choices = ''; 17 + 18 + 19 + /** Initialize script. */ 20 + function init() { 21 + /** Define button behavior. */ 22 + document.querySelector('.starting.start.button').addEventListener('click', start); 23 + document.querySelector('.starting.load.button').addEventListener('click', loadProgress); 24 + 25 + document.querySelector('.left.sort.image').addEventListener('click', pick('left')); 26 + document.querySelector('.right.sort.image').addEventListener('click', pick('right')); 27 + 28 + document.querySelector('.sorting.tie.button').addEventListener('click', pick('tie')); 29 + document.querySelector('.sorting.undo.button').addEventListener('click', undo); 30 + document.querySelector('.sorting.save.button').addEventListener('click', saveProgress); 31 + 32 + setLatestDataset(); 33 + 34 + /** Decode query string if available. */ 35 + decodeQuery(window.location.search.slice(1)); 36 + } 37 + 38 + /** Begin sorting. */ 39 + function start() { 40 + document.querySelectorAll('input[type=checkbox]').forEach(cb => cb.disabled = true); 41 + 42 + prngSeed = prngSeed || new Date().toISOString().split('T')[0]; 43 + Math.seedrandom(prngSeed); 44 + 45 + /** Copy data into sorting array to filter. */ 46 + characterDataToSort = characterData.slice(0); 47 + 48 + /** Check selected options. */ 49 + options.forEach(opt => { 50 + if ('sub' in opt) { 51 + if (!document.getElementById(`cbgroup-${opt.key}`).checked) optTaken.push(false); 52 + else { 53 + let i = 0, suboptArray = []; 54 + while (i < opt.sub.length) { suboptArray.push(document.getElementById(`cb-${opt.key}-${i++}`).checked); } 55 + optTaken.push(suboptArray); 56 + } 57 + } else { optTaken.push(document.getElementById(`cb-${opt.key}`).checked); } 58 + }); 59 + 60 + /** Filter out deselected criteria. */ 61 + options.forEach((opt, index) => { 62 + if ('sub' in opt) { 63 + if (optTaken[index]) { 64 + const subArray = optTaken[index].reduce((subList, subBool, subIndex) => { 65 + if (subBool) { subList.push(subIndex); } 66 + return subList; 67 + }, []); 68 + characterDataToSort = characterDataToSort.filter(char => { 69 + return char.opts[opt.key].some(subidx => subArray.includes(subidx)); 70 + }); 71 + } 72 + } else if (optTaken[index]) { 73 + characterDataToSort = characterDataToSort.filter(char => !char.opts[opt.key]); 74 + } 75 + }); 76 + } 77 + 78 + /** 79 + * Sort between two character choices or tie. 80 + * 81 + * @param {'left'|'right'|'tie'} sortType 82 + */ 83 + function pick(sortType) { 84 + 85 + } 86 + 87 + /** Undo previous choice. */ 88 + function undo() {} 89 + 90 + function display() {} 91 + 92 + function result() {} 93 + 94 + function saveProgress() {} 95 + 96 + function loadProgress() {} 97 + 98 + function loadResult() {} 99 + 100 + function generateImage() {} 101 + 102 + function generateTextList() {} 103 + 104 + function generateLink() { 105 + // LZString.compressToEncodedURIComponent() 106 + } 107 + 108 + function setLatestDataset() { 109 + /** Use latest character data and options. */ 110 + const latestDateIndex = Object.keys(dataSet) 111 + .map(date => new Date(date)) 112 + .reduce((latestDateIndex, currentDate, currentIndex, array) => { 113 + return currentDate > array[latestDateIndex] ? currentIndex : latestDateIndex; 114 + }, 0); 115 + const latestDate = Object.keys(dataSet)[latestDateIndex]; 116 + 117 + characterData = dataSet[latestDate].characterData; 118 + options = dataSet[latestDate].options; 119 + 120 + /** Set some defaults. */ 121 + prngSeed = ''; 122 + timeTaken = 0; 123 + choices = ''; 124 + 125 + /** Insert data from new options. */ 126 + populateOptions(); 127 + } 128 + 129 + /** Populate option list. */ 130 + function populateOptions() { 131 + const optList = document.querySelector('.options'); 132 + const optInsert = (name, id, tooltip = '', checked = true, disabled = false) => { 133 + return `<div><label class="${tooltip?'tooltip':''}" title="${tooltip}"><input id="cb-${id}" type="checkbox" ${checked?'checked':''} ${disabled?'disabled':''}> ${name}</label></div>`; 134 + }; 135 + const optInsertLarge = (name, id, tooltip = '', checked = true) => { 136 + return `<div class="large option"><label class="${tooltip?'tooltip':''}" title="${tooltip}"><input id="cbgroup-${id}" type="checkbox" ${checked?'checked':''}> ${name}</label></div>`; 137 + }; 138 + 139 + /** Clear out any previous options. */ 140 + optList.innerHTML = ''; 141 + 142 + /** Insert sorter options and set grouped option behavior. */ 143 + options.forEach(opt => { 144 + if ('sub' in opt) { 145 + optList.insertAdjacentHTML('beforeend', optInsertLarge(opt.name, opt.key, opt.tooltip, opt.checked)); 146 + opt.sub.forEach((subopt, subindex) => { 147 + optList.insertAdjacentHTML('beforeend', optInsert(subopt.name, `${opt.key}-${subindex}`, subopt.tooltip, subopt.checked, opt.checked === false)); 148 + }); 149 + optList.insertAdjacentHTML('beforeend', '<hr>'); 150 + 151 + const groupbox = document.getElementById(`cbgroup-${opt.key}`); 152 + 153 + groupbox.parentElement.addEventListener('click', () => { 154 + let i = 0; 155 + while (i < opt.sub.length) { 156 + document.getElementById(`cb-${opt.key}-${i}`).disabled = !groupbox.checked; 157 + if (groupbox.checked) { document.getElementById(`cb-${opt.key}-${i}`).checked = true; } 158 + i++; 159 + } 160 + }); 161 + } else { 162 + optList.insertAdjacentHTML('beforeend', optInsert(opt.name, opt.key, opt.tooltip, opt.checked)); 163 + } 164 + }); 165 + } 166 + 167 + /** 168 + * Decodes compressed shareable link query string. 169 + * @param {string} queryString 170 + */ 171 + function decodeQuery(queryString) { 172 + if (!queryString) return; 173 + 174 + let successfulLoad; 175 + 176 + try { 177 + /** 178 + * Retrieve data from compressed string. 179 + * @type {string[]} 180 + */ 181 + const decoded = LZString.decompressFromEncodedURIComponent(queryString).split('|'); 182 + 183 + prngSeed = decoded[0]; 184 + timeTaken = Number(decoded[1]); 185 + choices = decoded[3]; 186 + 187 + const optDecoded = decoded[2]; 188 + const suboptDecoded = decoded.slice(0); 189 + 190 + /** 191 + * Get latest data set version from before the seed's date. 192 + * If seed date is before any of the datasets, get the earliest one. 193 + */ 194 + const seedDate = { str: prngSeed, val: new Date(prngSeed) }; 195 + const dateMap = Object.keys(dataSet) 196 + .map(date => { 197 + return { str: date, val: new Date(date) }; 198 + }) 199 + const dateVersion = dateMap 200 + .sort((a, b) => a.val - b.val) 201 + .reduce((prevDate, currDate) => { 202 + return currDate.val > prevDate.val && currDate.val > seedDate.val ? prevDate : currDate; 203 + }, seedDate); 204 + const targetVersion = dateVersion.val < dateMap[0].val ? dateMap[0].str : dateVersion.str; 205 + 206 + options = dataSet[targetVersion].options; 207 + characterData = dataSet[targetVersion].characterData; 208 + 209 + /** Populate option list and decode options selected. */ 210 + populateOptions(); 211 + 212 + let suboptDecodedIndex = 0; 213 + options.forEach((opt, index) => { 214 + if ('sub' in opt) { 215 + const optIsTrue = optDecoded[index] === '1'; 216 + document.getElementById(`cbgroup-${opt.key}`).checked = optIsTrue; 217 + opt.sub.forEach((subopt, subindex) => { 218 + const subIsTrue = optIsTrue ? suboptDecoded[suboptDecodedIndex][subindex] === '1' : true; 219 + document.getElementById(`cb-${opt.key}-${subindex}`).checked = subIsTrue; 220 + document.getElementById(`cb-${opt.key}-${subindex}`).disabled = optIsTrue; 221 + }); 222 + suboptDecodedIndex = suboptDecodedIndex + optIsTrue ? 1 : 0; 223 + } else { document.getElementById(`cb-${opt.key}`).checked = optDecoded[index] === '1'; } 224 + }); 225 + 226 + successfulLoad = true; 227 + } catch (err) { 228 + console.error(`Error loading shareable link: ${err}`); 229 + setLatestDataset(); // Restore to default function if loading link does not work. 230 + } 231 + 232 + if (successfulLoad) { start(); } 233 + } 234 + 235 + function preloadImages() { 236 + const loadImage = (src) => { 237 + return new Promise((resolve, reject) => { 238 + const img = new Image(); 239 + img.onload = () => resolve(img); 240 + img.onerror = img.onabort = () => reject(src); 241 + img.src = src; 242 + }); 243 + }; 244 + const promises = characterData.map(char => loadImage(imageRoot + char.img)); 245 + return Promise.all(promises); 246 + } 247 + 248 + window.onload = init;
-40
src/main.js
··· 1 - /** 2 - * Initialize script. 3 - */ 4 - function init() { 5 - const optList = document.querySelector('.options'); 6 - const optInsert = (name, id, tooltip = '', checked = true, disabled = false) => { 7 - return `<div><label title="${tooltip}"><input id="cb-${id}" type="checkbox" ${checked?'checked':''} ${disabled?'disabled':''}> ${name}</label></div>`; 8 - }; 9 - const optInsertLarge = (name, id, tooltip = '', checked = true) => { 10 - return `<div class="large option"><label title="${tooltip}"><input id="cbgroup-${id}" type="checkbox" ${checked?'checked':''}> ${name}</label></div>`; 11 - }; 12 - 13 - /** 14 - * Insert sorter options and set grouped option behavior. 15 - */ 16 - options.forEach(opt => { 17 - if ('sub' in opt) { 18 - optList.insertAdjacentHTML('beforeend', optInsertLarge(opt.name, opt.key, opt.tooltip, opt.checked)); 19 - opt.sub.forEach((subopt, subindex) => { 20 - optList.insertAdjacentHTML('beforeend', optInsert(subopt.name, `${opt.key}-${subindex}`, subopt.tooltip, subopt.checked, opt.checked === false)); 21 - }); 22 - optList.insertAdjacentHTML('beforeend', '<hr>'); 23 - 24 - const groupbox = document.querySelector(`#cbgroup-${opt.key}`); 25 - 26 - groupbox.parentElement.addEventListener('click', () => { 27 - let i = 0; 28 - while (i < opt.sub.length) { 29 - document.getElementById(`cb-${opt.key}-${i}`).disabled = !groupbox.checked; 30 - if (groupbox.checked) { document.getElementById(`cb-${opt.key}-${i}`).checked = true; } 31 - i++; 32 - } 33 - }); 34 - } else { 35 - optList.insertAdjacentHTML('beforeend', optInsert(opt.name, opt.key, opt.tooltip, opt.checked)); 36 - } 37 - }); 38 - } 39 - 40 - window.onload = init;
src/reset.css src/css/reset.css
src/seedrandom.min.js src/js/seedrandom.min.js
-143
src/styles.css
··· 1 - body { 2 - font-family: Arial, Helvetica, sans-serif; 3 - font-size: 16px; 4 - } 5 - 6 - .container { 7 - display: grid; 8 - grid-row-gap: 1em; 9 - margin-top: 1em; 10 - } 11 - 12 - .progress { 13 - margin: 1em auto; 14 - width: 500px; 15 - display: none; 16 - } 17 - 18 - .progressbar { 19 - position: relative; 20 - width: 492px; 21 - border: 1px solid black; 22 - padding: 3px; 23 - margin: 2px 0px; 24 - } 25 - 26 - .progresstext { 27 - position: absolute; 28 - width: 492px; 29 - margin: 3px 0px; 30 - text-align: center; 31 - font-size: 0.7em; 32 - } 33 - 34 - .progressfill { 35 - height: 20px; 36 - background-color: lightgreen; 37 - width: 79%; 38 - } 39 - 40 - .sorter { 41 - margin: 0px auto; 42 - display: grid; 43 - grid-template-columns: 120px 1fr 120px; 44 - grid-gap: 5px; 45 - width: 420px; 46 - } 47 - 48 - .sorter > * { 49 - cursor: pointer; 50 - } 51 - 52 - .button { 53 - border: 1px solid black; 54 - text-align: center; 55 - padding: 10%; 56 - grid-column: 2 / 3; 57 - } 58 - 59 - .starting.button { 60 - grid-row: span 3; 61 - } 62 - 63 - .sorting.button { 64 - grid-row: span 2; 65 - display: none; 66 - } 67 - 68 - .sorter > .image { 69 - width: 120px; 70 - height: 180px; 71 - margin: auto; 72 - grid-row: 1 / 7; 73 - } 74 - 75 - .sorter > .text { 76 - width: 120px; 77 - height: 60px; 78 - display: none; 79 - } 80 - 81 - .sorter > .text > p { 82 - margin: 5px 5px; 83 - width: calc(100%-10px); 84 - text-align: center; 85 - font-size: 0.8em; 86 - line-height: 1.5em; 87 - } 88 - 89 - .sorter > .left { 90 - grid-column: 1 / 2; 91 - border: 1px solid #000000; 92 - } 93 - 94 - .sorter > .right { 95 - grid-column: 3 / 4; 96 - border: 1px solid #000000; 97 - } 98 - 99 - .options { 100 - margin: 1em auto; 101 - display: grid; 102 - text-align: left; 103 - grid-template-columns: repeat(3, 1fr); 104 - grid-gap: 10px; 105 - width: 450px; 106 - } 107 - 108 - .options > div { 109 - font-size: 0.5625em; 110 - } 111 - 112 - label { 113 - cursor: pointer; 114 - } 115 - 116 - .options > .large.option, .options > hr { 117 - grid-column: span 3; 118 - text-align: center; 119 - width: 100%; 120 - } 121 - 122 - .info { 123 - margin: 0px auto 3em; 124 - display: block; 125 - text-align: center; 126 - font-size: 0.6875em; 127 - width: 80%; 128 - line-height: 1.2em; 129 - } 130 - 131 - a { 132 - color: #990000; 133 - font-weight: bold; 134 - text-decoration: none; 135 - } 136 - 137 - a:hover { 138 - color: #FF6600; 139 - } 140 - 141 - a:visited { 142 - color: #6600FF; 143 - }
src/yinyang.ico src/assets/yinyang.ico
+14
test.js
··· 1 + const prngSeed = '2000-01-01' 2 + const seedDate = { str: prngSeed, val: new Date(prngSeed) }; 3 + const dateMap = ['2018-01-01', '2017-01-01'] 4 + .map(date => { 5 + return { str: date, val: new Date(date) }; 6 + }) 7 + .sort((a, b) => a.val - b.val); 8 + let dateVersion = dateMap 9 + .reduce((prevDate, currDate) => { 10 + return currDate.val > prevDate.val && currDate.val > seedDate.val ? prevDate : currDate; 11 + }, seedDate).str; 12 + dateVersion = new Date(dateVersion) < dateMap[0].val ? dateMap[0].str : dateVersion; 13 + 14 + console.log(dateVersion);