diff options
77 files changed, 3114 insertions, 1029 deletions
@@ -0,0 +1,19 @@ +# TODO + +- [ ] Make all pony heads the same size (square) +- [ ] Add a "similar pages" section in the navigation bar + +---- + +- [x] <s>Remove "Sensitivity" item</s> +- [x] <s>Rename "Sexually active" to "Preemptive sexual consent"</s> +- [x] <s>Add romantic/sexual alignments</s> + - [x] <s>Aromantic/asexual (default), straight, gay/lesbian (pick automatically depending on gender), bi, pan</s> + - [x] <s>Leave some bits for potentially more options</s> +- [x] <s>Toggles for sexual polyamory, romantic polyamory, or both</s> +- [x] <s>Actually remove deprecated values</s> +- [x] <s>Allow caretakers for younger ponies (not just littles)</s> +- [x] <s>Redesign the "systems rules" page</s> +- [x] <s>Add Markdown support</s> +- [x] <s>Redesign the bitset calculator</s> +- [x] <s>Redesign the "front planner" page</s>
\ No newline at end of file diff --git a/api/desktop-app.php b/api/desktop-app.php new file mode 100644 index 0000000..20b1da9 --- /dev/null +++ b/api/desktop-app.php @@ -0,0 +1,76 @@ +<?php + +require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/bitset.inc"; + +if (!isset($_GET["id"])) die(); + +if (isset($_GET["config"])) { + $data = []; + + $keys = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/permitted.json"), true); + $proceed = false; + + if (in_array($_GET["id"], array_keys($keys))) { + $proceed = true; + $fronters = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/" . $keys[$_GET["id"]]["system"] . "/fronters.json"), true)["members"]; + + if (count($fronters) > 0) { + $fronter = $fronters[0]; + $info = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/" . $fronter["id"] . ".json"), true); + $info = parseMetadata($info); + + if (isset($info["birth"]["age"]) && $info["birth"]["age"] < 16 && $info["birth"]["age"] > 0) { + $proceed = false; + } else if (isset($info["birth"]["year"]) && $info["birth"]["year"] > 1900) { + if (!isset($info["birth"]["date"])) $info["birth"]["date"] = "01-01"; + + $age = (int)date('Y') - $info["birth"]["year"] + (strtotime(date('Y') . "-" . $info["birth"]["date"]) <= time() ? 0 : -1); + + if ($age < 16) { + $proceed = false; + } + } + } + } + + if ($proceed) { + $data = $keys[$_GET["id"]]["config"] ?? []; + } +} else { + $data = [ + "success" => true, + "valid" => false, + "underage" => false, + "normal" => false + ]; + + $keys = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/permitted.json"), true); + + if (in_array($_GET["id"], array_keys($keys))) { + $data["valid"] = true; + + $fronters = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/" . $keys[$_GET["id"]]["system"] . "/fronters.json"), true)["members"]; + + if (count($fronters) > 0) { + $fronter = $fronters[0]; + $info = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/" . $fronter["id"] . ".json"), true); + $info = parseMetadata($info); + + $data["normal"] = $info["sexually_active"]; + + if (isset($info["birth"]["age"]) && $info["birth"]["age"] < 16 && $info["birth"]["age"] > 0) { + $data["underage"] = true; + } else if (isset($info["birth"]["year"]) && $info["birth"]["year"] > 1900) { + if (!isset($info["birth"]["date"])) $info["birth"]["date"] = "01-01"; + + $age = (int)date('Y') - $info["birth"]["year"] + (strtotime(date('Y') . "-" . $info["birth"]["date"]) <= time() ? 0 : -1); + + if ($age < 16) { + $data["underage"] = true; + } + } + } + } +} + +die(json_encode($data));
\ No newline at end of file @@ -18,6 +18,7 @@ if (in_array($toplevel, ["editor", "icons", "species", "uploads"])) { header("Location: /?error=" . $lang["app"]["file"]) and die(); } } elseif ($toplevel === "") { + $pageFile = $_SERVER['DOCUMENT_ROOT'] . "/pages/home.inc"; require_once $_SERVER['DOCUMENT_ROOT'] . "/pages/home.inc"; } else { if ($toplevel === "-") { @@ -25,12 +26,14 @@ if (in_array($toplevel, ["editor", "icons", "species", "uploads"])) { $toplevel = explode("/", $pagename)[0]; if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/pages/" . $toplevel . ".inc")) { + $pageFile = $_SERVER['DOCUMENT_ROOT'] . "/pages/" . $toplevel . ".inc"; require_once $_SERVER['DOCUMENT_ROOT'] . "/pages/" . $toplevel . ".inc"; } else { header("Location: /?error=" . $lang["app"]["page"] . " " . strip_tags($pagename)) and die(); } } else if ($toplevel === "api") { if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/pages/" . $toplevel . ".inc")) { + $pageFile = $_SERVER['DOCUMENT_ROOT'] . "/pages/" . $toplevel . ".inc"; require_once $_SERVER['DOCUMENT_ROOT'] . "/pages/" . $toplevel . ".inc"; } } else if ($toplevel === "cloudburst" || $toplevel === "raindrops") { @@ -50,6 +53,7 @@ if (in_array($toplevel, ["editor", "icons", "species", "uploads"])) { die(); } + $pageFile = $_SERVER['DOCUMENT_ROOT'] . "/pages/page.inc"; require_once $_SERVER['DOCUMENT_ROOT'] . "/pages/page.inc"; } else { if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/pages/" . $toplevel . ".inc")) { @@ -63,9 +67,11 @@ if (in_array($toplevel, ["editor", "icons", "species", "uploads"])) { }, json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd/members.json"), true)), "unknown-rd"]; if ((in_array($toplevel, $namesCloudburst) || in_array($toplevel, $namesRaindrops)) && $toplevel !== "unknown") { + $pageFile = $_SERVER['DOCUMENT_ROOT'] . "/pages/page.inc"; require_once $_SERVER['DOCUMENT_ROOT'] . "/pages/page.inc"; } else { global $toplevel; + $pageFile = $_SERVER['DOCUMENT_ROOT'] . "/includes/short.inc"; require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/short.inc"; } } diff --git a/app/fronters/profiles/auvwc.png b/app/fronters/profiles/auvwc.png Binary files differindex ac404f7..8eeb044 100644 --- a/app/fronters/profiles/auvwc.png +++ b/app/fronters/profiles/auvwc.png diff --git a/app/fronters/profiles/bbrig.png b/app/fronters/profiles/bbrig.png Binary files differindex 8dc8c32..138d98f 100644 --- a/app/fronters/profiles/bbrig.png +++ b/app/fronters/profiles/bbrig.png diff --git a/app/fronters/profiles/ckqsw.png b/app/fronters/profiles/ckqsw.png Binary files differindex 83acb02..b6aec17 100644 --- a/app/fronters/profiles/ckqsw.png +++ b/app/fronters/profiles/ckqsw.png diff --git a/app/fronters/profiles/dogwu.png b/app/fronters/profiles/dogwu.png Binary files differindex 60302f5..d0ad4cc 100644 --- a/app/fronters/profiles/dogwu.png +++ b/app/fronters/profiles/dogwu.png diff --git a/app/fronters/profiles/eebmh.png b/app/fronters/profiles/eebmh.png Binary files differindex 07cb4fc..b09a6c8 100644 --- a/app/fronters/profiles/eebmh.png +++ b/app/fronters/profiles/eebmh.png diff --git a/app/fronters/profiles/erefx.png b/app/fronters/profiles/erefx.png Binary files differindex 3c9be5f..0b908c1 100644 --- a/app/fronters/profiles/erefx.png +++ b/app/fronters/profiles/erefx.png diff --git a/app/fronters/profiles/erknz.png b/app/fronters/profiles/erknz.png Binary files differindex 96e6dc8..6b08bd7 100644 --- a/app/fronters/profiles/erknz.png +++ b/app/fronters/profiles/erknz.png diff --git a/app/fronters/profiles/exudo.png b/app/fronters/profiles/exudo.png Binary files differindex 9118868..0a90596 100644 --- a/app/fronters/profiles/exudo.png +++ b/app/fronters/profiles/exudo.png diff --git a/app/fronters/profiles/fdaay.png b/app/fronters/profiles/fdaay.png Binary files differindex 602a400..6631d16 100644 --- a/app/fronters/profiles/fdaay.png +++ b/app/fronters/profiles/fdaay.png diff --git a/app/fronters/profiles/gevde.png b/app/fronters/profiles/gevde.png Binary files differindex 11837f5..677e598 100644 --- a/app/fronters/profiles/gevde.png +++ b/app/fronters/profiles/gevde.png diff --git a/app/fronters/profiles/gfhsr.png b/app/fronters/profiles/gfhsr.png Binary files differindex b8a7eec..7132071 100644 --- a/app/fronters/profiles/gfhsr.png +++ b/app/fronters/profiles/gfhsr.png diff --git a/app/fronters/profiles/ghrby.png b/app/fronters/profiles/ghrby.png Binary files differindex 2e7fd08..b176521 100644 --- a/app/fronters/profiles/ghrby.png +++ b/app/fronters/profiles/ghrby.png diff --git a/app/fronters/profiles/hpwyq.png b/app/fronters/profiles/hpwyq.png Binary files differindex 6ad6c22..87b3078 100644 --- a/app/fronters/profiles/hpwyq.png +++ b/app/fronters/profiles/hpwyq.png diff --git a/app/fronters/profiles/irxyh.png b/app/fronters/profiles/irxyh.png Binary files differindex cee8265..a77bec6 100644 --- a/app/fronters/profiles/irxyh.png +++ b/app/fronters/profiles/irxyh.png diff --git a/app/fronters/profiles/jjzcb.png b/app/fronters/profiles/jjzcb.png Binary files differnew file mode 100644 index 0000000..0800abb --- /dev/null +++ b/app/fronters/profiles/jjzcb.png diff --git a/app/fronters/profiles/jnbae.png b/app/fronters/profiles/jnbae.png Binary files differindex a7e8a6e..dc71f43 100644 --- a/app/fronters/profiles/jnbae.png +++ b/app/fronters/profiles/jnbae.png diff --git a/app/fronters/profiles/khsbb.png b/app/fronters/profiles/khsbb.png Binary files differindex d7a0595..34998de 100644 --- a/app/fronters/profiles/khsbb.png +++ b/app/fronters/profiles/khsbb.png diff --git a/app/fronters/profiles/kkhbw.png b/app/fronters/profiles/kkhbw.png Binary files differindex e943fa5..c461040 100644 --- a/app/fronters/profiles/kkhbw.png +++ b/app/fronters/profiles/kkhbw.png diff --git a/app/fronters/profiles/lllfw.png b/app/fronters/profiles/lllfw.png Binary files differindex 64fe05d..98526b2 100644 --- a/app/fronters/profiles/lllfw.png +++ b/app/fronters/profiles/lllfw.png diff --git a/app/fronters/profiles/lqolg.png b/app/fronters/profiles/lqolg.png Binary files differnew file mode 100644 index 0000000..beace78 --- /dev/null +++ b/app/fronters/profiles/lqolg.png diff --git a/app/fronters/profiles/lzlaq.png b/app/fronters/profiles/lzlaq.png Binary files differindex 98846f5..01ccf00 100644 --- a/app/fronters/profiles/lzlaq.png +++ b/app/fronters/profiles/lzlaq.png diff --git a/app/fronters/profiles/mglyq.png b/app/fronters/profiles/mglyq.png Binary files differindex 52c60f4..17b3156 100644 --- a/app/fronters/profiles/mglyq.png +++ b/app/fronters/profiles/mglyq.png diff --git a/app/fronters/profiles/mhnqy.png b/app/fronters/profiles/mhnqy.png Binary files differindex c3e1d43..7966132 100644 --- a/app/fronters/profiles/mhnqy.png +++ b/app/fronters/profiles/mhnqy.png diff --git a/app/fronters/profiles/mvaws.png b/app/fronters/profiles/mvaws.png Binary files differindex 3d152a9..ae45fef 100644 --- a/app/fronters/profiles/mvaws.png +++ b/app/fronters/profiles/mvaws.png diff --git a/app/fronters/profiles/pabmo.png b/app/fronters/profiles/pabmo.png Binary files differindex 7f8f020..117a75f 100644 --- a/app/fronters/profiles/pabmo.png +++ b/app/fronters/profiles/pabmo.png diff --git a/app/fronters/profiles/qbzxm.png b/app/fronters/profiles/qbzxm.png Binary files differindex 3679f54..e2848ad 100644 --- a/app/fronters/profiles/qbzxm.png +++ b/app/fronters/profiles/qbzxm.png diff --git a/app/fronters/profiles/qcemf.png b/app/fronters/profiles/qcemf.png Binary files differindex 609d535..debf1a7 100644 --- a/app/fronters/profiles/qcemf.png +++ b/app/fronters/profiles/qcemf.png diff --git a/app/fronters/profiles/qraku.png b/app/fronters/profiles/qraku.png Binary files differindex 10858a6..50d25b5 100644 --- a/app/fronters/profiles/qraku.png +++ b/app/fronters/profiles/qraku.png diff --git a/app/fronters/profiles/rdstg.png b/app/fronters/profiles/rdstg.png Binary files differindex 2fbc54c..e1f15af 100644 --- a/app/fronters/profiles/rdstg.png +++ b/app/fronters/profiles/rdstg.png diff --git a/app/fronters/profiles/rirgf.png b/app/fronters/profiles/rirgf.png Binary files differindex 152c8e2..36a45d6 100644 --- a/app/fronters/profiles/rirgf.png +++ b/app/fronters/profiles/rirgf.png diff --git a/app/fronters/profiles/rpjok.png b/app/fronters/profiles/rpjok.png Binary files differindex c38ba39..950a4fa 100644 --- a/app/fronters/profiles/rpjok.png +++ b/app/fronters/profiles/rpjok.png diff --git a/app/fronters/profiles/sbxze.png b/app/fronters/profiles/sbxze.png Binary files differindex 2744fff..6d7b24a 100644 --- a/app/fronters/profiles/sbxze.png +++ b/app/fronters/profiles/sbxze.png diff --git a/app/fronters/profiles/sehke.png b/app/fronters/profiles/sehke.png Binary files differindex 0e6f956..0d2c424 100644 --- a/app/fronters/profiles/sehke.png +++ b/app/fronters/profiles/sehke.png diff --git a/app/fronters/profiles/sjuao.png b/app/fronters/profiles/sjuao.png Binary files differindex 12f5b33..b19ce07 100644 --- a/app/fronters/profiles/sjuao.png +++ b/app/fronters/profiles/sjuao.png diff --git a/app/fronters/profiles/tfbob.png b/app/fronters/profiles/tfbob.png Binary files differindex 4cc390a..ea78bea 100644 --- a/app/fronters/profiles/tfbob.png +++ b/app/fronters/profiles/tfbob.png diff --git a/app/fronters/profiles/ufadt.png b/app/fronters/profiles/ufadt.png Binary files differindex 4af6c47..b0a09a8 100644 --- a/app/fronters/profiles/ufadt.png +++ b/app/fronters/profiles/ufadt.png diff --git a/app/fronters/profiles/umbyl.png b/app/fronters/profiles/umbyl.png Binary files differnew file mode 100644 index 0000000..119321c --- /dev/null +++ b/app/fronters/profiles/umbyl.png diff --git a/app/fronters/profiles/vncoa.png b/app/fronters/profiles/vncoa.png Binary files differindex 9636278..bdf4e80 100644 --- a/app/fronters/profiles/vncoa.png +++ b/app/fronters/profiles/vncoa.png diff --git a/app/fronters/profiles/vvsxf.png b/app/fronters/profiles/vvsxf.png Binary files differindex 74f542e..95a1b4b 100644 --- a/app/fronters/profiles/vvsxf.png +++ b/app/fronters/profiles/vvsxf.png diff --git a/app/fronters/profiles/xbvwt.png b/app/fronters/profiles/xbvwt.png Binary files differindex 4b6c8d3..f8fb01a 100644 --- a/app/fronters/profiles/xbvwt.png +++ b/app/fronters/profiles/xbvwt.png diff --git a/app/fronters/profiles/xcjhj.png b/app/fronters/profiles/xcjhj.png Binary files differindex 4ab828d..ada7641 100644 --- a/app/fronters/profiles/xcjhj.png +++ b/app/fronters/profiles/xcjhj.png diff --git a/app/fronters/profiles/yhbrc.png b/app/fronters/profiles/yhbrc.png Binary files differindex 8b07487..e7949d1 100644 --- a/app/fronters/profiles/yhbrc.png +++ b/app/fronters/profiles/yhbrc.png diff --git a/app/fronters/profiles/zajrk.png b/app/fronters/profiles/zajrk.png Binary files differindex e6cfa68..6ab4b18 100644 --- a/app/fronters/profiles/zajrk.png +++ b/app/fronters/profiles/zajrk.png diff --git a/app/fronters/profiles/zdtsg.png b/app/fronters/profiles/zdtsg.png Binary files differindex 6ec0b3c..2e62832 100644 --- a/app/fronters/profiles/zdtsg.png +++ b/app/fronters/profiles/zdtsg.png diff --git a/app/fronters/profiles/zhtzs.png b/app/fronters/profiles/zhtzs.png Binary files differindex b65fd37..a5aee05 100644 --- a/app/fronters/profiles/zhtzs.png +++ b/app/fronters/profiles/zhtzs.png diff --git a/app/fronters/profiles/ztfjz.png b/app/fronters/profiles/ztfjz.png Binary files differindex 7765304..0308b63 100644 --- a/app/fronters/profiles/ztfjz.png +++ b/app/fronters/profiles/ztfjz.png diff --git a/app/fronters/profiles/zzise.png b/app/fronters/profiles/zzise.png Binary files differindex 76b1996..46ad36e 100644 --- a/app/fronters/profiles/zzise.png +++ b/app/fronters/profiles/zzise.png @@ -127,6 +127,7 @@ let filesToCache = [ "/app/fronters/profiles/hpwyq.png", "/app/fronters/profiles/irxyh.png", "/app/fronters/profiles/jhemc.png", + "/app/fronters/profiles/jjzcb.png", "/app/fronters/profiles/jlpjf.png", "/app/fronters/profiles/jmasq.png", "/app/fronters/profiles/jnbae.png", @@ -137,6 +138,7 @@ let filesToCache = [ "/app/fronters/profiles/kzfjn.png", "/app/fronters/profiles/lllfw.png", "/app/fronters/profiles/lqahp.png", + "/app/fronters/profiles/lqolg.png", "/app/fronters/profiles/lyotd.png", "/app/fronters/profiles/lzlaq.png", "/app/fronters/profiles/mglyq.png", @@ -179,6 +181,7 @@ let filesToCache = [ "/app/fronters/profiles/ughya.png", "/app/fronters/profiles/uhfic.png", "/app/fronters/profiles/uicxr.png", + "/app/fronters/profiles/umbyl.png", "/app/fronters/profiles/vahcl.png", "/app/fronters/profiles/vaxyy.png", "/app/fronters/profiles/vncoa.png", @@ -230,8 +233,10 @@ let filesToCache = [ "/assets/uploads/pt-applebloom.png", "/assets/uploads/pt-babsseed.png", "/assets/uploads/pt-blueberrycloud.png", + "/assets/uploads/pt-cinnamonfire.png", "/assets/uploads/pt-cloudydreams.png", "/assets/uploads/pt-coral.png", + "/assets/uploads/pt-cozyglow.png", "/assets/uploads/pt-dahlia.png", "/assets/uploads/pt-duskrainbow.png", "/assets/uploads/pt-floralorchid.png", diff --git a/assets/logo/custom.css b/assets/logo/custom.css index 76ad1d5..11f29f9 100644 --- a/assets/logo/custom.css +++ b/assets/logo/custom.css @@ -119,15 +119,15 @@ body { } .dropdown-menu { - background-color: #222; + background-color: #222 !important; } .dropdown-item:hover { - background-color: rgba(255, 255, 255, .1); + background-color: rgba(255, 255, 255, .1) !important; } .dropdown-item:active, .dropdown-item:focus { - background-color: rgba(255, 255, 255, .2); + background-color: rgba(255, 255, 255, .2) !important; } .dropdown-item { @@ -135,16 +135,16 @@ body { } .dropdown-icon { - filter: invert(1); + filter: invert(1) !important; } .dropdown-toggle .dropdown-icon { - opacity: .5; - transition: 200ms opacity; + opacity: .5; !important; + transition: 200ms opacity !important; } .dropdown-toggle:hover .dropdown-icon, .dropdown-toggle:active .dropdown-icon, .dropdown-toggle:focus .dropdown-icon { - opacity: .75; + opacity: .75 !important; } dd { @@ -596,7 +596,7 @@ peh-muted { } .dropdown-toggle::after { - margin-bottom: -3px; + margin-bottom: -3px !important; } .navbar-nav { @@ -621,10 +621,27 @@ peh-muted { border-bottom-left-radius: 10px; border-bottom-right-radius: 10px; display: grid; - grid-template-columns: repeat(6, 1fr); + grid-template-columns: repeat(5, 1fr); text-align: center; } +#member-details.member-details-loggedIn { + border-radius: 0; + padding-bottom: 0 !important; +} + +#member-details-2 { + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; + padding: 10px 20px; + text-align: center; + display: grid; + grid-template-columns: repeat(3, 1fr); + background: rgba(255, 255, 255, .1); + border: 1px solid transparent; + border-top: none; +} + .navbar-collapse.show { z-index: 99999; } @@ -646,6 +663,27 @@ peh-muted { grid-template-columns: repeat(2, 1fr) !important; text-align: left; } + + #member-details-2 { + grid-template-columns: repeat(2, 1fr) !important; + text-align: left; + } + + .member-detail-desktop { + display: none !important; + } + + .member-detail-mobile { + display: initial !important; + } +} + +.member-detail-desktop { + display: initial; +} + +.member-detail-mobile { + display: none; } .linked-card { diff --git a/assets/uploads/pt-cinnamonfire.png b/assets/uploads/pt-cinnamonfire.png Binary files differnew file mode 100644 index 0000000..c23c1ad --- /dev/null +++ b/assets/uploads/pt-cinnamonfire.png diff --git a/assets/uploads/pt-cozyglow.png b/assets/uploads/pt-cozyglow.png Binary files differnew file mode 100644 index 0000000..4f65ee9 --- /dev/null +++ b/assets/uploads/pt-cozyglow.png diff --git a/assets/uploads/pt-creamychocolate.png b/assets/uploads/pt-creamychocolate.png Binary files differnew file mode 100644 index 0000000..1cb2699 --- /dev/null +++ b/assets/uploads/pt-creamychocolate.png diff --git a/includes/Parsedown.php b/includes/Parsedown.php new file mode 100644 index 0000000..3e29589 --- /dev/null +++ b/includes/Parsedown.php @@ -0,0 +1,1994 @@ +<?php + +# +# +# Parsedown +# http://parsedown.org +# +# (c) Emanuil Rusev +# http://erusev.com +# +# For the full license information, view the LICENSE file that was distributed +# with this source code. +# +# + +class Parsedown +{ + # ~ + + const version = '1.8.0-beta-7'; + + # ~ + + function text($text) + { + $Elements = $this->textElements($text); + + # convert to markup + $markup = $this->elements($Elements); + + # trim line breaks + $markup = trim($markup, "\n"); + + return $markup; + } + + protected function textElements($text) + { + # make sure no definitions are set + $this->DefinitionData = array(); + + # standardize line breaks + $text = str_replace(array("\r\n", "\r"), "\n", $text); + + # remove surrounding line breaks + $text = trim($text, "\n"); + + # split text into lines + $lines = explode("\n", $text); + + # iterate through lines to identify blocks + return $this->linesElements($lines); + } + + # + # Setters + # + + function setBreaksEnabled($breaksEnabled) + { + $this->breaksEnabled = $breaksEnabled; + + return $this; + } + + protected $breaksEnabled; + + function setMarkupEscaped($markupEscaped) + { + $this->markupEscaped = $markupEscaped; + + return $this; + } + + protected $markupEscaped; + + function setUrlsLinked($urlsLinked) + { + $this->urlsLinked = $urlsLinked; + + return $this; + } + + protected $urlsLinked = true; + + function setSafeMode($safeMode) + { + $this->safeMode = (bool) $safeMode; + + return $this; + } + + protected $safeMode; + + function setStrictMode($strictMode) + { + $this->strictMode = (bool) $strictMode; + + return $this; + } + + protected $strictMode; + + protected $safeLinksWhitelist = array( + 'http://', + 'https://', + 'ftp://', + 'ftps://', + 'mailto:', + 'tel:', + 'data:image/png;base64,', + 'data:image/gif;base64,', + 'data:image/jpeg;base64,', + 'irc:', + 'ircs:', + 'git:', + 'ssh:', + 'news:', + 'steam:', + ); + + # + # Lines + # + + protected $BlockTypes = array( + '#' => array('Header'), + '*' => array('Rule', 'List'), + '+' => array('List'), + '-' => array('SetextHeader', 'Table', 'Rule', 'List'), + '0' => array('List'), + '1' => array('List'), + '2' => array('List'), + '3' => array('List'), + '4' => array('List'), + '5' => array('List'), + '6' => array('List'), + '7' => array('List'), + '8' => array('List'), + '9' => array('List'), + ':' => array('Table'), + '<' => array('Comment', 'Markup'), + '=' => array('SetextHeader'), + '>' => array('Quote'), + '[' => array('Reference'), + '_' => array('Rule'), + '`' => array('FencedCode'), + '|' => array('Table'), + '~' => array('FencedCode'), + ); + + # ~ + + protected $unmarkedBlockTypes = array( + 'Code', + ); + + # + # Blocks + # + + protected function lines(array $lines) + { + return $this->elements($this->linesElements($lines)); + } + + protected function linesElements(array $lines) + { + $Elements = array(); + $CurrentBlock = null; + + foreach ($lines as $line) + { + if (chop($line) === '') + { + if (isset($CurrentBlock)) + { + $CurrentBlock['interrupted'] = (isset($CurrentBlock['interrupted']) + ? $CurrentBlock['interrupted'] + 1 : 1 + ); + } + + continue; + } + + while (($beforeTab = strstr($line, "\t", true)) !== false) + { + $shortage = 4 - mb_strlen($beforeTab, 'utf-8') % 4; + + $line = $beforeTab + . str_repeat(' ', $shortage) + . substr($line, strlen($beforeTab) + 1) + ; + } + + $indent = strspn($line, ' '); + + $text = $indent > 0 ? substr($line, $indent) : $line; + + # ~ + + $Line = array('body' => $line, 'indent' => $indent, 'text' => $text); + + # ~ + + if (isset($CurrentBlock['continuable'])) + { + $methodName = 'block' . $CurrentBlock['type'] . 'Continue'; + $Block = $this->$methodName($Line, $CurrentBlock); + + if (isset($Block)) + { + $CurrentBlock = $Block; + + continue; + } + else + { + if ($this->isBlockCompletable($CurrentBlock['type'])) + { + $methodName = 'block' . $CurrentBlock['type'] . 'Complete'; + $CurrentBlock = $this->$methodName($CurrentBlock); + } + } + } + + # ~ + + $marker = $text[0]; + + # ~ + + $blockTypes = $this->unmarkedBlockTypes; + + if (isset($this->BlockTypes[$marker])) + { + foreach ($this->BlockTypes[$marker] as $blockType) + { + $blockTypes []= $blockType; + } + } + + # + # ~ + + foreach ($blockTypes as $blockType) + { + $Block = $this->{"block$blockType"}($Line, $CurrentBlock); + + if (isset($Block)) + { + $Block['type'] = $blockType; + + if ( ! isset($Block['identified'])) + { + if (isset($CurrentBlock)) + { + $Elements[] = $this->extractElement($CurrentBlock); + } + + $Block['identified'] = true; + } + + if ($this->isBlockContinuable($blockType)) + { + $Block['continuable'] = true; + } + + $CurrentBlock = $Block; + + continue 2; + } + } + + # ~ + + if (isset($CurrentBlock) and $CurrentBlock['type'] === 'Paragraph') + { + $Block = $this->paragraphContinue($Line, $CurrentBlock); + } + + if (isset($Block)) + { + $CurrentBlock = $Block; + } + else + { + if (isset($CurrentBlock)) + { + $Elements[] = $this->extractElement($CurrentBlock); + } + + $CurrentBlock = $this->paragraph($Line); + + $CurrentBlock['identified'] = true; + } + } + + # ~ + + if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type'])) + { + $methodName = 'block' . $CurrentBlock['type'] . 'Complete'; + $CurrentBlock = $this->$methodName($CurrentBlock); + } + + # ~ + + if (isset($CurrentBlock)) + { + $Elements[] = $this->extractElement($CurrentBlock); + } + + # ~ + + return $Elements; + } + + protected function extractElement(array $Component) + { + if ( ! isset($Component['element'])) + { + if (isset($Component['markup'])) + { + $Component['element'] = array('rawHtml' => $Component['markup']); + } + elseif (isset($Component['hidden'])) + { + $Component['element'] = array(); + } + } + + return $Component['element']; + } + + protected function isBlockContinuable($Type) + { + return method_exists($this, 'block' . $Type . 'Continue'); + } + + protected function isBlockCompletable($Type) + { + return method_exists($this, 'block' . $Type . 'Complete'); + } + + # + # Code + + protected function blockCode($Line, $Block = null) + { + if (isset($Block) and $Block['type'] === 'Paragraph' and ! isset($Block['interrupted'])) + { + return; + } + + if ($Line['indent'] >= 4) + { + $text = substr($Line['body'], 4); + + $Block = array( + 'element' => array( + 'name' => 'pre', + 'element' => array( + 'name' => 'code', + 'text' => $text, + ), + ), + ); + + return $Block; + } + } + + protected function blockCodeContinue($Line, $Block) + { + if ($Line['indent'] >= 4) + { + if (isset($Block['interrupted'])) + { + $Block['element']['element']['text'] .= str_repeat("\n", $Block['interrupted']); + + unset($Block['interrupted']); + } + + $Block['element']['element']['text'] .= "\n"; + + $text = substr($Line['body'], 4); + + $Block['element']['element']['text'] .= $text; + + return $Block; + } + } + + protected function blockCodeComplete($Block) + { + return $Block; + } + + # + # Comment + + protected function blockComment($Line) + { + if ($this->markupEscaped or $this->safeMode) + { + return; + } + + if (strpos($Line['text'], '<!--') === 0) + { + $Block = array( + 'element' => array( + 'rawHtml' => $Line['body'], + 'autobreak' => true, + ), + ); + + if (strpos($Line['text'], '-->') !== false) + { + $Block['closed'] = true; + } + + return $Block; + } + } + + protected function blockCommentContinue($Line, array $Block) + { + if (isset($Block['closed'])) + { + return; + } + + $Block['element']['rawHtml'] .= "\n" . $Line['body']; + + if (strpos($Line['text'], '-->') !== false) + { + $Block['closed'] = true; + } + + return $Block; + } + + # + # Fenced Code + + protected function blockFencedCode($Line) + { + $marker = $Line['text'][0]; + + $openerLength = strspn($Line['text'], $marker); + + if ($openerLength < 3) + { + return; + } + + $infostring = trim(substr($Line['text'], $openerLength), "\t "); + + if (strpos($infostring, '`') !== false) + { + return; + } + + $Element = array( + 'name' => 'code', + 'text' => '', + ); + + if ($infostring !== '') + { + /** + * https://www.w3.org/TR/2011/WD-html5-20110525/elements.html#classes + * Every HTML element may have a class attribute specified. + * The attribute, if specified, must have a value that is a set + * of space-separated tokens representing the various classes + * that the element belongs to. + * [...] + * The space characters, for the purposes of this specification, + * are U+0020 SPACE, U+0009 CHARACTER TABULATION (tab), + * U+000A LINE FEED (LF), U+000C FORM FEED (FF), and + * U+000D CARRIAGE RETURN (CR). + */ + $language = substr($infostring, 0, strcspn($infostring, " \t\n\f\r")); + + $Element['attributes'] = array('class' => "language-$language"); + } + + $Block = array( + 'char' => $marker, + 'openerLength' => $openerLength, + 'element' => array( + 'name' => 'pre', + 'element' => $Element, + ), + ); + + return $Block; + } + + protected function blockFencedCodeContinue($Line, $Block) + { + if (isset($Block['complete'])) + { + return; + } + + if (isset($Block['interrupted'])) + { + $Block['element']['element']['text'] .= str_repeat("\n", $Block['interrupted']); + + unset($Block['interrupted']); + } + + if (($len = strspn($Line['text'], $Block['char'])) >= $Block['openerLength'] + and chop(substr($Line['text'], $len), ' ') === '' + ) { + $Block['element']['element']['text'] = substr($Block['element']['element']['text'], 1); + + $Block['complete'] = true; + + return $Block; + } + + $Block['element']['element']['text'] .= "\n" . $Line['body']; + + return $Block; + } + + protected function blockFencedCodeComplete($Block) + { + return $Block; + } + + # + # Header + + protected function blockHeader($Line) + { + $level = strspn($Line['text'], '#'); + + if ($level > 6) + { + return; + } + + $text = trim($Line['text'], '#'); + + if ($this->strictMode and isset($text[0]) and $text[0] !== ' ') + { + return; + } + + $text = trim($text, ' '); + + $Block = array( + 'element' => array( + 'name' => 'h' . $level, + 'handler' => array( + 'function' => 'lineElements', + 'argument' => $text, + 'destination' => 'elements', + ) + ), + ); + + return $Block; + } + + # + # List + + protected function blockList($Line, array $CurrentBlock = null) + { + list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]{1,9}+[.\)]'); + + if (preg_match('/^('.$pattern.'([ ]++|$))(.*+)/', $Line['text'], $matches)) + { + $contentIndent = strlen($matches[2]); + + if ($contentIndent >= 5) + { + $contentIndent -= 1; + $matches[1] = substr($matches[1], 0, -$contentIndent); + $matches[3] = str_repeat(' ', $contentIndent) . $matches[3]; + } + elseif ($contentIndent === 0) + { + $matches[1] .= ' '; + } + + $markerWithoutWhitespace = strstr($matches[1], ' ', true); + + $Block = array( + 'indent' => $Line['indent'], + 'pattern' => $pattern, + 'data' => array( + 'type' => $name, + 'marker' => $matches[1], + 'markerType' => ($name === 'ul' ? $markerWithoutWhitespace : substr($markerWithoutWhitespace, -1)), + ), + 'element' => array( + 'name' => $name, + 'elements' => array(), + ), + ); + $Block['data']['markerTypeRegex'] = preg_quote($Block['data']['markerType'], '/'); + + if ($name === 'ol') + { + $listStart = ltrim(strstr($matches[1], $Block['data']['markerType'], true), '0') ?: '0'; + + if ($listStart !== '1') + { + if ( + isset($CurrentBlock) + and $CurrentBlock['type'] === 'Paragraph' + and ! isset($CurrentBlock['interrupted']) + ) { + return; + } + + $Block['element']['attributes'] = array('start' => $listStart); + } + } + + $Block['li'] = array( + 'name' => 'li', + 'handler' => array( + 'function' => 'li', + 'argument' => !empty($matches[3]) ? array($matches[3]) : array(), + 'destination' => 'elements' + ) + ); + + $Block['element']['elements'] []= & $Block['li']; + + return $Block; + } + } + + protected function blockListContinue($Line, array $Block) + { + if (isset($Block['interrupted']) and empty($Block['li']['handler']['argument'])) + { + return null; + } + + $requiredIndent = ($Block['indent'] + strlen($Block['data']['marker'])); + + if ($Line['indent'] < $requiredIndent + and ( + ( + $Block['data']['type'] === 'ol' + and preg_match('/^[0-9]++'.$Block['data']['markerTypeRegex'].'(?:[ ]++(.*)|$)/', $Line['text'], $matches) + ) or ( + $Block['data']['type'] === 'ul' + and preg_match('/^'.$Block['data']['markerTypeRegex'].'(?:[ ]++(.*)|$)/', $Line['text'], $matches) + ) + ) + ) { + if (isset($Block['interrupted'])) + { + $Block['li']['handler']['argument'] []= ''; + + $Block['loose'] = true; + + unset($Block['interrupted']); + } + + unset($Block['li']); + + $text = isset($matches[1]) ? $matches[1] : ''; + + $Block['indent'] = $Line['indent']; + + $Block['li'] = array( + 'name' => 'li', + 'handler' => array( + 'function' => 'li', + 'argument' => array($text), + 'destination' => 'elements' + ) + ); + + $Block['element']['elements'] []= & $Block['li']; + + return $Block; + } + elseif ($Line['indent'] < $requiredIndent and $this->blockList($Line)) + { + return null; + } + + if ($Line['text'][0] === '[' and $this->blockReference($Line)) + { + return $Block; + } + + if ($Line['indent'] >= $requiredIndent) + { + if (isset($Block['interrupted'])) + { + $Block['li']['handler']['argument'] []= ''; + + $Block['loose'] = true; + + unset($Block['interrupted']); + } + + $text = substr($Line['body'], $requiredIndent); + + $Block['li']['handler']['argument'] []= $text; + + return $Block; + } + + if ( ! isset($Block['interrupted'])) + { + $text = preg_replace('/^[ ]{0,'.$requiredIndent.'}+/', '', $Line['body']); + + $Block['li']['handler']['argument'] []= $text; + + return $Block; + } + } + + protected function blockListComplete(array $Block) + { + if (isset($Block['loose'])) + { + foreach ($Block['element']['elements'] as &$li) + { + if (end($li['handler']['argument']) !== '') + { + $li['handler']['argument'] []= ''; + } + } + } + + return $Block; + } + + # + # Quote + + protected function blockQuote($Line) + { + if (preg_match('/^>[ ]?+(.*+)/', $Line['text'], $matches)) + { + $Block = array( + 'element' => array( + 'name' => 'blockquote', + 'handler' => array( + 'function' => 'linesElements', + 'argument' => (array) $matches[1], + 'destination' => 'elements', + ) + ), + ); + + return $Block; + } + } + + protected function blockQuoteContinue($Line, array $Block) + { + if (isset($Block['interrupted'])) + { + return; + } + + if ($Line['text'][0] === '>' and preg_match('/^>[ ]?+(.*+)/', $Line['text'], $matches)) + { + $Block['element']['handler']['argument'] []= $matches[1]; + + return $Block; + } + + if ( ! isset($Block['interrupted'])) + { + $Block['element']['handler']['argument'] []= $Line['text']; + + return $Block; + } + } + + # + # Rule + + protected function blockRule($Line) + { + $marker = $Line['text'][0]; + + if (substr_count($Line['text'], $marker) >= 3 and chop($Line['text'], " $marker") === '') + { + $Block = array( + 'element' => array( + 'name' => 'hr', + ), + ); + + return $Block; + } + } + + # + # Setext + + protected function blockSetextHeader($Line, array $Block = null) + { + if ( ! isset($Block) or $Block['type'] !== 'Paragraph' or isset($Block['interrupted'])) + { + return; + } + + if ($Line['indent'] < 4 and chop(chop($Line['text'], ' '), $Line['text'][0]) === '') + { + $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2'; + + return $Block; + } + } + + # + # Markup + + protected function blockMarkup($Line) + { + if ($this->markupEscaped or $this->safeMode) + { + return; + } + + if (preg_match('/^<[\/]?+(\w*)(?:[ ]*+'.$this->regexHtmlAttribute.')*+[ ]*+(\/)?>/', $Line['text'], $matches)) + { + $element = strtolower($matches[1]); + + if (in_array($element, $this->textLevelElements)) + { + return; + } + + $Block = array( + 'name' => $matches[1], + 'element' => array( + 'rawHtml' => $Line['text'], + 'autobreak' => true, + ), + ); + + return $Block; + } + } + + protected function blockMarkupContinue($Line, array $Block) + { + if (isset($Block['closed']) or isset($Block['interrupted'])) + { + return; + } + + $Block['element']['rawHtml'] .= "\n" . $Line['body']; + + return $Block; + } + + # + # Reference + + protected function blockReference($Line) + { + if (strpos($Line['text'], ']') !== false + and preg_match('/^\[(.+?)\]:[ ]*+<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*+$/', $Line['text'], $matches) + ) { + $id = strtolower($matches[1]); + + $Data = array( + 'url' => $matches[2], + 'title' => isset($matches[3]) ? $matches[3] : null, + ); + + $this->DefinitionData['Reference'][$id] = $Data; + + $Block = array( + 'element' => array(), + ); + + return $Block; + } + } + + # + # Table + + protected function blockTable($Line, array $Block = null) + { + if ( ! isset($Block) or $Block['type'] !== 'Paragraph' or isset($Block['interrupted'])) + { + return; + } + + if ( + strpos($Block['element']['handler']['argument'], '|') === false + and strpos($Line['text'], '|') === false + and strpos($Line['text'], ':') === false + or strpos($Block['element']['handler']['argument'], "\n") !== false + ) { + return; + } + + if (chop($Line['text'], ' -:|') !== '') + { + return; + } + + $alignments = array(); + + $divider = $Line['text']; + + $divider = trim($divider); + $divider = trim($divider, '|'); + + $dividerCells = explode('|', $divider); + + foreach ($dividerCells as $dividerCell) + { + $dividerCell = trim($dividerCell); + + if ($dividerCell === '') + { + return; + } + + $alignment = null; + + if ($dividerCell[0] === ':') + { + $alignment = 'left'; + } + + if (substr($dividerCell, - 1) === ':') + { + $alignment = $alignment === 'left' ? 'center' : 'right'; + } + + $alignments []= $alignment; + } + + # ~ + + $HeaderElements = array(); + + $header = $Block['element']['handler']['argument']; + + $header = trim($header); + $header = trim($header, '|'); + + $headerCells = explode('|', $header); + + if (count($headerCells) !== count($alignments)) + { + return; + } + + foreach ($headerCells as $index => $headerCell) + { + $headerCell = trim($headerCell); + + $HeaderElement = array( + 'name' => 'th', + 'handler' => array( + 'function' => 'lineElements', + 'argument' => $headerCell, + 'destination' => 'elements', + ) + ); + + if (isset($alignments[$index])) + { + $alignment = $alignments[$index]; + + $HeaderElement['attributes'] = array( + 'style' => "text-align: $alignment;", + ); + } + + $HeaderElements []= $HeaderElement; + } + + # ~ + + $Block = array( + 'alignments' => $alignments, + 'identified' => true, + 'element' => array( + 'name' => 'table', + 'elements' => array(), + ), + ); + + $Block['element']['elements'] []= array( + 'name' => 'thead', + ); + + $Block['element']['elements'] []= array( + 'name' => 'tbody', + 'elements' => array(), + ); + + $Block['element']['elements'][0]['elements'] []= array( + 'name' => 'tr', + 'elements' => $HeaderElements, + ); + + return $Block; + } + + protected function blockTableContinue($Line, array $Block) + { + if (isset($Block['interrupted'])) + { + return; + } + + if (count($Block['alignments']) === 1 or $Line['text'][0] === '|' or strpos($Line['text'], '|')) + { + $Elements = array(); + + $row = $Line['text']; + + $row = trim($row); + $row = trim($row, '|'); + + preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]++`|`)++/', $row, $matches); + + $cells = array_slice($matches[0], 0, count($Block['alignments'])); + + foreach ($cells as $index => $cell) + { + $cell = trim($cell); + + $Element = array( + 'name' => 'td', + 'handler' => array( + 'function' => 'lineElements', + 'argument' => $cell, + 'destination' => 'elements', + ) + ); + + if (isset($Block['alignments'][$index])) + { + $Element['attributes'] = array( + 'style' => 'text-align: ' . $Block['alignments'][$index] . ';', + ); + } + + $Elements []= $Element; + } + + $Element = array( + 'name' => 'tr', + 'elements' => $Elements, + ); + + $Block['element']['elements'][1]['elements'] []= $Element; + + return $Block; + } + } + + # + # ~ + # + + protected function paragraph($Line) + { + return array( + 'type' => 'Paragraph', + 'element' => array( + 'name' => 'p', + 'handler' => array( + 'function' => 'lineElements', + 'argument' => $Line['text'], + 'destination' => 'elements', + ), + ), + ); + } + + protected function paragraphContinue($Line, array $Block) + { + if (isset($Block['interrupted'])) + { + return; + } + + $Block['element']['handler']['argument'] .= "\n".$Line['text']; + + return $Block; + } + + # + # Inline Elements + # + + protected $InlineTypes = array( + '!' => array('Image'), + '&' => array('SpecialCharacter'), + '*' => array('Emphasis'), + ':' => array('Url'), + '<' => array('UrlTag', 'EmailTag', 'Markup'), + '[' => array('Link'), + '_' => array('Emphasis'), + '`' => array('Code'), + '~' => array('Strikethrough'), + '\\' => array('EscapeSequence'), + ); + + # ~ + + protected $inlineMarkerList = '!*_&[:<`~\\'; + + # + # ~ + # + + public function line($text, $nonNestables = array()) + { + return $this->elements($this->lineElements($text, $nonNestables)); + } + + protected function lineElements($text, $nonNestables = array()) + { + # standardize line breaks + $text = str_replace(array("\r\n", "\r"), "\n", $text); + + $Elements = array(); + + $nonNestables = (empty($nonNestables) + ? array() + : array_combine($nonNestables, $nonNestables) + ); + + # $excerpt is based on the first occurrence of a marker + + while ($excerpt = strpbrk($text, $this->inlineMarkerList)) + { + $marker = $excerpt[0]; + + $markerPosition = strlen($text) - strlen($excerpt); + + $Excerpt = array('text' => $excerpt, 'context' => $text); + + foreach ($this->InlineTypes[$marker] as $inlineType) + { + # check to see if the current inline type is nestable in the current context + + if (isset($nonNestables[$inlineType])) + { + continue; + } + + $Inline = $this->{"inline$inlineType"}($Excerpt); + + if ( ! isset($Inline)) + { + continue; + } + + # makes sure that the inline belongs to "our" marker + + if (isset($Inline['position']) and $Inline['position'] > $markerPosition) + { + continue; + } + + # sets a default inline position + + if ( ! isset($Inline['position'])) + { + $Inline['position'] = $markerPosition; + } + + # cause the new element to 'inherit' our non nestables + + + $Inline['element']['nonNestables'] = isset($Inline['element']['nonNestables']) + ? array_merge($Inline['element']['nonNestables'], $nonNestables) + : $nonNestables + ; + + # the text that comes before the inline + $unmarkedText = substr($text, 0, $Inline['position']); + + # compile the unmarked text + $InlineText = $this->inlineText($unmarkedText); + $Elements[] = $InlineText['element']; + + # compile the inline + $Elements[] = $this->extractElement($Inline); + + # remove the examined text + $text = substr($text, $Inline['position'] + $Inline['extent']); + + continue 2; + } + + # the marker does not belong to an inline + + $unmarkedText = substr($text, 0, $markerPosition + 1); + + $InlineText = $this->inlineText($unmarkedText); + $Elements[] = $InlineText['element']; + + $text = substr($text, $markerPosition + 1); + } + + $InlineText = $this->inlineText($text); + $Elements[] = $InlineText['element']; + + foreach ($Elements as &$Element) + { + if ( ! isset($Element['autobreak'])) + { + $Element['autobreak'] = false; + } + } + + return $Elements; + } + + # + # ~ + # + + protected function inlineText($text) + { + $Inline = array( + 'extent' => strlen($text), + 'element' => array(), + ); + + $Inline['element']['elements'] = self::pregReplaceElements( + $this->breaksEnabled ? '/[ ]*+\n/' : '/(?:[ ]*+\\\\|[ ]{2,}+)\n/', + array( + array('name' => 'br'), + array('text' => "\n"), + ), + $text + ); + + return $Inline; + } + + protected function inlineCode($Excerpt) + { + $marker = $Excerpt['text'][0]; + + if (preg_match('/^(['.$marker.']++)[ ]*+(.+?)[ ]*+(?<!['.$marker.'])\1(?!'.$marker.')/s', $Excerpt['text'], $matches)) + { + $text = $matches[2]; + $text = preg_replace('/[ ]*+\n/', ' ', $text); + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'code', + 'text' => $text, + ), + ); + } + } + + protected function inlineEmailTag($Excerpt) + { + $hostnameLabel = '[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?'; + + $commonMarkEmail = '[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]++@' + . $hostnameLabel . '(?:\.' . $hostnameLabel . ')*'; + + if (strpos($Excerpt['text'], '>') !== false + and preg_match("/^<((mailto:)?$commonMarkEmail)>/i", $Excerpt['text'], $matches) + ){ + $url = $matches[1]; + + if ( ! isset($matches[2])) + { + $url = "mailto:$url"; + } + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'a', + 'text' => $matches[1], + 'attributes' => array( + 'href' => $url, + ), + ), + ); + } + } + + protected function inlineEmphasis($Excerpt) + { + if ( ! isset($Excerpt['text'][1])) + { + return; + } + + $marker = $Excerpt['text'][0]; + + if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches)) + { + $emphasis = 'strong'; + } + elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches)) + { + $emphasis = 'em'; + } + else + { + return; + } + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => $emphasis, + 'handler' => array( + 'function' => 'lineElements', + 'argument' => $matches[1], + 'destination' => 'elements', + ) + ), + ); + } + + protected function inlineEscapeSequence($Excerpt) + { + if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters)) + { + return array( + 'element' => array('rawHtml' => $Excerpt['text'][1]), + 'extent' => 2, + ); + } + } + + protected function inlineImage($Excerpt) + { + if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[') + { + return; + } + + $Excerpt['text']= substr($Excerpt['text'], 1); + + $Link = $this->inlineLink($Excerpt); + + if ($Link === null) + { + return; + } + + $Inline = array( + 'extent' => $Link['extent'] + 1, + 'element' => array( + 'name' => 'img', + 'attributes' => array( + 'src' => $Link['element']['attributes']['href'], + 'alt' => $Link['element']['handler']['argument'], + ), + 'autobreak' => true, + ), + ); + + $Inline['element']['attributes'] += $Link['element']['attributes']; + + unset($Inline['element']['attributes']['href']); + + return $Inline; + } + + protected function inlineLink($Excerpt) + { + $Element = array( + 'name' => 'a', + 'handler' => array( + 'function' => 'lineElements', + 'argument' => null, + 'destination' => 'elements', + ), + 'nonNestables' => array('Url', 'Link'), + 'attributes' => array( + 'href' => null, + 'title' => null, + ), + ); + + $extent = 0; + + $remainder = $Excerpt['text']; + + if (preg_match('/\[((?:[^][]++|(?R))*+)\]/', $remainder, $matches)) + { + $Element['handler']['argument'] = $matches[1]; + + $extent += strlen($matches[0]); + + $remainder = substr($remainder, $extent); + } + else + { + return; + } + + if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*+"|\'[^\']*+\'))?\s*+[)]/', $remainder, $matches)) + { + $Element['attributes']['href'] = $matches[1]; + + if (isset($matches[2])) + { + $Element['attributes']['title'] = substr($matches[2], 1, - 1); + } + + $extent += strlen($matches[0]); + } + else + { + if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches)) + { + $definition = strlen($matches[1]) ? $matches[1] : $Element['handler']['argument']; + $definition = strtolower($definition); + + $extent += strlen($matches[0]); + } + else + { + $definition = strtolower($Element['handler']['argument']); + } + + if ( ! isset($this->DefinitionData['Reference'][$definition])) + { + return; + } + + $Definition = $this->DefinitionData['Reference'][$definition]; + + $Element['attributes']['href'] = $Definition['url']; + $Element['attributes']['title'] = $Definition['title']; + } + + return array( + 'extent' => $extent, + 'element' => $Element, + ); + } + + protected function inlineMarkup($Excerpt) + { + if ($this->markupEscaped or $this->safeMode or strpos($Excerpt['text'], '>') === false) + { + return; + } + + if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w[\w-]*+[ ]*+>/s', $Excerpt['text'], $matches)) + { + return array( + 'element' => array('rawHtml' => $matches[0]), + 'extent' => strlen($matches[0]), + ); + } + + if ($Excerpt['text'][1] === '!' and preg_match('/^<!---?[^>-](?:-?+[^-])*-->/s', $Excerpt['text'], $matches)) + { + return array( + 'element' => array('rawHtml' => $matches[0]), + 'extent' => strlen($matches[0]), + ); + } + + if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w[\w-]*+(?:[ ]*+'.$this->regexHtmlAttribute.')*+[ ]*+\/?>/s', $Excerpt['text'], $matches)) + { + return array( + 'element' => array('rawHtml' => $matches[0]), + 'extent' => strlen($matches[0]), + ); + } + } + + protected function inlineSpecialCharacter($Excerpt) + { + if (substr($Excerpt['text'], 1, 1) !== ' ' and strpos($Excerpt['text'], ';') !== false + and preg_match('/^&(#?+[0-9a-zA-Z]++);/', $Excerpt['text'], $matches) + ) { + return array( + 'element' => array('rawHtml' => '&' . $matches[1] . ';'), + 'extent' => strlen($matches[0]), + ); + } + + return; + } + + protected function inlineStrikethrough($Excerpt) + { + if ( ! isset($Excerpt['text'][1])) + { + return; + } + + if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches)) + { + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'del', + 'handler' => array( + 'function' => 'lineElements', + 'argument' => $matches[1], + 'destination' => 'elements', + ) + ), + ); + } + } + + protected function inlineUrl($Excerpt) + { + if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/') + { + return; + } + + if (strpos($Excerpt['context'], 'http') !== false + and preg_match('/\bhttps?+:[\/]{2}[^\s<]+\b\/*+/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE) + ) { + $url = $matches[0][0]; + + $Inline = array( + 'extent' => strlen($matches[0][0]), + 'position' => $matches[0][1], + 'element' => array( + 'name' => 'a', + 'text' => $url, + 'attributes' => array( + 'href' => $url, + ), + ), + ); + + return $Inline; + } + } + + protected function inlineUrlTag($Excerpt) + { + if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w++:\/{2}[^ >]++)>/i', $Excerpt['text'], $matches)) + { + $url = $matches[1]; + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'a', + 'text' => $url, + 'attributes' => array( + 'href' => $url, + ), + ), + ); + } + } + + # ~ + + protected function unmarkedText($text) + { + $Inline = $this->inlineText($text); + return $this->element($Inline['element']); + } + + # + # Handlers + # + + protected function handle(array $Element) + { + if (isset($Element['handler'])) + { + if (!isset($Element['nonNestables'])) + { + $Element['nonNestables'] = array(); + } + + if (is_string($Element['handler'])) + { + $function = $Element['handler']; + $argument = $Element['text']; + unset($Element['text']); + $destination = 'rawHtml'; + } + else + { + $function = $Element['handler']['function']; + $argument = $Element['handler']['argument']; + $destination = $Element['handler']['destination']; + } + + $Element[$destination] = $this->{$function}($argument, $Element['nonNestables']); + + if ($destination === 'handler') + { + $Element = $this->handle($Element); + } + + unset($Element['handler']); + } + + return $Element; + } + + protected function handleElementRecursive(array $Element) + { + return $this->elementApplyRecursive(array($this, 'handle'), $Element); + } + + protected function handleElementsRecursive(array $Elements) + { + return $this->elementsApplyRecursive(array($this, 'handle'), $Elements); + } + + protected function elementApplyRecursive($closure, array $Element) + { + $Element = call_user_func($closure, $Element); + + if (isset($Element['elements'])) + { + $Element['elements'] = $this->elementsApplyRecursive($closure, $Element['elements']); + } + elseif (isset($Element['element'])) + { + $Element['element'] = $this->elementApplyRecursive($closure, $Element['element']); + } + + return $Element; + } + + protected function elementApplyRecursiveDepthFirst($closure, array $Element) + { + if (isset($Element['elements'])) + { + $Element['elements'] = $this->elementsApplyRecursiveDepthFirst($closure, $Element['elements']); + } + elseif (isset($Element['element'])) + { + $Element['element'] = $this->elementsApplyRecursiveDepthFirst($closure, $Element['element']); + } + + $Element = call_user_func($closure, $Element); + + return $Element; + } + + protected function elementsApplyRecursive($closure, array $Elements) + { + foreach ($Elements as &$Element) + { + $Element = $this->elementApplyRecursive($closure, $Element); + } + + return $Elements; + } + + protected function elementsApplyRecursiveDepthFirst($closure, array $Elements) + { + foreach ($Elements as &$Element) + { + $Element = $this->elementApplyRecursiveDepthFirst($closure, $Element); + } + + return $Elements; + } + + protected function element(array $Element) + { + if ($this->safeMode) + { + $Element = $this->sanitiseElement($Element); + } + + # identity map if element has no handler + $Element = $this->handle($Element); + + $hasName = isset($Element['name']); + + $markup = ''; + + if ($hasName) + { + $markup .= '<' . $Element['name']; + + if (isset($Element['attributes'])) + { + foreach ($Element['attributes'] as $name => $value) + { + if ($value === null) + { + continue; + } + + $markup .= " $name=\"".self::escape($value).'"'; + } + } + } + + $permitRawHtml = false; + + if (isset($Element['text'])) + { + $text = $Element['text']; + } + // very strongly consider an alternative if you're writing an + // extension + elseif (isset($Element['rawHtml'])) + { + $text = $Element['rawHtml']; + + $allowRawHtmlInSafeMode = isset($Element['allowRawHtmlInSafeMode']) && $Element['allowRawHtmlInSafeMode']; + $permitRawHtml = !$this->safeMode || $allowRawHtmlInSafeMode; + } + + $hasContent = isset($text) || isset($Element['element']) || isset($Element['elements']); + + if ($hasContent) + { + $markup .= $hasName ? '>' : ''; + + if (isset($Element['elements'])) + { + $markup .= $this->elements($Element['elements']); + } + elseif (isset($Element['element'])) + { + $markup .= $this->element($Element['element']); + } + else + { + if (!$permitRawHtml) + { + $markup .= self::escape($text, true); + } + else + { + $markup .= $text; + } + } + + $markup .= $hasName ? '</' . $Element['name'] . '>' : ''; + } + elseif ($hasName) + { + $markup .= ' />'; + } + + return $markup; + } + + protected function elements(array $Elements) + { + $markup = ''; + + $autoBreak = true; + + foreach ($Elements as $Element) + { + if (empty($Element)) + { + continue; + } + + $autoBreakNext = (isset($Element['autobreak']) + ? $Element['autobreak'] : isset($Element['name']) + ); + // (autobreak === false) covers both sides of an element + $autoBreak = !$autoBreak ? $autoBreak : $autoBreakNext; + + $markup .= ($autoBreak ? "\n" : '') . $this->element($Element); + $autoBreak = $autoBreakNext; + } + + $markup .= $autoBreak ? "\n" : ''; + + return $markup; + } + + # ~ + + protected function li($lines) + { + $Elements = $this->linesElements($lines); + + if ( ! in_array('', $lines) + and isset($Elements[0]) and isset($Elements[0]['name']) + and $Elements[0]['name'] === 'p' + ) { + unset($Elements[0]['name']); + } + + return $Elements; + } + + # + # AST Convenience + # + + /** + * Replace occurrences $regexp with $Elements in $text. Return an array of + * elements representing the replacement. + */ + protected static function pregReplaceElements($regexp, $Elements, $text) + { + $newElements = array(); + + while (preg_match($regexp, $text, $matches, PREG_OFFSET_CAPTURE)) + { + $offset = $matches[0][1]; + $before = substr($text, 0, $offset); + $after = substr($text, $offset + strlen($matches[0][0])); + + $newElements[] = array('text' => $before); + + foreach ($Elements as $Element) + { + $newElements[] = $Element; + } + + $text = $after; + } + + $newElements[] = array('text' => $text); + + return $newElements; + } + + # + # Deprecated Methods + # + + function parse($text) + { + $markup = $this->text($text); + + return $markup; + } + + protected function sanitiseElement(array $Element) + { + static $goodAttribute = '/^[a-zA-Z0-9][a-zA-Z0-9-_]*+$/'; + static $safeUrlNameToAtt = array( + 'a' => 'href', + 'img' => 'src', + ); + + if ( ! isset($Element['name'])) + { + unset($Element['attributes']); + return $Element; + } + + if (isset($safeUrlNameToAtt[$Element['name']])) + { + $Element = $this->filterUnsafeUrlInAttribute($Element, $safeUrlNameToAtt[$Element['name']]); + } + + if ( ! empty($Element['attributes'])) + { + foreach ($Element['attributes'] as $att => $val) + { + # filter out badly parsed attribute + if ( ! preg_match($goodAttribute, $att)) + { + unset($Element['attributes'][$att]); + } + # dump onevent attribute + elseif (self::striAtStart($att, 'on')) + { + unset($Element['attributes'][$att]); + } + } + } + + return $Element; + } + + protected function filterUnsafeUrlInAttribute(array $Element, $attribute) + { + foreach ($this->safeLinksWhitelist as $scheme) + { + if (self::striAtStart($Element['attributes'][$attribute], $scheme)) + { + return $Element; + } + } + + $Element['attributes'][$attribute] = str_replace(':', '%3A', $Element['attributes'][$attribute]); + + return $Element; + } + + # + # Static Methods + # + + protected static function escape($text, $allowQuotes = false) + { + return htmlspecialchars($text, $allowQuotes ? ENT_NOQUOTES : ENT_QUOTES, 'UTF-8'); + } + + protected static function striAtStart($string, $needle) + { + $len = strlen($needle); + + if ($len > strlen($string)) + { + return false; + } + else + { + return strtolower(substr($string, 0, $len)) === strtolower($needle); + } + } + + static function instance($name = 'default') + { + if (isset(self::$instances[$name])) + { + return self::$instances[$name]; + } + + $instance = new static(); + + self::$instances[$name] = $instance; + + return $instance; + } + + private static $instances = array(); + + # + # Fields + # + + protected $DefinitionData; + + # + # Read-Only + + protected $specialCharacters = array( + '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|', '~' + ); + + protected $StrongRegex = array( + '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*+[*])+?)[*]{2}(?![*])/s', + '_' => '/^__((?:\\\\_|[^_]|_[^_]*+_)+?)__(?!_)/us', + ); + + protected $EmRegex = array( + '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s', + '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us', + ); + + protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*+(?:\s*+=\s*+(?:[^"\'=<>`\s]+|"[^"]*+"|\'[^\']*+\'))?+'; + + protected $voidElements = array( + 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', + ); + + protected $textLevelElements = array( + 'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont', + 'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing', + 'i', 'rp', 'del', 'code', 'strike', 'marquee', + 'q', 'rt', 'ins', 'font', 'strong', + 's', 'tt', 'kbd', 'mark', + 'u', 'xm', 'sub', 'nobr', + 'sup', 'ruby', + 'var', 'span', + 'wbr', 'time', + ); +}
\ No newline at end of file diff --git a/includes/banner.inc b/includes/banner.inc index 1d33b43..4ad0583 100644 --- a/includes/banner.inc +++ b/includes/banner.inc @@ -367,14 +367,6 @@ function getMemberBannerData(string $id, string $system, bool $french = false) { ]; } - if (($metadata["sexually_active"] ?? false) && !$french && $isLoggedIn) { - $badges[] = [ - "id" => "sexually_active", - "color" => "d6a833", - "html" => '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Sexually active</b><br>' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' ' . (getMemberPronouns($member['pronouns'])["third"] ? "is" : "are") . ' doing sexual acts and would pleasure ' . getMemberPronouns($member['pronouns'])["possessive_det"] . ' marefriend·s IRL if ' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' had the chance." class="badge rounded-pill" style="background-color:#d68f33;">Sexually active</span>' - ]; - } - if (($metadata["leader"] ?? false) && $isLoggedIn) { $badges[] = [ "id" => "leader", diff --git a/includes/bitset.inc b/includes/bitset.inc index 96c6bfd..f45beb1 100644 --- a/includes/bitset.inc +++ b/includes/bitset.inc @@ -4,27 +4,23 @@ function parseBitset ($bitset) { $bin = str_repeat("0", 48 - strlen(decbin($bitset))) . decbin($bitset); $sharedMemory = bindec(substr($bin, 24, 2)); - $median = substr($bin, 26, 1) !== "0"; $little = bindec(substr($bin, 27, 2)); $food = bindec(substr($bin, 16, 2)); $nonverbal = substr($bin, 15, 1) !== "0"; $lessFrequent = substr($bin, 14, 1) !== "0"; $sexuallyActive = substr($bin, 13, 1) !== "0"; - $ageRegressor = substr($bin, 12, 1) !== "0"; $leader = substr($bin, 11, 1) !== "0"; $persecutor = substr($bin, 10, 1) !== "0"; - $magic = bindec(substr($bin, 18, 3)); - $sensitivity = bindec(substr($bin, 21, 3)); $protector = substr($bin, 29, 1) !== "0"; $fictive = substr($bin, 30, 1) !== "0"; - $notTalking = substr($bin, 31, 1) !== "0"; - $host = substr($bin, 32, 1) !== "0"; $robot = substr($bin, 45, 1) !== "0"; $plush = substr($bin, 46, 1) !== "0"; - $age = substr($bin, 47, 1) !== "0"; + $polyamorous1 = substr($bin, 18, 1) !== "0"; + $polyamorous2 = substr($bin, 19, 1) !== "0"; $species1 = substr($bin, 33, 4); $species2 = substr($bin, 37, 4); - $species3 = substr($bin, 41, 4); + $alignment1 = substr($bin, 41, 4); + $alignment2 = substr($bin, 20, 4); $species1 = match ($species1) { "0001" => "earth", @@ -48,46 +44,57 @@ function parseBitset ($bitset) { default => null, }; - $species3 = match ($species3) { - "0001" => "earth", - "0010" => "unicorn", - "0011" => "pegasus", - "0100" => "alicorn", - "0101" => "batpony", - "0110" => "crystal", - "0111" => "changeling", + $alignment1 = match ($alignment1) { + "0000" => "aroace", + "0001" => "hetero", + "0010" => "homo", + "0011" => "bi", + "0100" => "pan", default => null, }; - if ($little === 1) { - $ageRegressor = true; - $little = 0; - } + $alignment2 = match ($alignment2) { + "0000" => "aroace", + "0001" => "hetero", + "0010" => "homo", + "0011" => "bi", + "0100" => "pan", + default => null, + }; + + if ($little === 1) $little = 0; return [ 'shared_memory' => $sharedMemory, - 'median' => $median, + 'median' => false, 'protector' => $protector, 'fictive' => $fictive, 'little' => $little, - 'not_talking' => $notTalking, - 'host' => $host, + 'not_talking' => false, + 'host' => false, 'robot' => $robot, - 'magic' => $magic, - 'sensitivity' => $sensitivity, + 'magic' => 0, + 'sensitivity' => 0, 'food' => $food, 'plush' => $plush, 'nonverbal' => $nonverbal, 'less_frequent' => $lessFrequent, - 'age_spells' => $age, - 'age_regressor' => $ageRegressor, + 'age_spells' => false, + 'age_regressor' => false, 'leader' => $leader, 'persecutor' => $persecutor, 'sexually_active' => $sexuallyActive, + 'polyamorous' => [ + 'romantic' => $polyamorous1, + 'sexual' => $polyamorous2 + ], + 'alignment' => [ + 'romantic' => $alignment1, + 'sexual' => $alignment2 + ], 'species' => array_filter([ $species1, - $species2, - $species3 + $species2 ], function ($i) { return isset($i); }) diff --git a/includes/details.inc b/includes/details.inc index 8fee082..08ba52a 100644 --- a/includes/details.inc +++ b/includes/details.inc @@ -1,40 +1,32 @@ -<?php global $isLoggedIn; global $metadata; global $memberData; global $lang; global $pages; ?> +<?php global $isLoggedIn; global $metadata; global $memberData; global $lang; global $pages; $pronouns = getMemberPronouns($memberData['pronouns']); ?> <div id="member-details" class="<?= $isLoggedIn ? 'member-details-loggedIn' : '' ?>" style="<?php if (!$isLoggedIn): ?>grid-template-columns: repeat(3, 1fr);<?php endif; ?> background-color: <?= isset($memberData["color"]) ? '#' . $memberData["color"] . "33" : "transparent" ?>; margin-left: -20px; margin-right: -20px;"> <div> <b><?= $lang["details"]["food"] ?></b><span class="member-small-separator"><br></span> <?= match ($metadata["food"]) { - 0 => "<span data-bs-toggle='tooltip' title='" . ucfirst(str_replace("'", "'", getMemberPronouns($memberData['pronouns'])["possessive_det"])) . " species indicates that " . str_replace("'", "'", getMemberPronouns($memberData['pronouns'])["subjective"]) . ' ' . (getMemberPronouns($memberData["pronouns"])["third"] ? "doesn't" : "don't") . " need to eat food, therefore " . str_replace("'", "'", getMemberPronouns($memberData['pronouns'])["subjective"]) . " can eat all foods.'>" . $lang["details"]["food_states"][0] . "</span>", - 1 => "<span data-bs-toggle='tooltip' title='" . ucfirst(str_replace("'", "'", getMemberPronouns($memberData['pronouns'])["subjective"])) . " can't eat fish or meat.'>" . $lang["details"]["food_states"][1] . "</span>", - 2 => "<span data-bs-toggle='tooltip' title='" . ucfirst(str_replace("'", "'", getMemberPronouns($memberData['pronouns'])["subjective"])) . " can't eat meat, but can eat fish.'>" . $lang["details"]["food_states"][2] . "</span>", - 3 => "<span data-bs-toggle='tooltip' title='" . ucfirst(str_replace("'", "'", getMemberPronouns($memberData['pronouns'])["subjective"])) . " can eat all foods, meat and fish included.'>" . $lang["details"]["food_states"][3] . "</span>", + 0 => "<span data-bs-toggle='tooltip' title='" . ucfirst(str_replace("'", "'", $pronouns["possessive_det"])) . " species indicates that " . str_replace("'", "'", $pronouns["subjective"]) . ' ' . ($pronouns["third"] ? "doesn't" : "don't") . " need to eat food, therefore " . str_replace("'", "'", $pronouns["subjective"]) . " can eat all foods.'>" . $lang["details"]["food_states"][0] . "</span>", + 1 => "<span data-bs-toggle='tooltip' title='" . ucfirst(str_replace("'", "'", $pronouns["subjective"])) . " can't eat fish or meat.'>" . $lang["details"]["food_states"][1] . "</span>", + 2 => "<span data-bs-toggle='tooltip' title='" . ucfirst(str_replace("'", "'", $pronouns["subjective"])) . " can't eat meat, but can eat fish.'>" . $lang["details"]["food_states"][2] . "</span>", + 3 => "<span data-bs-toggle='tooltip' title='" . ucfirst(str_replace("'", "'", $pronouns["subjective"])) . " can eat all foods, meat and fish included.'>" . $lang["details"]["food_states"][3] . "</span>", } ?> </div> <div> <b><?= $lang["details"]["memory"] ?></b><span class="member-small-separator"><br></span> <?= match ($metadata["shared_memory"]) { - 0 => "<span data-bs-toggle='tooltip' title='" . str_replace("'", "'", ucfirst(getMemberPronouns($memberData['pronouns'])["subjective"])) . (getMemberPronouns($memberData["pronouns"])["third"] ? " doesn't directly share" : " don't directly share") . " memory with " . str_replace("'", "'", getMemberPronouns($memberData['pronouns'])["possessive_det"]) . " headmates, but is able to (slowly) query it if that is needed.'>" . $lang["details"]["memory_states"][0] . "</span>", - 1 => "<span data-bs-toggle='tooltip' title='" . str_replace("'", "'", ucfirst(getMemberPronouns($memberData['pronouns'])["subjective"])) . (getMemberPronouns($memberData["pronouns"])["third"] ? " doesn't directly shares" : " don't directly share") . " all memories with " . str_replace("'", "'", getMemberPronouns($memberData['pronouns'])["possessive_det"]) . " headmates, but may be able to share certain memories and/or share memories at will.'>" . $lang["details"]["memory_states"][1] . "</span>", - 2 => "<span data-bs-toggle='tooltip' title='" . str_replace("'", "'", ucfirst(getMemberPronouns($memberData['pronouns'])["subjective"])) . (getMemberPronouns($memberData["pronouns"])["third"] ? " shares" : " share") . " all memories with " . str_replace("'", "'", getMemberPronouns($memberData['pronouns'])["possessive_det"]) . " headmates.'>" . $lang["details"]["memory_states"][2] . "</span>", + 0 => "<span data-bs-toggle='tooltip' title='" . str_replace("'", "'", ucfirst($pronouns["subjective"])) . ($pronouns["third"] ? " doesn't directly share" : " don't directly share") . " memory with " . str_replace("'", "'", $pronouns["possessive_det"]) . " headmates, but is able to (slowly) query it if that is needed.'>" . $lang["details"]["memory_states"][0] . "</span>", + 1 => "<span data-bs-toggle='tooltip' title='" . str_replace("'", "'", ucfirst($pronouns["subjective"])) . ($pronouns["third"] ? " doesn't directly shares" : " don't directly share") . " all memories with " . str_replace("'", "'", $pronouns["possessive_det"]) . " headmates, but may be able to share certain memories and/or share memories at will.'>" . $lang["details"]["memory_states"][1] . "</span>", + 2 => "<span data-bs-toggle='tooltip' title='" . str_replace("'", "'", ucfirst($pronouns["subjective"])) . ($pronouns["third"] ? " shares" : " share") . " all memories with " . str_replace("'", "'", $pronouns["possessive_det"]) . " headmates.'>" . $lang["details"]["memory_states"][2] . "</span>", } ?> </div> <?php if ($isLoggedIn): ?> <div> - <b>Sensitivity:</b><span class="member-small-separator"><br></span> - <?= match ($metadata["sensitivity"]) { - 0 => "<span data-bs-toggle='tooltip' title='" . str_replace("'", "'", ucfirst(getMemberPronouns($memberData['pronouns'])["subjective"])) . (getMemberPronouns($memberData["pronouns"])["third"] ? " does" : " do") . " not have sensitive spots on " . str_replace("'", "'", getMemberPronouns($memberData['pronouns'])["possessive_det"]) . " body.'>None</span>", - 1 => "<span data-bs-toggle='tooltip' title='" . str_replace("'", "'", ucfirst(getMemberPronouns($memberData['pronouns'])["subjective"])) . " may have (a) sensitive spot·s on " . str_replace("'", "'", getMemberPronouns($memberData['pronouns'])["possessive_det"]) . " body.'>Maybe</span>", - 2 => "<span data-bs-toggle='tooltip' title='" . str_replace("'", "'", ucfirst(getMemberPronouns($memberData['pronouns'])["subjective"])) . (getMemberPronouns($memberData["pronouns"])["third"] ? " has" : " have") . " (a) sensitive spot·s on " . str_replace("'", "'", getMemberPronouns($memberData['pronouns'])["possessive_det"]) . " body, and playing with it/them will make " . str_replace("'", "'", getMemberPronouns($memberData['pronouns'])["object"]) . " feel affectionate: extremely safe and cared for.'>Affectionate</span>", - 3 => "<span data-bs-toggle='tooltip' title='" . str_replace("'", "'", ucfirst(getMemberPronouns($memberData['pronouns'])["subjective"])) . (getMemberPronouns($memberData["pronouns"])["third"] ? " has" : " have") . " (a) sensitive spot·s on " . str_replace("'", "'", getMemberPronouns($memberData['pronouns'])["possessive_det"]) . " body, and playing with it/them will make " . str_replace("'", "'", getMemberPronouns($memberData['pronouns'])["object"]) . " feel sexually pleasured.'>Sexual</span>", - 4 => "<span data-bs-toggle='tooltip' title='" . str_replace("'", "'", ucfirst(getMemberPronouns($memberData['pronouns'])["subjective"])) . (getMemberPronouns($memberData["pronouns"])["third"] ? " has" : " have") . " (a) sensitive spot·s on " . str_replace("'", "'", getMemberPronouns($memberData['pronouns'])["possessive_det"]) . " body, and playing with it/them will make " . str_replace("'", "'", getMemberPronouns($memberData['pronouns'])["object"]) . " feel affectionate or sexually pleasured.'>Affectionate and sexual</span>", - } ?> - </div> - <div> <b>Age:</b><span class="member-small-separator"><br></span> <?php if (!isset($metadata["birth"]["year"])): ?> - <?php if ($metadata["birth"]["age"] <= 0): ?> + <?php if ($metadata["birth"]["age"] === -1): $age = abs(log(0)); // => INF ?> + <?= "<span data-bs-toggle='tooltip' title='" . str_replace("'", "'", ucfirst($pronouns["subjective"])) . " never age" . str_replace("'", "'", $pronouns["third"] ? "s" : "") . " and " . str_replace("'", "'", $pronouns["third"] ? "doesn't" : "don't") . " have a defined age, making " . str_replace("'", "'", $pronouns["object"]) . " pretty much eternal.'>Eternal</span>" ?> + <?php elseif ($metadata["birth"]["age"] <= 0): ?> - - <?php else: ?> - <?= "<span data-bs-toggle='tooltip' title='" . str_replace("'", "'", ucfirst(getMemberPronouns($memberData['pronouns'])["subjective"])) . str_replace("'", "'", getMemberPronouns($memberData["pronouns"])["third"] ? " doesn't" : " don't") . " age like the body does, making " . str_replace("'", "'", getMemberPronouns($memberData['pronouns'])["object"]) . " stay " . $metadata["birth"]["age"] . " years old for " . str_replace("'", "'", getMemberPronouns($memberData['pronouns'])["possessive_det"]) . " entire life.'>" . $metadata["birth"]["age"] . "* years old</span>" ?> + <?php else: $age = $metadata["birth"]["age"]; ?> + <?= "<span data-bs-toggle='tooltip' title='" . str_replace("'", "'", ucfirst($pronouns["subjective"])) . str_replace("'", "'", $pronouns["third"] ? " doesn't" : " don't") . " age like the body does, making " . str_replace("'", "'", $pronouns["object"]) . " stay " . $metadata["birth"]["age"] . " years old for " . str_replace("'", "'", $pronouns["possessive_det"]) . " entire life.'>" . $metadata["birth"]["age"] . "* years old</span>" ?> <?php endif; ?> <?php else: ?> <?php if ($metadata["birth"]["year"] <= 1900): ?> @@ -47,7 +39,8 @@ $birthdate = $metadata["birth"]["year"] . "-" . $metadata["birth"]["date"]; } - $age = floor((time() - strtotime($birthdate)) / 31557600); + $age = (int)date('Y') - $metadata["birth"]["year"] + (strtotime(date('Y') . "-" . $metadata["birth"]["date"]) <= time() ? 0 : -1); + $age2 = floor((time() - strtotime($birthdate)) / 31557600); $birthday = "as time passes"; if (isset($metadata["birth"]["date"]) && trim($metadata["birth"]["date"]) !== "" && $metadata["birth"]["date"] !== "01-01") { @@ -55,7 +48,7 @@ } ?> - <?= "<span data-bs-toggle='tooltip' title='" . str_replace("'", "'", ucfirst(getMemberPronouns($memberData['pronouns'])["subjective"])) . " age" . str_replace("'", "'", getMemberPronouns($memberData["pronouns"])["third"] ? "s" : "") . " like the body does, making " . str_replace("'", "'", getMemberPronouns($memberData['pronouns'])["object"]) . " stay " . $age . " years old for now, having this age change $birthday.'>" . $age . " years old</span>" ?> + <?= "<span data-bs-toggle='tooltip' title='$age2 with the old algorithm | " . str_replace("'", "'", ucfirst($pronouns["subjective"])) . " age" . str_replace("'", "'", $pronouns["third"] ? "s" : "") . " like the body does, making " . str_replace("'", "'", $pronouns["object"]) . " stay " . $age . " years old for now, having this age change $birthday.'>" . $age . " years old</span>" ?> <?php endif; ?> <?php endif; ?> </div> @@ -76,4 +69,75 @@ - <?php endif; ?> </div> -</div>
\ No newline at end of file +</div> +<?php if ($isLoggedIn): ?> +<div id="member-details-2" style="background-color: <?= isset($memberData["color"]) ? '#' . $memberData["color"] . "33" : "transparent" ?>; margin-left: -20px; margin-right: -20px;"> + <?php if ((isset($age) && $age >= 16 && $metadata["little"] === 0) || (!isset($age) && $metadata["little"] === 0)): ?> + <div> + <b>Sexual consent:</b><span class="member-small-separator"><br></span> + <?php if ($metadata["sexually_active"]): ?> + <span title="Consent is assumed when having sex with <?= $pronouns["object"] ?>, however <?= $pronouns["subjective"] ?> may ask to not have sex at the moment and such request must be honored." data-bs-toggle="tooltip">Preemptive</span> + <?php else: ?> + <span title="Consent is absolutely required every time when having sex with <?= $pronouns["object"] ?>." data-bs-toggle="tooltip">Required</span> + <?php endif; ?> + </div> + <div> + <b><span class="member-detail-desktop">Sexual alignment</span><span class="member-detail-mobile">Sex. algn.</span>:</b><span class="member-small-separator"><br></span> + <?php switch ($metadata["alignment"]["sexual"]) { + case "aroace": + echo "Asexual"; + break; + case "hetero": + echo "Straight"; + break; + case "homo": + echo $pronouns["subjective"] === "she" ? "Lesbian" : ($pronouns["subjective"] === "he" ? "Gay" : "Homosexual"); + break; + case "bi": + echo "Bisexual"; + break; + case "pan": + echo "Pansexual"; + break; + } ?><?php if ($metadata["polyamorous"]["sexual"]): ?> (poly)<?php endif; ?> + </div> + <?php else: ?> + <div> + <style> + #member-details-2 { + grid-template-columns: 2fr 1fr; + } + </style> + <div style="display: flex; align-items: center; justify-content: center; height: 100%;opacity:.5;">This member is too young to have a sexual relationship.</div> + </div> + <?php endif; ?> + <div> + <b><span class="member-detail-desktop">Romantic alignment</span><span class="member-detail-mobile">Rom. algn.</span>:</b><span class="member-small-separator"><br></span> + <?php switch ($metadata["alignment"]["romantic"]) { + case "aroace": + echo "Aromantic"; + break; + case "hetero": + echo "Heteroromantic"; + break; + case "homo": + echo $pronouns["subjective"] === "she" ? "Lesbiromantic" : ($pronouns["subjective"] === "he" ? "Gay" : "Homoromantic"); + break; + case "bi": + echo "Biromantic"; + break; + case "pan": + echo "Panromantic"; + break; + } ?><?php if ($metadata["polyamorous"]["romantic"]): ?> (poly)<?php endif; ?> + </div> + <!--<div> + <b>Item 4:</b><span class="member-small-separator"><br></span> + Value 4 + </div> + <div> + <b>Item 5:</b><span class="member-small-separator"><br></span> + Value 5 + </div>--> +</div> +<?php endif; ?> diff --git a/includes/footer.inc b/includes/footer.inc index b95ea78..cd1bb89 100644 --- a/includes/footer.inc +++ b/includes/footer.inc @@ -1,5 +1,6 @@ <?php +global $pageFile; require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/functions.inc"; ?> @@ -16,7 +17,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/functions.inc"; global $lang; global $pages; ?> - © <?= date("Y") ?> <a href="https://equestria.horse" target="_blank" class="text-muted"><?= $lang["footer"]["copyright"] ?></a> · version 2.<?= $version["build"] ?>.<?= hexdec(substr($version["hash"], 0, 4)) ?>.<?= $version["revision"] ?><br> + © <?= date("Y") ?> <a href="https://equestria.horse" target="_blank" class="text-muted"><?= $lang["footer"]["copyright"] ?></a> · version 2.<?= $version["build"] ?>.<?= $version["revision"] ?> · node <?= strtoupper(dechex(fileinode($pageFile))) ?><br> <a href="/-/debug" class="text-muted" style="text-decoration: none;"><?= $lang["footer"]["update"] ?> <?= trim(timeAgo($refresh["timestamp"], $lang["_french"])) ?> (<?php if (time() - $refresh["timestamp"] > 360): ?><?= $lang["footer"]["no_update"] ?><?= $lang["footer"]["separator"] ?>; <?php endif; ?><?= date('D j M, G:i:s T', (int)$refresh["timestamp"]) ?><?= $lang["footer"]["separator"] ?>; <?= $lang["footer"]["took"] ?> <?= round($refresh["duration"] * 1000) ?> ms, <?= count($refresh["restored"]) > 0 ? (count($refresh["restored"]) > 1 ? $lang["footer"]["failures"][0] . count($refresh["restored"]) . $lang["footer"]["failures"][1] : $lang["footer"]["failure"]) : $lang["footer"]["no_failure"] ?>)</a> <br><br><br><br><br> </div> diff --git a/includes/fullbanner.inc b/includes/fullbanner.inc index 24e0295..c50ad5f 100644 --- a/includes/fullbanner.inc +++ b/includes/fullbanner.inc @@ -20,69 +20,6 @@ <b>This member's metadata needs an update.</b> It still uses the old JSON metadata system instead of the new 24bit integer-based system. Contact a developer to request an update. (only administrators can see this; file: <code><?= $_SERVER['DOCUMENT_ROOT'] ?>/includes/data/metadata/<?= $memberID ?>.json</code>) </div> <?php endif; ?> - <div class="alert alert-dark"> - <details> - <summary>Private administrator information</summary> - <ul style="margin-bottom:0;"> - <li><b>ID:</b> <code><?= $memberID ?></code> (<code><?= $systemID . "/" . $memberID ?></code>, <?= $memberData["name"] ?>)</li> - <li><b>Files:</b> - <ul> - <li><code><?= $_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/$memberID.json" ?></code> (<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/$memberID.json") ? filesize($_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/$memberID.json") . " bytes" : "not found" ?>)</li> - <li><code><?= $_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/$memberID.html" ?></code> (<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/$memberID.html") ? filesize($_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/$memberID.html") . " bytes" : "not found" ?>)</li> - <li><code><?= $_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/$memberID-private.html" ?></code> (<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/$memberID-private.html") ? filesize($_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/$memberID-private.html") . " bytes" : "not found" ?>)</li> - </ul> - </li> - <li><b>Date added:</b> <?= date('l j F Y', strtotime($memberData["created"])) ?> (<?= timeAgo($memberData["created"]) ?>, <code><?= $memberData["created"] ?></code>)</li> - <li><b>Pronouns:</b> <?= implode(", ", getPronounsFromMark($memberData['pronouns'])) ?></li> - <li><b>Pronouns usage:</b> <ul><?php - - foreach (getMemberPronouns($memberData['pronouns']) as $type => $usage) { - if (is_string($usage) && $type !== "color") { - echo("<li><b>" . $type . ":</b> " . $usage . "</li>"); - } - } - - ?></ul></li> - <li><b>Color:</b> <span style="border:1px solid rgba(255, 255, 255, .5);background-color:#<?= $memberData["color"] ?? "ffffff" ?>;display:inline-block;width:16px;height:16px;border-radius:5px;vertical-align: middle;filter: invert(1) hue-rotate(180deg);"></span> <span style="vertical-align: middle;"><code>#<?= $memberData["color"] ?? "ffffff" ?></code></span> - <li><b>Bitset:</b><?php if (isset($metadata["bitset"])): ?> <code><?= str_repeat("0", 48 - strlen(decbin($metadata["bitset"]))) . decbin($metadata["bitset"]) ?></code> (0x<?= str_repeat("0", 12 - strlen(dechex($metadata["bitset"]))) . dechex($metadata["bitset"]) ?>, <?= $metadata["bitset"] ?>)</li><?php else: ?> <span class="text-warning" style="filter:invert(1) hue-rotate(180deg);">Not using bitset; please update.</span><?php endif; ?> - <li><b>Reduced name:</b> <?= getMiniName($memberData["display_name"] ?? $member["name"]) ?></li> - <li><b>Shared memory access:</b> <code><?= $metadata["shared_memory"] ?></code> (<?= $metadata["shared_memory"] === 2 ? "Full direct access" : ($metadata["shared_memory"] === 0 ? "No direct access" : "Partial direct access") ?>)</li> - <li><b>Protector:</b> <code><?= $metadata["protector"] ? "1" : "0" ?></code> (<?= $metadata["protector"] ? "Yes" : "No" ?>)</li> - <li><b>Little:</b> <code><?= $metadata["little"] ?></code> (<?= $metadata["little"] === 2 ? "Is a little" : ($metadata["little"] === 1 ? "Is an age regressor" : ($metadata["little"] === 3 ? "Not a little, but younger" : "No")) ?>)</li> - <li><b>Relations count:</b> <code><?= count($metadata["marefriends"]) + count($metadata["sisters"]) ?></code></li> - <?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/score.inc"; $score = calculateScore($metadata, $memberData["display_name"] ?? $memberData["name"]); ?> - <li> - <b>Score breakdown:</b> <code><?= $score["total"] ?></code> - <ul><?php - - foreach ($metadata as $type => $usage) { - if (is_string($usage)) { - echo("<li><b>" . $type . ":</b> " . $usage . "</li>"); - } else if (is_array($usage)) { - if (count($usage) === 0) { - echo("<li><b>" . $type . ":</b> []</li>"); - } else { - echo("<li><b>" . $type . ":</b><ul>"); - - foreach ($usage as $key => $item) { - if (is_string($item)) { - echo("<li><b>" . $key . ":</b> " . $item . "</li>"); - } else { - echo("<li><b>" . $key . ":</b> " . json_encode($item, JSON_UNESCAPED_SLASHES) . "</li>"); - } - } - - echo("</ul></li>"); - } - } else { - echo("<li><b>" . $type . ":</b> " . json_encode($usage, JSON_UNESCAPED_SLASHES) . "</li>"); - } - } - - ?></ul></li> - </ul> - </details> - </div> <?php endif; ?> <?php if ($travelling[$memberID]['travelling'] && !$travelling[$memberID]['equestria']): ?> <div class="alert alert-primary"> diff --git a/includes/functions.inc b/includes/functions.inc index 915d41b..bdc961b 100644 --- a/includes/functions.inc +++ b/includes/functions.inc @@ -196,18 +196,7 @@ if (!function_exists("getMiniName")) { if (!function_exists("withCaretakersDown")) { function withCaretakersDown(array $ordered): array { - $caretakersNo = []; - $caretakersYes = []; - - foreach ($ordered as $item) { - if ($item["_metadata"]["little"] === 2) { - $caretakersYes[] = $item; - } else { - $caretakersNo[] = $item; - } - } - - return [...$caretakersNo, ...$caretakersYes]; + return $ordered; } } diff --git a/includes/maintenance/clearUnused.php b/includes/maintenance/clearUnused.php new file mode 100644 index 0000000..99b6ca4 --- /dev/null +++ b/includes/maintenance/clearUnused.php @@ -0,0 +1,34 @@ +<?php + +$_SERVER['DOCUMENT_ROOT'] = "../.."; + +foreach (array_filter(scandir($_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata"), function ($i) { return !str_starts_with($i, "."); }) as $file) { + $data = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/$file"), true); + echo("$file\n"); + + if (!isset($data["bitset"])) { + die(" No bitset?? wtf\n"); + } else { + $bits = str_split(substr(str_repeat("0", 48), 0, 48 - strlen(decbin($data["bitset"]))) . decbin($data["bitset"])); + echo(" Old:\n"); + echo(" " . str_replace("+", "\e[92m1\e[39m", str_replace("-", "\e[31m0\e[39m", implode("", str_replace("0", "-", str_replace("1", "+", $bits))))) . "\n"); + echo(" XXXXXXXXXX X XXXXXX X XX XXXX X\n"); + + $bitsOld = implode("", $bits); + foreach ([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 18, 19, 20, 21, 22, 23, 26, 31, 32, 41, 42, 43, 44, 47] as $bit) { + $bits[$bit] = "0"; + } + + echo(" New:\n"); + echo(" " . str_replace("+", "\e[92m1\e[39m", str_replace("-", "\e[31m0\e[39m", implode("", str_replace("0", "-", str_replace("1", "+", $bits))))) . "\n"); + echo(" XXXXXXXXXX X XXXXXX X XX XXXX X\n"); + echo(" Changed " . levenshtein($bitsOld, implode("", $bits)) . " bit(s)\n"); + + echo(" Backup made in " . substr($file, 0, -5) . "-old.json\n"); + copy($_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/$file", $_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/" . substr($file, 0, -5) . "-old.json"); + + $data["bitset"] = bindec(implode("", $bits)); + echo(" Saved to " . $file . "\n"); + file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/$file", json_encode($data)); + } +}
\ No newline at end of file diff --git a/includes/member.inc b/includes/member.inc index 8734471..e984861 100644 --- a/includes/member.inc +++ b/includes/member.inc @@ -78,7 +78,71 @@ if ($memberData["name"] === "fusion") { </div> <?php else: ?> <?php global $isLoggedIn; if ($isLoggedIn): ?> - <small style="opacity:.5;display:block;">(edit: <a href="/-/metadata/<?= $system ?>/<?= $memberData['name'] ?>">metadata</a>, <a href="/-/edit/<?= $system ?>/<?= $memberData['name'] ?>">public</a>, <a href="/-/edit-private/<?= $system ?>/<?= $memberData['name'] ?>">private</a>)</small> + <details> + <summary style="list-style: none;"> + <small style="opacity:.5;display:block;">(edit: <a href="/-/metadata/<?= $system ?>/<?= $memberData['name'] ?>">metadata</a>, <a href="/-/edit/<?= $system ?>/<?= $memberData['name'] ?>">public</a>, <a href="/-/edit-private/<?= $system ?>/<?= $memberData['name'] ?>">private</a>)</small> + </summary> + <div class="alert alert-dark"> + <ul style="margin-bottom:0;"> + <li><b>ID:</b> <code><?= $memberID ?></code> (<code><?= $systemID . "/" . $memberID ?></code>, <?= $memberData["name"] ?>)</li> + <li><b>Files:</b> + <ul> + <li><code><?= $_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/$memberID.json" ?></code> (<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/$memberID.json") ? filesize($_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/$memberID.json") . " bytes" : "not found" ?>)</li> + <li><code><?= $_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/$memberID.html" ?></code> (<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/$memberID.html") ? filesize($_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/$memberID.html") . " bytes" : "not found" ?>)</li> + <li><code><?= $_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/$memberID-private.html" ?></code> (<?= file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/$memberID-private.html") ? filesize($_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/$memberID-private.html") . " bytes" : "not found" ?>)</li> + </ul> + </li> + <li><b>Date added:</b> <?= date('l j F Y', strtotime($memberData["created"])) ?> (<?= timeAgo($memberData["created"]) ?>, <code><?= $memberData["created"] ?></code>)</li> + <li><b>Pronouns:</b> <?= implode(", ", getPronounsFromMark($memberData['pronouns'])) ?></li> + <li><b>Pronouns usage:</b> <ul><?php + + foreach (getMemberPronouns($memberData['pronouns']) as $type => $usage) { + if (is_string($usage) && $type !== "color") { + echo("<li><b>" . $type . ":</b> " . $usage . "</li>"); + } + } + + ?></ul></li> + <li><b>Color:</b> <span style="border:1px solid rgba(255, 255, 255, .5);background-color:#<?= $memberData["color"] ?? "ffffff" ?>;display:inline-block;width:16px;height:16px;border-radius:5px;vertical-align: middle;filter: invert(1) hue-rotate(180deg);"></span> <span style="vertical-align: middle;"><code>#<?= $memberData["color"] ?? "ffffff" ?></code></span> + <li><b>Bitset:</b><?php if (isset($metadata["bitset"])): ?> <code><?= str_repeat("0", 48 - strlen(decbin($metadata["bitset"]))) . decbin($metadata["bitset"]) ?></code> (0x<?= str_repeat("0", 12 - strlen(dechex($metadata["bitset"]))) . dechex($metadata["bitset"]) ?>, <?= $metadata["bitset"] ?>)</li><?php else: ?> <span class="text-warning" style="filter:invert(1) hue-rotate(180deg);">Not using bitset; please update.</span><?php endif; ?> + <li><b>Reduced name:</b> <?= getMiniName($memberData["display_name"] ?? $member["name"]) ?></li> + <li><b>Shared memory access:</b> <code><?= $metadata["shared_memory"] ?></code> (<?= $metadata["shared_memory"] === 2 ? "Full direct access" : ($metadata["shared_memory"] === 0 ? "No direct access" : "Partial direct access") ?>)</li> + <li><b>Protector:</b> <code><?= $metadata["protector"] ? "1" : "0" ?></code> (<?= $metadata["protector"] ? "Yes" : "No" ?>)</li> + <li><b>Little:</b> <code><?= $metadata["little"] ?></code> (<?= $metadata["little"] === 2 ? "Is a little" : ($metadata["little"] === 1 ? "Is an age regressor" : ($metadata["little"] === 3 ? "Not a little, but younger" : "No")) ?>)</li> + <li><b>Relations count:</b> <code><?= count($metadata["marefriends"]) + count($metadata["sisters"]) ?></code></li> + <?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/score.inc"; $score = calculateScore($metadata, $memberData["display_name"] ?? $memberData["name"]); ?> + <li> + <b>Score breakdown:</b> <code><?= $score["total"] ?></code> + <ul><?php + + foreach ($metadata as $type => $usage) { + if (is_string($usage)) { + echo("<li><b>" . $type . ":</b> " . $usage . "</li>"); + } else if (is_array($usage)) { + if (count($usage) === 0) { + echo("<li><b>" . $type . ":</b> []</li>"); + } else { + echo("<li><b>" . $type . ":</b><ul>"); + + foreach ($usage as $key => $item) { + if (is_string($item)) { + echo("<li><b>" . $key . ":</b> " . $item . "</li>"); + } else { + echo("<li><b>" . $key . ":</b> " . json_encode($item, JSON_UNESCAPED_SLASHES) . "</li>"); + } + } + + echo("</ul></li>"); + } + } else { + echo("<li><b>" . $type . ":</b> " . json_encode($usage, JSON_UNESCAPED_SLASHES) . "</li>"); + } + } + + ?></ul></li> + </ul> + </div> + </details> <?php endif; ?> <?php if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/$memberID-private.html") && $isLoggedIn): ?> diff --git a/includes/navigation.inc b/includes/navigation.inc index bdafe99..fe1c9ee 100644 --- a/includes/navigation.inc +++ b/includes/navigation.inc @@ -54,13 +54,6 @@ $navigation_admin = [ "minimal" => false, "items" => [ [ - "name" => $pages["dashboard"]["name"][$lang["_name"]], - "icon" => "/assets/icons/dashboard.svg", - "invert" => true, - "link" => "/-/dashboard", - "stepped" => null - ], - [ "name" => $pages["about"]["name"][$lang["_name"]], "icon" => "/assets/icons/about.svg", "invert" => true, @@ -89,10 +82,10 @@ $navigation_admin = [ "stepped" => null ], [ - "name" => $pages["rules-old"]["name"][$lang["_name"]], - "icon" => "/assets/icons/rules-old.svg", + "name" => $pages["rules"]["name"][$lang["_name"]], + "icon" => "/assets/icons/rules.svg", "invert" => true, - "link" => "/-/rules-old", + "link" => "/-/rules", "stepped" => null ], [ diff --git a/includes/pages.json b/includes/pages.json index 4c6fcf5..a55c748 100644 --- a/includes/pages.json +++ b/includes/pages.json @@ -235,22 +235,13 @@ }, "rules": { "name": { - "en": "General rules", - "fr": "General rules" + "en": "Rules", + "fr": "Rules" }, "short": "Rules", "admin": true, "rail": true }, - "rules-old": { - "name": { - "en": "Systems rules (legacy)", - "fr": "Systems rules (legacy)" - }, - "short": "Rules (old)", - "admin": true, - "rail": true - }, "s:compare": { "name": { "en": "Compare members", diff --git a/includes/planner.inc b/includes/planner.inc index 225fca4..a5782a5 100644 --- a/includes/planner.inc +++ b/includes/planner.inc @@ -84,7 +84,9 @@ function day($display, $diff): void { if ($diff < 0) $disabled = true; else $dis ?> <tr class="planner-day" id="planner-header-<?= $diff ?>" <?php if ($disabled): ?>style="opacity: .75; pointer-events: none;"<?php endif; ?>> - <td colspan="10"><?= $display ?></td> + <td colspan="10" style="border:none;padding:0;"> + <div style="padding:5px 10px;margin:-0.5px;border-top:1px solid #404040;border-left:1px solid #404040;border-right:1px solid #404040;border-top-left-radius: 10px; border-top-right-radius: 10px;"><?= $display ?></div> + </td> <?php if (!isset($cloudburst[date('Y-m-d', time() + (86400 * $diff))])) $cloudburst[date('Y-m-d', time() + (86400 * $diff))] = []; @@ -100,96 +102,110 @@ function day($display, $diff): void { if ($diff < 0) $disabled = true; else $dis ?> </tr> <tr class="planner-header" <?php if ($disabled): ?>style="opacity: .75; pointer-events: none;"<?php endif; ?>> - <td colspan="5">Cloudburst System</td> - <td colspan="5">Raindrops System</td> + <td colspan="5" style="border-top-color:transparent;border-right-color: transparent;">Cloudburst System</td> + <td colspan="5" style="border-top-color:transparent;border-left-color: transparent;">Raindrops System</td> </tr> <?php for ($i = 0; $i <= $biggest; $i++): ?> <tr class="planner-member" <?php if ($disabled): ?>style="opacity: .75; pointer-events: none;"<?php endif; ?>> <?php if (isset($dayCloudburst[$index])): ?> - <td class="planner-member-id"> + <td class="planner-member-id" style="border-right-color: transparent;border-bottom-color: transparent;"> <?= $index + 1 ?> </td> - <td class="planner-link" style="width:50vw;" <?php if (!isset($dayCloudburst[$index][1])): ?>colspan="3" <?php else: ?>colspan="2"<?php endif; ?>> + <td class="planner-link" style="width:50vw;border-right-color: transparent;border-bottom-color: transparent;" <?php if (!isset($dayCloudburst[$index][1])): ?>colspan="3" <?php else: ?>colspan="2"<?php endif; ?>> <?php $member = getMemberWithoutSystem($dayCloudburst[$index][0]); ?> <?php if ($member["name"] === "fusion"): ?> - <a class="member-link" onclick="openEditFronter('cloudburst', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * $diff)) ?>')"><img src="/assets/logo/logo.png" style="width:24px;"> <span class="member-link-text"><span class="text-muted merge-desktop">Multiple merged members</span><span class="text-muted merge-mobile">Merge</span></span></a> + <a class="member-link" onclick="openEditFronter('cloudburst', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * $diff)) ?>')"><span class="member-link-inner"><img src="/assets/logo/newlogo.png" style="filter:grayscale(1);width:24px;"> <span class="member-link-text"><span class="merge-desktop" style="opacity:.75;">Multiple merged members</span></span></span></a> + <?php elseif ($member["name"] === "unknown"): ?> + <a class="member-link" onclick="openEditFronter('cloudburst', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * $diff)) ?>')"><span class="member-link-inner"><img src="/assets/logo/newlogo.png" style="filter:grayscale(1);width:24px;"> <span class="member-link-text"><span class="merge-desktop" style="opacity:.75;">Other/unknown/fallback pony</span></span></span></a> <?php else: ?> - <a class="member-link" onclick="openEditFronter('cloudburst', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * $diff)) ?>')"><img src="<?= getAsset($member["system"], $member["id"], "heads") ?>" style="width:24px;"> <span class="member-link-text"><?= getMiniName($member["display_name"] ?? $member["name"]) ?></span></a> + <a class="member-link" onclick="openEditFronter('cloudburst', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * $diff)) ?>')"><span class="member-link-inner"><img src="<?= getAsset($member["system"], $member["id"], "heads") ?>" style="width:24px;"> <span class="member-link-text"><?= !isset($dayCloudburst[$index][1]) ? $member["display_name"] ?? $member["name"] : getMiniName($member["display_name"] ?? $member["name"]) ?></span></span></a> <?php endif; ?> </td> <?php if (!isset($dayCloudburst[$index][1])): ?> - <td class="planner-cofronter-inner planner-link" style="width:5%;text-align:center;"> - <a onclick="addCofronter('cloudburst', <?= $diff ?>, <?= $index ?>);" class="planner-add-link"> - <img src="/assets/icons/add.svg" alt="" class="planner-add-icon"> + <td class="planner-cofronter-inner planner-link" style="width:5%;text-align:center;border-left-color: transparent;border-bottom-color: transparent;"> + <a onclick="addCofronter('cloudburst', <?= $diff ?>, <?= $index ?>);" class="planner-add-link planner-add-link-cofronter"> + <span class="planner-add-link-cofronter-inner"><img src="/assets/icons/add.svg" alt="" class="planner-add-icon"></span> </a> </td> <?php else: ?> - <td class="planner-link" style="width:41.35vw;" colspan="2"> + <td class="planner-link" style="border-bottom-color: transparent;width:41.35vw;" colspan="2"> <?php $member = getMemberWithoutSystem($dayCloudburst[$index][1]); ?> <?php if ($member["name"] === "fusion"): ?> - <a class="member-link" onclick="openEditCofronter('cloudburst', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * $diff)) ?>')"><img src="/assets/logo/logo.png" style="width:24px;"> <span class="member-link-text"><span class="text-muted merge-desktop">Multiple merged members</span><span class="text-muted merge-mobile">Merge</span></span></a> + <a class="member-link" onclick="openEditCofronter('cloudburst', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * $diff)) ?>')"><span class="member-link-inner"><img src="/assets/logo/newlogo.png" style="filter:grayscale(1);width:24px;"> <span class="member-link-text"><span class="merge-desktop" style="opacity:.75;">Multiple merged members</span></span></span></a> + <?php elseif ($member["name"] === "unknown"): ?> + <a class="member-link" onclick="openEditCofronter('cloudburst', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * $diff)) ?>')"><span class="member-link-inner"><img src="/assets/logo/newlogo.png" style="filter:grayscale(1);width:24px;"> <span class="member-link-text"><span class="merge-desktop" style="opacity:.75;">Other/unknown/fallback pony</span></span></span></a> <?php else: ?> - <a class="member-link" onclick="openEditCofronter('cloudburst', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * $diff)) ?>')"><img src="<?= getAsset($member["system"], $member["id"], "heads") ?>" style="width:24px;"> <span class="member-link-text"><?= getMiniName($member["display_name"] ?? $member["name"]) ?></span></a> + <a class="member-link" onclick="openEditCofronter('cloudburst', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * $diff)) ?>')"><span class="member-link-inner"><img src="<?= getAsset($member["system"], $member["id"], "heads") ?>" style="width:24px;"> <span class="member-link-text"><?= !isset($dayCloudburst[$index][1]) ? $member["display_name"] ?? $member["name"] : getMiniName($member["display_name"] ?? $member["name"]) ?></span></span></a> <?php endif; ?> </td> <?php endif; ?> <?php elseif ($index === count($dayCloudburst)): ?> - <td class="planner-add-inner planner-link" colspan="5"> + <td class="planner-add-inner planner-link" colspan="5" style="border-top-color: transparent;border-bottom-color: transparent;"> <a onclick="addFronter('cloudburst', <?= $diff ?>, <?= $index ?>);" id="planner-add-link-cloudburst-<?= $diff ?>" class="planner-add-link"> - <img src="/assets/icons/add.svg" alt="" class="planner-add-icon"> - <span class="planner-add-text">Add new fronter</span> + <div class="planner-add-link-inner"> + <img src="/assets/icons/add.svg" alt="" class="planner-add-icon"> + <span class="planner-add-text">Add new fronter</span> + </div> </a> </td> <?php else: ?> - <td colspan="5" class="planner-empty"></td> + <td colspan="5" class="planner-empty" style="border-top-color:transparent;border-bottom-color:transparent;"></td> <?php endif; ?> <?php if (isset($dayRaindrops[$index])): ?> - <td class="planner-member-id"> + <td class="planner-member-id" style="border-bottom-color: transparent;border-right-color: transparent;"> <?= $index + 1 ?> </td> <?php $member = getMemberWithoutSystem($dayRaindrops[$index][0]); ?> - <td class="planner-link" style="width:50vw;" <?php if (!isset($dayRaindrops[$index][1])): ?>colspan="3" <?php else: ?>colspan="2"<?php endif; ?>> + <td class="planner-link" style="border-right-color:transparent;border-bottom-color: transparent;width:50vw;" <?php if (!isset($dayRaindrops[$index][1])): ?>colspan="3" <?php else: ?>colspan="2"<?php endif; ?>> <?php if ($member["name"] === "fusion"): ?> - <a class="member-link" onclick="openEditFronter('raindrops', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * $diff)) ?>')"><img src="/assets/logo/logo.png" style="width:24px;"> <span class="member-link-text"><span class="text-muted merge-desktop">Multiple merged members</span><span class="text-muted merge-mobile">Merge</span></span></a> + <a class="member-link" onclick="openEditFronter('raindrops', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * $diff)) ?>')"><span class="member-link-inner"><img src="/assets/logo/newlogo.png" style="filter:grayscale(1);width:24px;"> <span class="member-link-text"><span class="merge-desktop" style="opacity:.75;">Multiple merged members</span></span></span></a> + <?php elseif ($member["name"] === "unknown"): ?> + <a class="member-link" onclick="openEditFronter('raindrops', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * $diff)) ?>')"><span class="member-link-inner"><img src="/assets/logo/newlogo.png" style="filter:grayscale(1);width:24px;"> <span class="member-link-text"><span class="merge-desktop" style="opacity:.75;">Other/unknown/fallback pony</span></span></span></a> <?php else: ?> - <a class="member-link" onclick="openEditFronter('raindrops', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * $diff)) ?>')"><img src="<?= getAsset($member["system"], $member["id"], "heads") ?>" style="width:24px;"> <span class="member-link-text"><?= getMiniName($member["display_name"] ?? $member["name"]) ?></span></a> + <a class="member-link" onclick="openEditFronter('raindrops', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * $diff)) ?>')"><span class="member-link-inner"><img src="<?= getAsset($member["system"], $member["id"], "heads") ?>" style="width:24px;"> <span class="member-link-text"><?= !isset($dayRaindrops[$index][1]) ? $member["display_name"] ?? $member["name"] : getMiniName($member["display_name"] ?? $member["name"]) ?></span></span></a> <?php endif; ?> </td> <?php if (!isset($dayRaindrops[$index][1])): ?> - <td class="planner-cofronter-inner planner-link" style="width:5%;text-align:center;"> - <a onclick="addCofronter('raindrops', <?= $diff ?>, <?= $index ?>);" class="planner-add-link"> - <img src="/assets/icons/add.svg" alt="" class="planner-add-icon"> + <td class="planner-cofronter-inner planner-link" style="border-bottom-color: transparent;border-left-color: transparent;width:5%;text-align:center;"> + <a onclick="addCofronter('raindrops', <?= $diff ?>, <?= $index ?>);" class="planner-add-link planner-add-link-cofronter"> + <span class="planner-add-link-cofronter-inner"><img src="/assets/icons/add.svg" alt="" class="planner-add-icon"></span> </a> </td> <?php else: ?> <td class="planner-link" style="width:41.35vw;" colspan="2"> <?php $member = getMemberWithoutSystem($dayRaindrops[$index][1]); ?> <?php if ($member["name"] === "fusion"): ?> - <a class="member-link" onclick="openEditCofronter('raindrops', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * $diff)) ?>')"><img src="/assets/logo/logo.png" style="width:24px;"> <span class="member-link-text"><span class="text-muted merge-desktop">Multiple merged members</span><span class="text-muted merge-mobile">Merge</span></span></a> + <a class="member-link" onclick="openEditCofronter('raindrops', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * $diff)) ?>')"><span class="member-link-inner"><img src="/assets/logo/newlogo.png" style="filter:grayscale(1);width:24px;"> <span class="member-link-text"><span class="merge-desktop" style="opacity:.75;">Multiple merged members</span></span></span></a> + <?php elseif ($member["name"] === "unknown"): ?> + <a class="member-link" onclick="openEditCofronter('raindrops', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * $diff)) ?>')"><span class="member-link-inner"><img src="/assets/logo/newlogo.png" style="filter:grayscale(1);width:24px;"> <span class="member-link-text"><span class="merge-desktop" style="opacity:.75;">Other/unknown/fallback pony</span></span></span></a> <?php else: ?> - <a class="member-link" onclick="openEditCofronter('raindrops', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * $diff)) ?>')"><img src="<?= getAsset($member["system"], $member["id"], "heads") ?>" style="width:24px;"> <span class="member-link-text"><?= getMiniName($member["display_name"] ?? $member["name"]) ?></span></a> + <a class="member-link" onclick="openEditCofronter('raindrops', <?= $index ?>, '<?= date('Y-m-d', time() + (86400 * $diff)) ?>')"><span class="member-link-inner"><img src="<?= getAsset($member["system"], $member["id"], "heads") ?>" style="width:24px;"> <span class="member-link-text"><?= !isset($dayRaindrops[$index][1]) ? $member["display_name"] ?? $member["name"] : getMiniName($member["display_name"] ?? $member["name"]) ?></span></span></a> <?php endif; ?> </td> <?php endif; ?> <?php elseif ($index === count($dayRaindrops)): ?> - <td class="planner-add-inner planner-link" colspan="5"> + <td class="planner-add-inner planner-link" colspan="5" style="border-top-color: transparent;border-bottom-color: transparent;"> <a onclick="addFronter('raindrops', <?= $diff ?>, <?= $index ?>);" id="planner-add-link-raindrops-<?= $diff ?>" class="planner-add-link"> - <img src="/assets/icons/add.svg" alt="" class="planner-add-icon"> - <span class="planner-add-text">Add new fronter</span> + <div class="planner-add-link-inner"> + <img src="/assets/icons/add.svg" alt="" class="planner-add-icon"> + <span class="planner-add-text">Add new fronter</span> + </div> </a> </td> <?php else: ?> - <td colspan="5" class="planner-empty"></td> + <td colspan="5" class="planner-empty" style="border-top-color:transparent;border-bottom-color:transparent;"></td> <?php endif; ?> </tr> <?php $index++; endfor; ?> <tr class="planner-day planner-end-of-day" <?php if ($disabled): ?>style="opacity: .75; pointer-events: none;"<?php endif; ?>> - <td colspan="10"> - <?php if (count($dayCloudburst) > 0 && count($dayRaindrops) > 0): ?> - <?= getMiniName(getMemberWithoutSystem($dayCloudburst[count($dayCloudburst) - 1][0])["display_name"] ?? getMemberWithoutSystem($dayCloudburst[count($dayCloudburst) - 1][0])["name"]) ?> will sleep with <?= getMiniName(getMemberWithoutSystem($dayRaindrops[count($dayRaindrops) - 1][0])["display_name"] ?? getMemberWithoutSystem($dayRaindrops[count($dayRaindrops) - 1][0])["name"]) ?> - <?php else: ?> - Unable to calculate who will sleep with who - <?php endif; ?> + <td colspan="10" style="padding: 0;border:none;"> + <div style="padding: 5px 10px;border: 1px solid #404040;margin:-1px;border-bottom-left-radius: 10px;border-bottom-right-radius: 10px;"> + <?php if (count($dayCloudburst) > 0 && count($dayRaindrops) > 0): ?> + <?= getMiniName(getMemberWithoutSystem($dayCloudburst[count($dayCloudburst) - 1][0])["display_name"] ?? getMemberWithoutSystem($dayCloudburst[count($dayCloudburst) - 1][0])["name"]) ?> will sleep with <?= getMiniName(getMemberWithoutSystem($dayRaindrops[count($dayRaindrops) - 1][0])["display_name"] ?? getMemberWithoutSystem($dayRaindrops[count($dayRaindrops) - 1][0])["name"]) ?> + <?php else: ?> + Unable to calculate who will sleep with who + <?php endif; ?> + </div> </td> </tr> <tr class="planner-separator"></tr> @@ -358,6 +374,33 @@ function day($display, $diff): void { if ($diff < 0) $disabled = true; else $dis } } + .member-link, .planner-link { + background-color: transparent !important; + } + + .member-link-inner, .planner-add-link-inner, .planner-add-link-cofronter-inner { + display: block; + padding: 3px 7px; + border-radius: 5px; + } + + .planner-add-link-cofronter-inner { + border-radius: 100%; + } + + .planner-add-link-cofronter-inner .planner-add-icon { + margin-top: -2px; + } + + .member-link, .planner-add-link-inner, .planner-add-link-cofronter-inner { + padding: 2px 3px !important; + } + + .member-link:hover .member-link-inner, .planner-add-link:hover .planner-add-link-inner, .planner-add-link-cofronter:hover .planner-add-link-cofronter-inner { + background-color: rgba(255, 255, 255, .125); + /*background-color: rgba(255, 255, 255, .25);*/ + } + </style> <!--suppress JSUnresolvedVariable, JSUnresolvedFunction --> @@ -394,14 +437,16 @@ function day($display, $diff): void { if ($diff < 0) $disabled = true; else $dis let otherPony = window.fronting[window.addSystem === "gdapd" ? "cloudburst" : "raindrops"][window.currentWorkingDate][window.addIndex][0]; let availablePonies = window.relations[window.fronting[window.addSystem === "gdapd" ? "cloudburst" : "raindrops"][window.currentWorkingDate][window.addIndex][0]]; - document.getElementById("associated-results").innerHTML = ""; + if (availablePonies) { + document.getElementById("associated-results").innerHTML = ""; - for (let pony of availablePonies) { - document.getElementById("associated-results").innerHTML += document.getElementById("list-pony-" + pony).outerHTML; - } + for (let pony of availablePonies) { + document.getElementById("associated-results").innerHTML += document.getElementById("list-pony-" + pony).outerHTML; + } - document.getElementById("list").style.display = "none"; - document.getElementById("associated-results").style.display = ""; + document.getElementById("list").style.display = "none"; + document.getElementById("associated-results").style.display = ""; + } } } @@ -687,7 +732,8 @@ function day($display, $diff): void { if ($diff < 0) $disabled = true; else $dis <?php foreach (scoreOrderGlobal() as $member): ?> <a id="list-pony-<?= $member['id'] ?>" onclick="confirmFronterAdd(window.addSystem, '<?= $member['id'] ?>');" class="new-fronter-link member-link list-group-item list-group-item-action"><img src="<?= getAsset($member["system"], $member["id"], "heads") ?>" style="width:24px;"> <?= getMiniName($member["display_name"] ?? $member["name"]) ?><peh-schedule-add></peh-schedule-add></a> <?php endforeach; $member = getSystemMember("gdapd", "irxyh") ?> - <a onclick="confirmFronterAdd(window.addSystem, 'irxyh');" class="new-fronter-link member-link list-group-item list-group-item-action"><img src="/assets/logo/logo.png" style="width:24px;"> <span class="text-muted">Multiple merged members</span></a> + <a onclick="confirmFronterAdd(window.addSystem, 'irxyh');" class="new-fronter-link member-link list-group-item list-group-item-action"><img src="/assets/logo/newlogo.png" style="filter:grayscale(1);width:24px;"> <span style="opacity:.75">Multiple merged members</span></a> + <a onclick="confirmFronterAdd(window.addSystem, 'zdtsg');" class="new-fronter-link member-link list-group-item list-group-item-action"><img src="/assets/logo/newlogo.png" style="filter:grayscale(1);width:24px;"> <span style="opacity:.75">Other/unknown/fallback pony</span></a> </div> </div> diff --git a/includes/refresh.php b/includes/refresh.php index 18c01ed..fe01414 100644 --- a/includes/refresh.php +++ b/includes/refresh.php @@ -161,19 +161,31 @@ function getSystem(string $id) { echo(" Switches\n"); $currentOpStart = microtime(true); - echo(" Part 1/3\n"); + echo(" Part 1/6\n"); $switches1 = json_decode(file_get_contents("https://pluralkit.equestria.dev/v2/systems/$id/switches"), true); $oldest = $switches1[count($switches1) - 1]["timestamp"]; - echo(" Part 2/3\n"); + echo(" Part 2/6\n"); $switches2 = json_decode(file_get_contents("https://pluralkit.equestria.dev/v2/systems/$id/switches?before=$oldest"), true); $oldest = $switches2[count($switches2) - 1]["timestamp"]; - echo(" Part 3/3\n"); + echo(" Part 3/6\n"); $switches3 = json_decode(file_get_contents("https://pluralkit.equestria.dev/v2/systems/$id/switches?before=$oldest"), true); + $oldest = $switches3[count($switches3) - 1]["timestamp"]; - if ($switches1 !== null && $switches2 !== null && $switches3 !== null) { - file_put_contents("./data/$id/switches.json", json_encode([...$switches1, ...$switches2, ...$switches3], JSON_PRETTY_PRINT)); + echo(" Part 4/6\n"); + $switches4 = json_decode(file_get_contents("https://pluralkit.equestria.dev/v2/systems/$id/switches?before=$oldest"), true); + $oldest = $switches4[count($switches4) - 1]["timestamp"]; + + echo(" Part 5/6\n"); + $switches5 = json_decode(file_get_contents("https://pluralkit.equestria.dev/v2/systems/$id/switches?before=$oldest"), true); + $oldest = $switches5[count($switches5) - 1]["timestamp"]; + + echo(" Part 6/6\n"); + $switches6 = json_decode(file_get_contents("https://pluralkit.equestria.dev/v2/systems/$id/switches?before=$oldest"), true); + + if ($switches1 !== null && $switches2 !== null && $switches3 !== null && $switches4 !== null && $switches5 !== null && $switches6 !== null) { + file_put_contents("./data/$id/switches.json", json_encode([...$switches1, ...$switches2, ...$switches3, ...$switches4, ...$switches5, ...$switches6], JSON_PRETTY_PRINT)); $times["system-switches-$id"] = microtime(true) - $currentOpStart; } } diff --git a/includes/sysbanner.inc b/includes/sysbanner.inc index bafb10d..f9feb5c 100644 --- a/includes/sysbanner.inc +++ b/includes/sysbanner.inc @@ -82,7 +82,7 @@ $pages = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/pa <?php $travellers = array_filter(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/" . ($systemID === "gdapd" ? "ynmuc" : "gdapd") . "/members.json"), true), function ($i) use ($travelling) { - return $travelling[$i['id']]['travelling']; + return $travelling[$i['id']]['travelling'] && !$travelling[$i['id']]['equestria']; }); ?> diff --git a/includes/system/species.inc b/includes/system/species.inc index 41b06fe..8f4368e 100644 --- a/includes/system/species.inc +++ b/includes/system/species.inc @@ -9,11 +9,11 @@ function species(array $members, string $id, string $name) { global $systemID; g $members = [ ...array_map(function ($i) use ($systemID) { $i["_system"] = $systemID; return $i; },array_filter($members, function ($i) use ($systemID) { global $travelling; - return !$travelling[$i['id']]['travelling']; + return !($travelling[$i['id']]['travelling'] || (isset($travelling[$i['id']]['equestria']) && $travelling[$i['id']]['equestria'])); })), ...array_map(function ($i) use ($systemID) { $i["_system"] = $systemID === "gdapd" ? "ynmuc" : "gdapd"; return $i; }, array_filter(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/" . ($systemID === "gdapd" ? "ynmuc" : "gdapd") . "/members.json"), true), function ($i) use ($id, $systemID) { global $travelling; - return $travelling[$i['id']]['travelling'] && in_array($id, parseMetadata(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/" . $i['id'] . ".json"), true))["species"]); + return $travelling[$i['id']]['travelling'] && !$travelling[$i['id']]['equestria'] && in_array($id, parseMetadata(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/" . $i['id'] . ".json"), true))["species"]); })) ]; diff --git a/pages/bitset.inc b/pages/bitset.inc index d4239a7..c74fe64 100644 --- a/pages/bitset.inc +++ b/pages/bitset.inc @@ -31,7 +31,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; <br> <div class="container"> <div id="page-content"> - <h2>Bitset calculator</h2> + <h2 oncontextmenu="document.getElementById('deprecated-options').style.display='block';">Bitset calculator</h2> </div> <div style="display:grid; grid-template-columns: repeat(48, 1fr);"> <div data-bs-toggle="tooltip" title="Value reserved for future use" id="binary-bit-1" class="font-monospace tooltip-nohelp text-muted" style="text-align: center;cursor: pointer;">0</div> @@ -46,27 +46,27 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; <div data-bs-toggle="tooltip" title="Value reserved for future use" id="binary-bit-10" class="font-monospace tooltip-nohelp text-muted" style="text-align: center;cursor: pointer;">0</div> <div data-bs-toggle="tooltip" title="Persecutor" id="binary-bit-11" class="font-monospace tooltip-nohelp" style="color:#197387;text-align: center;cursor: pointer;">0</div> <div data-bs-toggle="tooltip" title="Leader" id="binary-bit-12" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#877e19;">0</div> - <div data-bs-toggle="tooltip" title="Age regressor (deprecated)" id="binary-bit-13" class="font-monospace tooltip-nohelp" style="color: #383838;text-align: center;cursor: pointer;">0</div> - <div data-bs-toggle="tooltip" title="Sexually active" id="binary-bit-14" class="font-monospace tooltip-nohelp" style="color: rgb(59,196,46);text-align: center;cursor: pointer;">0</div> + <div data-bs-toggle="tooltip" title="Value reserved for future use" id="binary-bit-13" class="text-muted font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;">0</div> + <div data-bs-toggle="tooltip" title="Preemptive sexual consent" id="binary-bit-14" class="font-monospace tooltip-nohelp" style="color: rgb(59,196,46);text-align: center;cursor: pointer;">0</div> <div data-bs-toggle="tooltip" title="Fronts less frequently" id="binary-bit-15" class="font-monospace tooltip-nohelp" style="color: #198754;text-align: center;cursor: pointer;">0</div> <div data-bs-toggle="tooltip" title="Non verbal in real life" id="binary-bit-16" class="font-monospace tooltip-nohelp" style="color: #20c997;text-align: center;cursor: pointer;">0</div> <div data-bs-toggle="tooltip" title="Eatable food" id="binary-bit-17" class="font-monospace tooltip-nohelp" style="color: #d63384;text-align: center;cursor: pointer;">0</div> <div data-bs-toggle="tooltip" title="Eatable food" id="binary-bit-18" class="font-monospace tooltip-nohelp" style="color: #d63384;text-align: center;cursor: pointer;">0</div> - <div data-bs-toggle="tooltip" title="Ability to use magic" id="binary-bit-19" class="font-monospace tooltip-nohelp" style="color: #fd7e14;text-align: center;cursor: pointer;">0</div> - <div data-bs-toggle="tooltip" title="Ability to use magic" id="binary-bit-20" class="font-monospace tooltip-nohelp" style="color: #fd7e14;text-align: center;cursor: pointer;">0</div> - <div data-bs-toggle="tooltip" title="Ability to use magic" id="binary-bit-21" class="font-monospace tooltip-nohelp" style="color: #fd7e14;text-align: center;cursor: pointer;">0</div> - <div data-bs-toggle="tooltip" title="Affectionative or sexual sensitivity" id="binary-bit-22" class="font-monospace tooltip-nohelp" style="color:#3340d0; text-align: center;cursor: pointer;">0</div> - <div data-bs-toggle="tooltip" title="Affectionative or sexual sensitivity" id="binary-bit-23" class="font-monospace tooltip-nohelp" style="color:#3340d0; text-align: center;cursor: pointer;">0</div> - <div data-bs-toggle="tooltip" title="Affectionative or sexual sensitivity" id="binary-bit-24" class="font-monospace tooltip-nohelp" style="color:#3340d0; text-align: center;cursor: pointer;">0</div> + <div data-bs-toggle="tooltip" title="Polyamory (romantic)" id="binary-bit-19" class="font-monospace tooltip-nohelp" style="color: #eeb8b8; text-align: center;cursor: pointer;">0</div> + <div data-bs-toggle="tooltip" title="Polyamory (sexual)" id="binary-bit-20" class="font-monospace tooltip-nohelp" style="color: #e2b8ee; text-align: center;cursor: pointer;">0</div> + <div data-bs-toggle="tooltip" title="Sexual alignment" id="binary-bit-21" class="font-monospace tooltip-nohelp" style="color: #5fff4d; text-align: center;cursor: pointer;">0</div> + <div data-bs-toggle="tooltip" title="Sexual alignment" id="binary-bit-22" class="font-monospace tooltip-nohelp" style="color: #5fff4d; text-align: center;cursor: pointer;">0</div> + <div data-bs-toggle="tooltip" title="Sexual alignment" id="binary-bit-23" class="font-monospace tooltip-nohelp" style="color: #5fff4d; text-align: center;cursor: pointer;">0</div> + <div data-bs-toggle="tooltip" title="Sexual alignment" id="binary-bit-24" class="font-monospace tooltip-nohelp" style="color: #5fff4d; text-align: center;cursor: pointer;">0</div> <div data-bs-toggle="tooltip" title="Shared memory access" id="binary-bit-25" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#fa77ef;">0</div> <div data-bs-toggle="tooltip" title="Shared memory access" id="binary-bit-26" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#fa77ef;">0</div> - <div data-bs-toggle="tooltip" title="Median system" id="binary-bit-27" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color: #a14700;">0</div> + <div data-bs-toggle="tooltip" title="Value reserved for future use" id="binary-bit-27" class="text-muted font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;">0</div> <div data-bs-toggle="tooltip" title="Little/younger" id="binary-bit-28" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#b277fa;">0</div> <div data-bs-toggle="tooltip" title="Little/younger" id="binary-bit-29" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#b277fa;">0</div> <div data-bs-toggle="tooltip" title="Protector" id="binary-bit-30" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#77faab;">0</div> <div data-bs-toggle="tooltip" title="Fictive" id="binary-bit-31" class="font-monospace tooltip-nohelp text-info" style="text-align: center;cursor: pointer;">0</div> - <div data-bs-toggle="tooltip" title="Not talking" id="binary-bit-32" class="font-monospace tooltip-nohelp text-danger" style="text-align: center;cursor: pointer;">0</div> - <div data-bs-toggle="tooltip" title="Most common fronter (deprecated)" id="binary-bit-33" class="font-monospace tooltip-nohelp" style="color:#383838;text-align: center;cursor: pointer;">0</div> + <div data-bs-toggle="tooltip" title="Value reserved for future use" id="binary-bit-32" class="text-muted font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;">0</div> + <div data-bs-toggle="tooltip" title="Value reserved for future use" id="binary-bit-33" class="text-muted font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;">0</div> <div data-bs-toggle="tooltip" title="1st species" id="binary-bit-34" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#fab277;">0</div> <div data-bs-toggle="tooltip" title="1st species" id="binary-bit-35" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#fab277;">0</div> <div data-bs-toggle="tooltip" title="1st species" id="binary-bit-36" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#fab277;">0</div> @@ -75,18 +75,18 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; <div data-bs-toggle="tooltip" title="2nd species" id="binary-bit-39" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#faf377;">0</div> <div data-bs-toggle="tooltip" title="2nd species" id="binary-bit-40" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#faf377;">0</div> <div data-bs-toggle="tooltip" title="2nd species" id="binary-bit-41" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#faf377;">0</div> - <div data-bs-toggle="tooltip" title="3rd species" id="binary-bit-42" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#abfa77;">0</div> - <div data-bs-toggle="tooltip" title="3rd species" id="binary-bit-43" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#abfa77;">0</div> - <div data-bs-toggle="tooltip" title="3rd species" id="binary-bit-44" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#abfa77;">0</div> - <div data-bs-toggle="tooltip" title="3rd species" id="binary-bit-45" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#abfa77;">0</div> + <div data-bs-toggle="tooltip" title="Romantic alignment" id="binary-bit-42" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color: #4d5cff;">0</div> + <div data-bs-toggle="tooltip" title="Romantic alignment" id="binary-bit-43" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color: #4d5cff;">0</div> + <div data-bs-toggle="tooltip" title="Romantic alignment" id="binary-bit-44" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color: #4d5cff;">0</div> + <div data-bs-toggle="tooltip" title="Romantic alignment" id="binary-bit-45" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color: #4d5cff;">0</div> <div data-bs-toggle="tooltip" title="Robot" id="binary-bit-46" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;">0</div> <div data-bs-toggle="tooltip" title="Plush" id="binary-bit-47" class="font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;color:#6610f2;">0</div> - <div data-bs-toggle="tooltip" title="Affected by age spells (deprecated)" id="binary-bit-48" class="font-monospace tooltip-nohelp" style="color:#383838;text-align: center;cursor: pointer;">0</div> + <div data-bs-toggle="tooltip" title="Value reserved for future use" id="binary-bit-48" class="text-muted font-monospace tooltip-nohelp" style="text-align: center;cursor: pointer;">0</div> </div> <br> <p> - <b>Input:</b> <input onchange="calculateInput();" onkeydown="calculateInput()" onkeyup="calculateInput()" value="2048" id="input" type="number" class="form-control" style="width:256px;display: inline-block;color:white;background:#111;border-color:#222;" maxlength="15" max="281474976710655" min="2048"><br> - <b>Output:</b> <code id="output-bin">0b000000000000000000000000000000000000100000000000</code>, <code id="output-hex">0x000008000000</code>, <code id="output-dec">2048</code> + <input onchange="calculateInput();" onkeydown="calculateInput()" onkeyup="calculateInput()" value="2048" id="input" type="number" class="form-control" style="width:256px;display: inline-block;color:white;background:#111;border-color:#222;" maxlength="15" max="281474976710655" min="2048"><br> + <code id="output-bin">0b000000000000000000000000000000000000100000000000</code>, <code id="output-hex">0x000008000000</code>, <code id="output-dec">2048</code> </p> <script> for (let i = 1; i <= 48; i++) { @@ -113,130 +113,112 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; } } - let sharedMemory = parseInt(binString.substring(8 + 16, 10 + 16), 2); - let median = binString.substring(10 + 16, 11 + 16) !== "0"; - let little = parseInt(binString.substring(11 + 16, 13 + 16), 2); - let protector = binString.substring(13 + 16, 14 + 16) !== "0"; - let fictive = binString.substring(14 + 16, 15 + 16) !== "0"; - let notTalking = binString.substring(15 + 16, 16 + 16) !== "0"; - let host = binString.substring(16 + 16, 17 + 16) !== "0"; - let robot = binString.substring(29 + 16, 30 + 16) !== "0"; - let plush = binString.substring(30 + 16, 31 + 16) !== "0"; - let species1 = binString.substring(17 + 16, 21 + 16); - let species2 = binString.substring(21 + 16, 25 + 16); - let species3 = binString.substring(25 + 16, 29 + 16); - let food = parseInt(binString.substring(16, 2 + 16), 2); - let magic = parseInt(binString.substring(2 + 16, 5 + 16), 2); - let sensitivity = parseInt(binString.substring(5 + 16, 8 + 16), 2); - let ageSpells = binString.substring(31 + 16, 32 + 16) !== "0"; - let nonverbal = binString.substring(15, 16) !== "0"; - let lessFrequent = binString.substring(14, 15) !== "0"; - let sexuallyActive = binString.substring(13, 14) !== "0"; - let ageRegressor = binString.substring(12, 13) !== "0"; - let leader = binString.substring(11, 12) !== "0"; - let persecutor = binString.substring(10, 11) !== "0"; - - document.getElementById("value-0").value = sharedMemory; - document.getElementById("value-1").value = little; - document.getElementById("value-2").value = species1; - document.getElementById("value-3").value = species2; - document.getElementById("value-4").checked = median; - document.getElementById("value-5").checked = protector; - document.getElementById("value-6").checked = fictive; - document.getElementById("value-7").checked = notTalking; - document.getElementById("value-8").checked = host; - document.getElementById("value-9").checked = robot; - document.getElementById("value-10").checked = plush; - document.getElementById("value-11").value = food; - document.getElementById("value-12").value = magic; - document.getElementById("value-13").value = sensitivity; - document.getElementById("value-14").value = species3; - document.getElementById("value-15").checked = ageSpells; - document.getElementById("value-16").checked = nonverbal; - document.getElementById("value-17").checked = lessFrequent; - document.getElementById("value-18").checked = sexuallyActive; - document.getElementById("value-19").checked = ageRegressor; - document.getElementById("value-20").checked = leader; - document.getElementById("value-21").checked = persecutor; + display(binString); + setColors(); } - } - - function calculateOutput() { - let bin = [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null].map((_, i) => { - let e = i + 1; - return document.getElementById("binary-bit-" + e).innerText; - }).join(""); - document.getElementById("output-bin").innerText = "0b" + bin; - - let hex = parseInt(bin, 2).toString(16); - - document.getElementById("output-hex").innerText = "0x" + "0".repeat(12 - hex.length) + hex; - document.getElementById("output-dec").innerText = parseInt(bin, 2).toString(); - document.getElementById("input").value = parseInt(bin, 2).toString(); + setColors() } - function calculateInput() { - if (parseInt(document.getElementById("input").value).toString().length > 15) return; - - let binString = ("0".repeat(48 - parseInt(document.getElementById("input").value).toString(2).length)) + parseInt(document.getElementById("input").value).toString(2); - let bin = binString.split("").map((i) => parseInt(i)); - + function setColors() { for (let i = 1; i <= 48; i++) { - if (bin[i - 1]) { - document.getElementById("binary-bit-" + i).innerText = bin[i - 1].toString(); + let e = document.getElementById("binary-bit-" + i); + if (e.title !== "Value reserved for future use") { + e.style.color = ""; + + e.classList.remove("text-info"); + e.classList.remove("text-warning"); + e.classList.remove("text-light"); + e.classList.remove("text-dark"); + + if (e.innerText === "0") { + e.classList.remove("text-success"); + e.classList.add("text-danger"); + } else { + e.classList.add("text-success"); + e.classList.remove("text-danger"); + } } else { - document.getElementById("binary-bit-" + i).innerText = "0"; + e.style.cursor = "not-allowed"; + e.onclick = null; } } + } + function display(binString) { let sharedMemory = parseInt(binString.substring(8 + 16, 10 + 16), 2); - let median = binString.substring(10 + 16, 11 + 16) !== "0"; let little = parseInt(binString.substring(11 + 16, 13 + 16), 2); let protector = binString.substring(13 + 16, 14 + 16) !== "0"; let fictive = binString.substring(14 + 16, 15 + 16) !== "0"; - let notTalking = binString.substring(15 + 16, 16 + 16) !== "0"; - let host = binString.substring(16 + 16, 17 + 16) !== "0"; let robot = binString.substring(29 + 16, 30 + 16) !== "0"; let plush = binString.substring(30 + 16, 31 + 16) !== "0"; - let ageSpells = binString.substring(31 + 16, 32 + 16) !== "0"; let species1 = binString.substring(17 + 16, 21 + 16); let species2 = binString.substring(21 + 16, 25 + 16); - let species3 = binString.substring(25 + 16, 29 + 16); let food = parseInt(binString.substring(16, 2 + 16), 2); let nonverbal = binString.substring(15, 16) !== "0"; let lessFrequent = binString.substring(14, 15) !== "0"; - let magic = parseInt(binString.substring(2 + 16, 5 + 16), 2); - let sensitivity = parseInt(binString.substring(5 + 16, 8 + 16), 2); let sexuallyActive = binString.substring(13, 14) !== "0"; - let ageRegressor = binString.substring(12, 13) !== "0"; let leader = binString.substring(11, 12) !== "0"; let persecutor = binString.substring(10, 11) !== "0"; + let polyamorous1 = binString.substring(18, 19) !== "0"; + let polyamorous2 = binString.substring(19, 20) !== "0"; + let alignment1 = binString.substring(20, 24); + let alignment2 = binString.substring(25 + 16, 29 + 16); document.getElementById("value-0").value = sharedMemory; document.getElementById("value-1").value = little; document.getElementById("value-2").value = species1; document.getElementById("value-3").value = species2; - document.getElementById("value-11").value = food; - document.getElementById("value-12").value = magic; - document.getElementById("value-13").value = sensitivity; - document.getElementById("value-4").checked = median; document.getElementById("value-5").checked = protector; document.getElementById("value-6").checked = fictive; - document.getElementById("value-7").checked = notTalking; - document.getElementById("value-8").checked = host; document.getElementById("value-9").checked = robot; document.getElementById("value-10").checked = plush; - document.getElementById("value-14").value = species3; - document.getElementById("value-15").checked = ageSpells; + document.getElementById("value-11").value = food; document.getElementById("value-16").checked = nonverbal; document.getElementById("value-17").checked = lessFrequent; document.getElementById("value-18").checked = sexuallyActive; - document.getElementById("value-19").checked = ageRegressor; document.getElementById("value-20").checked = leader; document.getElementById("value-21").checked = persecutor; + document.getElementById("value-22").checked = polyamorous1; + document.getElementById("value-23").checked = polyamorous2; + document.getElementById("value-24").value = alignment1; + document.getElementById("value-25").value = alignment2; + + setColors(); + } + + function calculateOutput() { + let bin = [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null].map((_, i) => { + let e = i + 1; + return document.getElementById("binary-bit-" + e).innerText; + }).join(""); + + document.getElementById("output-bin").innerText = "0b" + bin; + + let hex = parseInt(bin, 2).toString(16); + + document.getElementById("output-hex").innerText = "0x" + "0".repeat(12 - hex.length) + hex; + document.getElementById("output-dec").innerText = parseInt(bin, 2).toString(); + document.getElementById("input").value = parseInt(bin, 2).toString(); + } + function calculateInput() { + if (parseInt(document.getElementById("input").value).toString().length > 15) return; + + let binString = ("0".repeat(48 - parseInt(document.getElementById("input").value).toString(2).length)) + parseInt(document.getElementById("input").value).toString(2); + let bin = binString.split("").map((i) => parseInt(i)); + + for (let i = 1; i <= 48; i++) { + if (bin[i - 1]) { + document.getElementById("binary-bit-" + i).innerText = bin[i - 1].toString(); + } else { + document.getElementById("binary-bit-" + i).innerText = "0"; + } + } + + display(binString); calculateOutput(); + setColors(); return bin; } @@ -246,24 +228,20 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; let val1 = document.getElementById("value-1").value; let val2 = document.getElementById("value-2").value; let val3 = document.getElementById("value-3").value; - let val4 = document.getElementById("value-4").checked; let val5 = document.getElementById("value-5").checked; let val6 = document.getElementById("value-6").checked; - let val7 = document.getElementById("value-7").checked; - let val8 = document.getElementById("value-8").checked; let val9 = document.getElementById("value-9").checked; let val10 = document.getElementById("value-10").checked; let val11 = document.getElementById("value-11").value; - let val12 = document.getElementById("value-12").value; - let val13 = document.getElementById("value-13").value; - let val14 = document.getElementById("value-14").value; - let val15 = document.getElementById("value-15").checked; let val16 = document.getElementById("value-16").checked; let val17 = document.getElementById("value-17").checked; let val18 = document.getElementById("value-18").checked; - let val19 = document.getElementById("value-19").checked; let val20 = document.getElementById("value-20").checked; let val21 = document.getElementById("value-21").checked; + let val22 = document.getElementById("value-22").checked; + let val23 = document.getElementById("value-23").checked; + let val24 = document.getElementById("value-24").value; + let val25 = document.getElementById("value-25").value; let val0bin = parseInt(val0).toString(2); val0bin = val0bin.length === 1 ? "0" + val0bin : val0bin; @@ -274,32 +252,30 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; let val11bin = parseInt(val11).toString(2); val11bin = val11bin.length === 1 ? "0" + val11bin : val11bin; - let val12bin = parseInt(val12).toString(2); - val12bin = val12bin.length === 1 ? "00" + val12bin : (val12bin.length === 2 ? "0" + val12bin : val12bin); - - let val13bin = parseInt(val13).toString(2); - val13bin = val13bin.length === 1 ? "00" + val13bin : (val13bin.length === 2 ? "0" + val13bin : val13bin); - let val2bin = val2; let val3bin = val3; - let val14bin = val14; + let val24bin = val24; + let val25bin = val25; + + let val4bin = "0"; + let val7bin = "0"; + let val8bin = "0"; + let val15bin = "0"; + let val19bin = "0"; - let val4bin = val4 ? "1" : "0"; let val5bin = val5 ? "1" : "0"; let val6bin = val6 ? "1" : "0"; - let val7bin = val7 ? "1" : "0"; - let val8bin = val8 ? "1" : "0"; let val9bin = val9 ? "1" : "0"; let val10bin = val10 ? "1" : "0"; - let val15bin = val15 ? "1" : "0"; let val16bin = val16 ? "1" : "0"; let val17bin = val17 ? "1" : "0"; let val18bin = val18 ? "1" : "0"; - let val19bin = val19 ? "1" : "0"; let val20bin = val20 ? "1" : "0"; let val21bin = val21 ? "1" : "0"; + let val22bin = val22 ? "1" : "0"; + let val23bin = val23 ? "1" : "0"; - let bin = "0000000000" + val21bin + val20bin + val19bin + val18bin + val17bin + val16bin + val11bin + val12bin + val13bin + val0bin + val4bin + val1bin + val5bin + val6bin + val7bin + val8bin + val2bin + val3bin + val14bin + val9bin + val10bin + val15bin; + let bin = "0000000000" + val21bin + val20bin + val19bin + val18bin + val17bin + val16bin + val11bin + val22bin + val23bin + val24bin + val0bin + val4bin + val1bin + val5bin + val6bin + val7bin + val8bin + val2bin + val3bin + val25bin + val9bin + val10bin + val15bin; console.log(bin, parseInt(bin, 2)); @@ -314,85 +290,107 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; } calculateOutput(); + setColors(); } </script> + <table style="margin-bottom: 1rem;"> + <tbody> + <tr> + <td style="padding-right: 10px; text-align: right;"> + <b>Shared memory access: </b> + </td> + <td> + <select class="tooltip-nohelp form-select" style='display:inline-block;width:max-content;color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");' id="value-0" onchange="updateFromSelection();"> + <option value="2">Full direct access</option> + <option value="1">Partial direct access</option> + <option value="0" selected>No direct access</option> + </select> + </td> + </tr><tr> + <td style="padding-right: 10px; text-align: right;"> + <b>Food: </b> + </td> + <td> + <select class="tooltip-nohelp form-select" style='display:inline-block;width:max-content;color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");' id="value-11" onchange="updateFromSelection();"> + <option value="0" selected>Doesn't need to eat</option> + <option value="1">Can't eat fish or meat</option> + <option value="2">Can't eat meat</option> + <option value="3">Can eat everything</option> + </select> + </td> + </tr><tr> + <td style="padding-right: 10px; text-align: right;"> + <b>Little/younger: </b> + </td> + <td> + <select class="tooltip-nohelp form-select" style='display:inline-block;width:max-content;color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");' id="value-1" onchange="updateFromSelection();"> + <option value="0" selected>None</option> + <option value="2">Little</option> + <option value="3">Younger</option> + <option value="1" disabled>Age regressor (old)</option> + </select> + </td> + </tr><tr> + <td style="padding-right: 10px; text-align: right;"> + <b>Species: </b> + </td> + <td> + <select class="tooltip-nohelp form-select" style='display:inline-block;width:max-content;color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");' id="value-2" onchange="updateFromSelection();"> + <option value="0000" disabled>None</option> + <option value="0001">Earth pony</option> + <option value="0010">Unicorn</option> + <option value="0011">Pegasus</option> + <option value="0100">Alicorn</option> + <option value="0101">Bat pony</option> + <option value="0110">Crystal pony</option> + <option value="0111">Changeling</option> + </select> + <select class="tooltip-nohelp form-select" style='display:inline-block;width:max-content;color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");' id="value-3" onchange="updateFromSelection();"> + <option value="0000">None</option> + <option value="0001">Earth pony</option> + <option value="0010">Unicorn</option> + <option value="0011">Pegasus</option> + <option value="0100">Alicorn</option> + <option value="0101">Bat pony</option> + <option value="0110">Crystal pony</option> + <option value="0111">Changeling</option> + </select> + <select class="tooltip-nohelp form-select" style='display:none;width:max-content;color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");' id="value-14" onchange="updateFromSelection();"> + <option value="0000">None</option> + <option value="0001">Earth pony</option> + <option value="0010">Unicorn</option> + <option value="0011">Pegasus</option> + <option value="0100">Alicorn</option> + <option value="0101">Bat pony</option> + <option value="0110">Crystal pony</option> + <option value="0111">Changeling</option> + </select> + </td> + </tr><tr> + <td style="padding-right: 10px; text-align: right;"> + <b>Alignment: </b> + </td> + <td> + <select class="tooltip-nohelp form-select" style='display:inline-block;width:max-content;color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");' id="value-24" onchange="updateFromSelection();"> + <option value="0000">Asexual</option> + <option value="0001">Heterosexual</option> + <option value="0010">Homosexual</option> + <option value="0011">Bisexual</option> + <option value="0100">Pansexual</option> + </select> + <select class="tooltip-nohelp form-select" style='display:inline-block;width:max-content;color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");' id="value-25" onchange="updateFromSelection();"> + <option value="0000">Aromantic</option> + <option value="0001">Heteroromantic</option> + <option value="0010">Homoromantic</option> + <option value="0011">Biromantic</option> + <option value="0100">Panromantic</option> + </select> + </td> + </tr> + </tbody> + </table> <p> - <b>Shared memory access: </b><select class="tooltip-nohelp form-select" style='display:inline-block;width:max-content;color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");' id="value-0" onchange="updateFromSelection();"> - <option value="2">Full direct access</option> - <option value="1">Partial direct access</option> - <option value="0" selected>No direct access</option> - </select> - <br> - <b>Food: </b><select class="tooltip-nohelp form-select" style='display:inline-block;width:max-content;color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");' id="value-11" onchange="updateFromSelection();"> - <option value="0" selected>Doesn't need to eat</option> - <option value="1">Can't eat fish or meat</option> - <option value="2">Can't eat meat</option> - <option value="3">Can eat everything</option> - </select> - <br> - <b>Little/younger: </b><select class="tooltip-nohelp form-select" style='display:inline-block;width:max-content;color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");' id="value-1" onchange="updateFromSelection();"> - <option value="0" selected>None</option> - <option value="2">Little</option> - <option value="3">Younger</option> - <option value="1" disabled>Age regressor (old)</option> - </select> - <br> - <b>Species: </b><select class="tooltip-nohelp form-select" style='display:inline-block;width:max-content;color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");' id="value-2" onchange="updateFromSelection();"> - <option value="0000" disabled>None</option> - <option value="0001">Earth pony</option> - <option value="0010">Unicorn</option> - <option value="0011">Pegasus</option> - <option value="0100">Alicorn</option> - <option value="0101">Bat pony</option> - <option value="0110">Crystal pony</option> - <option value="0111">Changeling</option> - </select> - <select class="tooltip-nohelp form-select" style='display:inline-block;width:max-content;color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");' id="value-3" onchange="updateFromSelection();"> - <option value="0000">None</option> - <option value="0001">Earth pony</option> - <option value="0010">Unicorn</option> - <option value="0011">Pegasus</option> - <option value="0100">Alicorn</option> - <option value="0101">Bat pony</option> - <option value="0110">Crystal pony</option> - <option value="0111">Changeling</option> - </select> - <select class="tooltip-nohelp form-select" style='display:inline-block;width:max-content;color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");' id="value-14" onchange="updateFromSelection();"> - <option value="0000">None</option> - <option value="0001">Earth pony</option> - <option value="0010">Unicorn</option> - <option value="0011">Pegasus</option> - <option value="0100">Alicorn</option> - <option value="0101">Bat pony</option> - <option value="0110">Crystal pony</option> - <option value="0111">Changeling</option> - </select> - <br> - <div style="display: none;"> - <b>Magic: </b><select class="tooltip-nohelp form-select" style='display:inline-block;width:max-content;color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");' id="value-12" onchange="updateFromSelection();"> - <option value="0" selected>Can't use magic</option> - <option value="1" disabled>Can use magic in some cases (deprecated)</option> - <option value="2">Can use magic with a horn</option> - <option value="3">Can use magic with wings</option> - <option value="4">Can use magic with wings and a horn</option> - <option value="5">Can use magic with another part of the body</option> - </select> - <br> - </div> - <b>Sensitivity: </b><select class="tooltip-nohelp form-select" style='display:inline-block;width:max-content;color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");' id="value-13" onchange="updateFromSelection();"> - <option value="0" selected>None</option> - <option value="1" disabled>May be sensitive (deprecated)</option> - <option value="2">Affectionately sensitive</option> - <option value="3">Sexually sensitive</option> - <option value="4" disabled>Sensitive in both ways (deprecated)</option> - </select> - </p> - <p> - <label style="margin-bottom:5px;"> - <input type="checkbox" id="value-4" onchange="updateFromSelection();"> - Part of a median system - </label><br> <label style="margin-bottom:5px;"> <input type="checkbox" id="value-5" onchange="updateFromSelection();"> Protector @@ -407,11 +405,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; </label><br> <label style="margin-bottom:5px;"> <input type="checkbox" id="value-18" onchange="updateFromSelection();"> - Sexually active - </label><br> - <label style="margin-bottom:5px;"> - <input type="checkbox" id="value-7" onchange="updateFromSelection();"> - Not talking + Preemptive sexual consent </label><br> <label style="margin-bottom:5px;"> <input type="checkbox" id="value-9" onchange="updateFromSelection();"> @@ -422,6 +416,14 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; Plush </label><br> <label style="margin-bottom:5px;"> + <input type="checkbox" id="value-22" onchange="updateFromSelection();"> + Polyamorous (romantic) + </label><br> + <label style="margin-bottom:5px;"> + <input type="checkbox" id="value-23" onchange="updateFromSelection();"> + Polyamorous (sexual) + </label><br> + <label style="margin-bottom:5px;"> <input type="checkbox" id="value-16" onchange="updateFromSelection();"> Non verbal in real life </label><br> @@ -435,26 +437,14 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; </label><br> </p> - <hr> - <details> - <summary>Show deprecated options</summary> - - <br> - <p> - <label style="margin-bottom:5px;"> - <input type="checkbox" id="value-8" onchange="updateFromSelection();"> - Most common fronter - </label><img alt="" src="/assets/icons/visibility-public.svg" style="filter:invert(1);width:24px;margin-left:5px;margin-top:-5px;" title="This information will be shown publicly" data-bs-toggle="tooltip"><br> - <label style="margin-bottom:5px;"> - <input type="checkbox" id="value-15" onchange="updateFromSelection();"> - Affected by age spells - </label><img alt="" src="/assets/icons/visibility-private.svg" style="filter:invert(1);width:24px;margin-left:5px;margin-top:-5px;" title="This information will remain private" data-bs-toggle="tooltip"><br> - <label style="margin-bottom:5px;"> - <input type="checkbox" id="value-19" onchange="updateFromSelection();"> - Age regressor - </label><img alt="" src="/assets/icons/visibility-public.svg" style="filter:invert(1);width:24px;margin-left:5px;margin-top:-5px;" title="This information will be shown publicly" data-bs-toggle="tooltip"><br> - </p> - </details> + <div id="deprecated-options" style="display: none;"> + <hr> + <details> + <summary>Show deprecated options</summary> + + <p>Nothing here lol</p> + </details> + </div> </div> <?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/footer.inc'; ?> diff --git a/pages/page.inc b/pages/page.inc index d13dd2f..f3859d3 100644 --- a/pages/page.inc +++ b/pages/page.inc @@ -38,11 +38,13 @@ $systemID = $system === "cloudburst" ? "ynmuc" : "gdapd"; if ($member === null) { global $_SystemName; $_SystemName = $system; + $pageFile = $_SERVER['DOCUMENT_ROOT'] . '/includes/system.inc'; require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/system.inc'; } else if ($member === "-" && isset($parts[2])) { if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/includes/system/' . $parts[2] . '.inc')) { global $_SystemPage; $_SystemPage = $parts[2]; + $pageFile = $_SERVER['DOCUMENT_ROOT'] . '/includes/system/' . $parts[2] . '.inc'; require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/system/' . $parts[2] . '.inc'; } else { header("Location: /?error=" . $lang["page"]["system"] . " " . $parts[2]) and die(); @@ -89,6 +91,7 @@ if ($member === null) { if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/includes/member/' . $parts[3] . '.inc')) { global $_MemberPage; $_MemberPage = $parts[3]; + $pageFile = $_SERVER['DOCUMENT_ROOT'] . '/includes/member/' . $parts[3] . '.inc'; require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/member/' . $parts[3] . '.inc'; } else { header("Location: /?error=" . $lang["page"]["system"] . " " . $parts[3]) and die(); @@ -98,6 +101,7 @@ if ($member === null) { global $_MemberName; $_MemberName = $member; + $pageFile = $_SERVER['DOCUMENT_ROOT'] . '/includes/member.inc'; require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/member.inc'; } diff --git a/pages/relations.inc b/pages/relations.inc index ce0d65c..8d14fe7 100644 --- a/pages/relations.inc +++ b/pages/relations.inc @@ -15,8 +15,8 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; } else { return true; } - })) as $member): if (count($member["_metadata"]["marefriends"]) > 0 || count($member["_metadata"]["sisters"]) > 0 || count($member["_metadata"]["caretakers"]) > 0): ?> - <div class="relation" style="background-color:rgba(255, 255, 255, .1);margin-bottom:10px;padding:10px;border-radius:10px;display:grid;grid-template-columns: 1.5fr <?= $member["_metadata"]["little"] === 2 ? "2fr 2fr 2fr" : "3fr 3fr" ?>;"> + })) as $member): ?> + <div class="relation" style="background-color:rgba(255, 255, 255, .1);margin-bottom:10px;padding:10px;border-radius:10px;display:grid;grid-template-columns: 1.5fr 2fr 2fr 2fr;"> <a class="relation-intro" style="background-color:rgba(255, 255, 255, .05);border-right:1px solid rgba(255, 255, 255, .1);margin:-10px;padding:10px;border-top-left-radius:10px;border-bottom-left-radius:10px;color: white;text-decoration: none;" href="/<?= $member["name"] ?>"> <img src="<?= getAsset($member["system"], $member["id"], "heads") ?>" style="width:24px;"> <?= $member["display_name"] ?? $member["name"] ?> </a> @@ -55,8 +55,8 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; </tr></tbody> </table> - <?php if ($member["_metadata"]["little"] === 2): ?> - <table class="relation-item relation-item-sisters" style="padding:0 20px;"> + <?php if ($member["_metadata"]["little"] >= 2): ?> + <table class="relation-item relation-item-caretakers" style="padding:0 20px;"> <tbody><tr> <td style="width: 50%;text-align:right;"> <b style="padding-right:5px;"><?= $lang["relations"]["caretakers"] ?></b><span class="list-separator-mobile"><br></span> @@ -74,7 +74,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; </table> <?php endif; ?> </div> - <?php endif; endforeach; ?> + <?php endforeach; ?> </div> <style> diff --git a/pages/rules-old.inc b/pages/rules-old.inc index 1fd7f94..a567bc5 100644 --- a/pages/rules-old.inc +++ b/pages/rules-old.inc @@ -1,253 +1,4 @@ <?php -require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/init.inc"; global $title; global $isLoggedIn; global $lang; global $pages; - -if (isset($_POST["updateRules"])) { - header("Content-Type: text/plain"); - - if (!isset($_POST['payload'])) { - header("Location: /-/rules"); - die(); - } - - foreach ($_POST['payload'] as $index => $rule) { - if (!isset($rule["name"]) || !isset($rule["content"]) && !is_numeric($index)) { - header("Location: /-/rules"); - die(); - } - - if (trim($rule["name"]) === "") { - unset($_POST["payload"][$index]); - continue; - } - - if (!isset($rule["approved"])) $rule["approved"] = []; - if (isset($rule["approved"][0])) $rule["approved"][0] = true; else $rule["approved"][0] = false; - if (isset($rule["approved"][1])) $rule["approved"][1] = true; else $rule["approved"][1] = false; - - $_POST["payload"][$index] = $rule; - } - - file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/rules/rules.json", utf8_encode(json_encode($_POST["payload"]))); - - header("Location: /-/rules"); - die(); -} - -require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; - -?> - -<br> -<div class="container"> - <div id="page-content"> - <h2>Systems rules</h2> - <p>Click on a rule to view additional details. <a onclick="event.target.blur();" href="#" data-bs-toggle="modal" data-bs-target="#editor">Edit rules</a></p> - - <?php - - $rules = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/rules/rules.json"), true); - - $protectorCloudburst = array_values(array_filter(scoreOrderGlobal(), function ($i) { - return $i["_system"] === "ynmuc" && $i["_metadata"]["protector"]; - }))[0]; - $protectorRaindrops = array_values(array_filter(scoreOrderGlobal(), function ($i) { - return $i["_system"] === "gdapd" && $i["_metadata"]["protector"]; - }))[0]; - - $pcName = getMiniName($protectorCloudburst["display_name"] ?? $protectorCloudburst["name"]); - $prName = getMiniName($protectorRaindrops["display_name"] ?? $protectorRaindrops["name"]); - - ?> - - <ul class="list-group"> - <?php $index = 1; foreach ($rules as $rule): ?> - <li class="list-group-item rule-outer"> - <details> - <summary class="rule"> - <b><?= $index ?>. <?= strip_tags($rule["name"]) ?></b> - <?php if (in_array(false, $rule["approved"])): ?> - <span class="badge bg-warning text-black rounded-pill">Unapproved</span> - <?php endif; ?> - </summary> - <?php if (in_array(false, $rule["approved"])): ?> - <div style="margin-top:10px;" class="alert alert-warning"> - <b>This rule has not yet been approved.</b> All rules need to be approved by the leaders from both systems. This rule is still missing approval from <?php - - if ($rule["approved"][0] === false) { - if ($rule["approved"][1] === false) { - echo($pcName . " and " . $prName); - } else { - echo($pcName); - } - } else if ($rule["approved"][1] === false) { - echo($prName); - } - - ?>. - </div> - <?php endif; ?> - <div <?= !in_array(false, $rule["approved"]) ? 'style="margin-top:10px;"' : '' ?> class="list-group-item"> - <?= strip_tags($rule["content"]) ?> - </div> - </details> - </li> - <?php $index++; endforeach; ?> - </ul> - </div> -</div> - -<div class="modal fade" id="editor"> - <div class="modal-dialog"> - <div class="modal-content"> - <div class="modal-header"> - <h4 class="modal-title">Rules editor</h4> - <button type="button" class="btn-close" data-bs-dismiss="modal"></button> - </div> - - <div class="modal-body"> - <p>Rules with an empty name are automatically deleted. Buttons to add new rules and save the changes are at the bottom of the list.</p> - <hr> - <form method="post"> - <div id="rules-editor"> - <?php $index = 1; foreach ($rules as $rule): ?> - <div <?= $index === 1 ? 'id="default-rule"' : '' ?>> - <p><b <?= $index === 1 ? 'id="default-rule--number"' : '' ?>>Rule #<?= $index ?>:</b></p> - <input <?= $index === 1 ? 'id="default-rule--name"' : '' ?> type="text" placeholder="Rule name" class="form-control" style="margin-bottom:15px;color:white;background:#111;border-color:#222;" name="payload[<?= $index - 1 ?>][name]" value="<?= str_replace('"', """, strip_tags($rule["name"])) ?>"> - - <textarea <?= $index === 1 ? 'id="default-rule--content"' : '' ?> name="payload[<?= $index - 1 ?>][content]" class="form-control" style="resize: none;color:white;background:#111;border-color:#222;" placeholder="Rule details"><?= strip_tags($rule["content"] ?? "") ?></textarea> - - <label style="margin-top:10px;margin-left:5px;"> - <input <?= $index === 1 ? 'id="default-rule--approval-1"' : '' ?> <?= ($rule["approved"][0] ?? false) ? "checked" : "" ?> type="checkbox" name="payload[<?= $index - 1 ?>][approved][0]"> - Approved by <?= $pcName ?> - </label><br> - <label style="margin-left:5px;"> - <input <?= $index === 1 ? 'id="default-rule--approval-2"' : '' ?> <?= ($rule["approved"][1] ?? false) ? "checked" : "" ?> type="checkbox" name="payload[<?= $index - 1 ?>][approved][1]"> - Approved by <?= $prName ?> - </label><br> - - <hr> - </div> - <?php $index++; endforeach; ?> - </div> - <input type="submit" value="Save" class="btn btn-primary"> - <a onclick="editorNewRule();" class="btn btn-secondary">New rule</a> - <input type="hidden" name="updateRules"> - </form> - </div> - </div> - </div> -</div> - -<script> - window.numberOfRules = <?= count($rules) ?>; - - Array.from(document.getElementsByClassName("rule-outer")).forEach((el) => { - let details = el.children[0]; - - el.onclick = () => { - Array.from(document.getElementsByClassName("rule-outer")).forEach((sel) => { - if (el === sel) return; - sel.children[0].open = false; - sel.classList.remove("open"); - }); - - details.open = !details.open; - - if (details.open) { - el.classList.add("open"); - } else { - el.classList.remove("open"); - } - } - }) - - function editorNewRule() { - let id = Math.random(36).toString().split(".")[1]; - - document.getElementById("default-rule").id = "added-" + id + "-1"; - document.getElementById("default-rule--number").id = "added-" + id + "-2"; - document.getElementById("default-rule--name").id = "added-" + id + "-3"; - document.getElementById("default-rule--content").id = "added-" + id + "-4"; - document.getElementById("default-rule--approval-1").id = "added-" + id + "-5"; - document.getElementById("default-rule--approval-2").id = "added-" + id + "-6"; - - let child = document.createElement("div"); - child.id = "temp-" + id; - window.numberOfRules++; - - document.getElementById("rules-editor").appendChild(child); - document.getElementById("temp-" + id).outerHTML = document.getElementById("added-" + id + "-1").outerHTML; - - document.getElementById("added-" + id + "-1").id = "default-rule"; - document.getElementById("added-" + id + "-2").id = "default-rule--number"; - document.getElementById("added-" + id + "-3").id = "default-rule--name"; - document.getElementById("added-" + id + "-4").id = "default-rule--content"; - document.getElementById("added-" + id + "-5").id = "default-rule--approval-1"; - document.getElementById("added-" + id + "-6").id = "default-rule--approval-2"; - - document.getElementById("added-" + id + "-3").name = "payload[" + (numberOfRules - 1) + "][name]"; - document.getElementById("added-" + id + "-4").name = "payload[" + (numberOfRules - 1) + "][content]"; - document.getElementById("added-" + id + "-5").name = "payload[" + (numberOfRules - 1) + "][approved][0]"; - document.getElementById("added-" + id + "-6").name = "payload[" + (numberOfRules - 1) + "][approved][1]"; - - document.getElementById("added-" + id + "-5").checked = false; - document.getElementById("added-" + id + "-6").checked = false; - document.getElementById("added-" + id + "-3").value = ""; - document.getElementById("added-" + id + "-4").value = ""; - document.getElementById("added-" + id + "-2").innerText = "Rule #" + numberOfRules + ":"; - } -</script> - -<style> - .list-group-item { - color: #fff; - background-color: #222; - border: 1px solid rgba(255, 255, 255, .125); - } - - .list-group-item.disabled { - color: #fff; - background-color: #222; - border-color: rgba(255, 255, 255, .125); - opacity: .75; - } - - .rule-outer:hover { - background-color: #252525; - color: #ddd; - } - - .rule-outer:active, .rule-outer:focus { - background-color: #272727; - color: #bbb; - } - - .rule-outer.open { - background-color: #333; - } - - .rule { - list-style: none; - pointer-events: none; - } - - .rule-outer { - cursor: pointer; - } - - .modal-header { - border-bottom: 1px solid #353738; - } - - .modal-content { - border: 1px solid rgba(255, 255, 255, .2); - background-color: #111; - } - - .btn-close { - filter: invert(1); - } -</style> - -<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/footer.inc'; ?> +header("Location: /-/rules"); +die();
\ No newline at end of file diff --git a/pages/rules.inc b/pages/rules.inc index cf88eb1..2268e75 100644 --- a/pages/rules.inc +++ b/pages/rules.inc @@ -1,6 +1,40 @@ <?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/init.inc"; global $title; global $isLoggedIn; global $lang; global $pages; +require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/Parsedown.php"; $Parsedown = new Parsedown(); + +if (isset($_POST["updateRules"])) { + header("Content-Type: text/plain"); + + if (!isset($_POST['payload'])) { + header("Location: /-/rules"); + die(); + } + + foreach ($_POST['payload'] as $index => $rule) { + if (!isset($rule["name"]) || !isset($rule["content"]) && !is_numeric($index)) { + header("Location: /-/rules"); + die(); + } + + if (trim($rule["name"]) === "") { + unset($_POST["payload"][$index]); + continue; + } + + if (!isset($rule["approved"])) $rule["approved"] = []; + if (isset($rule["approved"][0])) $rule["approved"][0] = true; else $rule["approved"][0] = false; + if (isset($rule["approved"][1])) $rule["approved"][1] = true; else $rule["approved"][1] = false; + + $_POST["payload"][$index] = $rule; + } + + file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/rules/rules.json", utf8_encode(json_encode($_POST["payload"]))); + + header("Location: /-/rules"); + die(); +} + require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; ?> @@ -8,95 +42,159 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; <br> <div class="container"> <div id="page-content"> - <h2>General rules</h2> - <p>Click on a rule in the list to view the history of that rule, including all amendments. Note that older versions of a rule cannot be enforced anymore and are provided only for informational purposes.</p> + <h2>Rules</h2> + <p><a onclick="event.target.blur();" href="#" data-bs-toggle="modal" data-bs-target="#editor">Edit rules</a></p> <?php - $rules = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/rules/newrules.json"), true); - - $catindex = 0; - foreach ($rules as $category): ?> - - <?php if ($catindex === 0): ?> + $rules = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/rules/rules.json"), true); - <ul class="list-group"> - <li class="list-group-item rule-outer"> - <details> - <summary class="rule"> - <b><?= $category['title'] ?><?php if (count($category['rules'][0]['amendments']) > 0): ?>-<?= $category['rules'][0]['amendments'][count($category['rules'][0]['amendments']) - 1]['id'] ?>:</b> <?= $category['rules'][0]['amendments'][count($category['rules'][0]['amendments']) - 1]['text'] ?><?php else: ?>:</b> <?= $category['rules'][0]['text'] ?><?php endif; ?> - </summary> + $protectorCloudburst = array_values(array_filter(scoreOrderGlobal(), function ($i) { + return $i["_system"] === "ynmuc" && $i["_metadata"]["protector"]; + }))[0]; + $protectorRaindrops = array_values(array_filter(scoreOrderGlobal(), function ($i) { + return $i["_system"] === "gdapd" && $i["_metadata"]["protector"]; + }))[0]; - <ul class="list-group" style="margin-top:10px;"> - <?php if (count($category['rules'][0]['amendments']) > 0): ?> - <?php $aindex = 0; foreach (array_reverse($category['rules'][0]['amendments']) as $amendment): ?> + $pcName = getMiniName($protectorCloudburst["display_name"] ?? $protectorCloudburst["name"]); + $prName = getMiniName($protectorRaindrops["display_name"] ?? $protectorRaindrops["name"]); - <?php if ($aindex === 0): ?> - <li class="list-group-item"><b>Curent version:</b> <?= $amendment['text'] ?></li> - <?php else: ?> - <li class="list-group-item"><b>Amendment Preamble-<?= $amendment['id'] ?>:</b> <?= $amendment['text'] ?></li> - <?php endif; ?> + ?> - <?php $aindex++; endforeach; ?> - <li class="list-group-item"><b>Original rule:</b> <?= $category['rules'][0]['text'] ?></li> - <?php else: ?> - <li class="list-group-item"> - <i>This rule was never amended.</i> - </li> - <?php endif; ?> - </ul> - </details> - </li> - </ul> - - <?php else: - - if ($category["title"] === "@punishments") { - $catindex = "P"; - $category["title"] = "Planned punishments"; - } - - ?> - - <h4 style="margin-top: 15px;">Articles <?= $catindex ?>.1 to <?= $catindex ?>.<?= count($category['rules']) ?>: <?= $category["title"] ?></h4> - - <ul class="list-group"> - <?php $ruleindex = 1; foreach ($category['rules'] as $rule): ?> - <li class="list-group-item rule-outer"> - <details> - <summary class="rule"> - <b>Article <?= $catindex ?>.<?= $ruleindex ?><?php if (count($rule['amendments']) > 0): ?>-<?= $rule['amendments'][count($rule['amendments']) - 1]['id'] ?>:</b> <?= $rule['amendments'][count($rule['amendments']) - 1]['text'] ?><?php else: ?>:</b> <?= $rule['text'] ?><?php endif; ?> - </summary> - - <ul class="list-group" style="margin-top:10px;"> - <?php if (count($rule['amendments']) > 0): ?> - <?php $aindex = 0; foreach (array_reverse($rule['amendments']) as $amendment): ?> - - <?php if ($aindex === 0): ?> - <li class="list-group-item"><b>Curent version:</b> <?= $amendment['text'] ?></li> - <?php else: ?> - <li class="list-group-item"><b>Amendment Preamble-<?= $amendment['id'] ?>:</b> <?= $amendment['text'] ?></li> - <?php endif; ?> - - <?php $aindex++; endforeach; ?> - <li class="list-group-item"><b>Original rule:</b> <?= $rule['text'] ?></li> - <?php else: ?> - <li class="list-group-item"> - <i>This rule was never amended.</i> - </li> + <?php $index = 1; foreach ($rules as $rule): ?> + <h4 style="margin-top: 20px;"> + <b>Rule <?= $index ?>: <?= strip_tags($rule["name"]) ?></b> + <?php if (in_array(false, $rule["approved"])): ?> + <span class="badge bg-warning text-black rounded-pill">Unapproved</span> <?php endif; ?> - </ul> - </details> + </h4> + <?php if (in_array(false, $rule["approved"])): ?> + <div style="margin-top:10px;" class="alert alert-warning"> + <b>This rule has not yet been approved.</b> All rules need to be approved by the leaders from both systems. This rule is still missing approval from <?php + + if ($rule["approved"][0] === false) { + if ($rule["approved"][1] === false) { + echo($pcName . " and " . $prName); + } else { + echo($pcName); + } + } else if ($rule["approved"][1] === false) { + echo($prName); + } + + ?>. + </div> + <?php endif; ?> + <div> + <p><?= $Parsedown->text(strip_tags($rule["content"])) ?></p> + </div> </li> - <?php $ruleindex++; endforeach; ?> - </ul> - - <?php endif; ?> + <?php $index++; endforeach; ?> + </div> +</div> - <?php $catindex++; endforeach; ?> +<div class="modal fade" id="editor"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <h4 class="modal-title">Rules editor</h4> + <button type="button" class="btn-close" data-bs-dismiss="modal"></button> + </div> + + <div class="modal-body"> + <p>Rules with an empty name are automatically deleted. Buttons to add new rules and save the changes are at the bottom of the list.</p> + <hr> + <form method="post"> + <div id="rules-editor"> + <?php $index = 1; foreach ($rules as $rule): ?> + <div <?= $index === 1 ? 'id="default-rule"' : '' ?>> + <p><b <?= $index === 1 ? 'id="default-rule--number"' : '' ?>>Rule #<?= $index ?>:</b></p> + <input <?= $index === 1 ? 'id="default-rule--name"' : '' ?> type="text" placeholder="Rule name" class="form-control" style="margin-bottom:15px;color:white;background:#111;border-color:#222;" name="payload[<?= $index - 1 ?>][name]" value="<?= str_replace('"', """, strip_tags($rule["name"])) ?>"> + + <textarea <?= $index === 1 ? 'id="default-rule--content"' : '' ?> name="payload[<?= $index - 1 ?>][content]" rows="5" class="form-control" style="resize: none;color:white;background:#111;border-color:#222;" placeholder="Rule details"><?= strip_tags($rule["content"] ?? "") ?></textarea> + + <label style="margin-top:10px;margin-left:5px;"> + <input <?= $index === 1 ? 'id="default-rule--approval-1"' : '' ?> <?= ($rule["approved"][0] ?? false) ? "checked" : "" ?> type="checkbox" name="payload[<?= $index - 1 ?>][approved][0]"> + Approved by <?= $pcName ?> + </label><br> + <label style="margin-left:5px;"> + <input <?= $index === 1 ? 'id="default-rule--approval-2"' : '' ?> <?= ($rule["approved"][1] ?? false) ? "checked" : "" ?> type="checkbox" name="payload[<?= $index - 1 ?>][approved][1]"> + Approved by <?= $prName ?> + </label><br> + + <hr> + </div> + <?php $index++; endforeach; ?> + </div> + <input type="submit" value="Save" class="btn btn-primary"> + <a onclick="editorNewRule();" class="btn btn-secondary">New rule</a> + <input type="hidden" name="updateRules"> + </form> + </div> + </div> </div> </div> +<script> + window.numberOfRules = <?= count($rules) ?>; + + Array.from(document.getElementsByClassName("rule-outer")).forEach((el) => { + let details = el.children[0]; + + el.onclick = () => { + Array.from(document.getElementsByClassName("rule-outer")).forEach((sel) => { + if (el === sel) return; + sel.children[0].open = false; + sel.classList.remove("open"); + }); + + details.open = !details.open; + + if (details.open) { + el.classList.add("open"); + } else { + el.classList.remove("open"); + } + } + }) + + function editorNewRule() { + let id = Math.random(36).toString().split(".")[1]; + + document.getElementById("default-rule").id = "added-" + id + "-1"; + document.getElementById("default-rule--number").id = "added-" + id + "-2"; + document.getElementById("default-rule--name").id = "added-" + id + "-3"; + document.getElementById("default-rule--content").id = "added-" + id + "-4"; + document.getElementById("default-rule--approval-1").id = "added-" + id + "-5"; + document.getElementById("default-rule--approval-2").id = "added-" + id + "-6"; + + let child = document.createElement("div"); + child.id = "temp-" + id; + window.numberOfRules++; + + document.getElementById("rules-editor").appendChild(child); + document.getElementById("temp-" + id).outerHTML = document.getElementById("added-" + id + "-1").outerHTML; + + document.getElementById("added-" + id + "-1").id = "default-rule"; + document.getElementById("added-" + id + "-2").id = "default-rule--number"; + document.getElementById("added-" + id + "-3").id = "default-rule--name"; + document.getElementById("added-" + id + "-4").id = "default-rule--content"; + document.getElementById("added-" + id + "-5").id = "default-rule--approval-1"; + document.getElementById("added-" + id + "-6").id = "default-rule--approval-2"; + + document.getElementById("added-" + id + "-3").name = "payload[" + (numberOfRules - 1) + "][name]"; + document.getElementById("added-" + id + "-4").name = "payload[" + (numberOfRules - 1) + "][content]"; + document.getElementById("added-" + id + "-5").name = "payload[" + (numberOfRules - 1) + "][approved][0]"; + document.getElementById("added-" + id + "-6").name = "payload[" + (numberOfRules - 1) + "][approved][1]"; + + document.getElementById("added-" + id + "-5").checked = false; + document.getElementById("added-" + id + "-6").checked = false; + document.getElementById("added-" + id + "-3").value = ""; + document.getElementById("added-" + id + "-4").value = ""; + document.getElementById("added-" + id + "-2").innerText = "Rule #" + numberOfRules + ":"; + } +</script> + <style> .list-group-item { color: #fff; @@ -127,6 +225,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.inc'; .rule { list-style: none; + pointer-events: none; } .rule-outer { diff --git a/pages/stats.inc b/pages/stats.inc index 75b9e9d..510c317 100644 --- a/pages/stats.inc +++ b/pages/stats.inc @@ -263,6 +263,232 @@ $switchesCloudburst = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . } }); </script> + + <h3 style="margin-top: 15px;">Average switches per day of the week</h3> + <canvas id="graph-01" style="width: 100%; height: 300px; max-height: 100%;"></canvas> + <?php + + $switchesDaysRaindrops = [0, 0, 0, 0, 0, 0, 0]; + $amountDaysRaindrops = [0, 0, 0, 0, 0, 0, 0]; + $switchDoWRaindrops = [0, 0, 0, 0, 0, 0, 0]; + $daysRaindrops = []; + + $switchesDaysCloudburst = [0, 0, 0, 0, 0, 0, 0]; + $amountDaysCloudburst = [0, 0, 0, 0, 0, 0, 0]; + $switchDoWCloudburst = [0, 0, 0, 0, 0, 0, 0]; + $daysCloudburst = []; + + foreach ($switchesCloudburst as $switch) { + if (!in_array(date("Y-m-d", strtotime($switch["timestamp"])), $daysCloudburst)) { + $daysCloudburst[] = date("Y-m-d", strtotime($switch["timestamp"])); + $amountDaysCloudburst[(int)date('N', strtotime($switch["timestamp"])) - 1]++; + } + + $switchesDaysCloudburst[(int)date('N', strtotime($switch["timestamp"])) - 1]++; + } + + $switchDoWCloudburst = [ + $switchesDaysCloudburst[0] / $amountDaysCloudburst[0], + $switchesDaysCloudburst[1] / $amountDaysCloudburst[1], + $switchesDaysCloudburst[2] / $amountDaysCloudburst[2], + $switchesDaysCloudburst[3] / $amountDaysCloudburst[3], + $switchesDaysCloudburst[4] / $amountDaysCloudburst[4], + $switchesDaysCloudburst[5] / $amountDaysCloudburst[5], + $switchesDaysCloudburst[6] / $amountDaysCloudburst[6] + ]; + + foreach ($switchesRaindrops as $switch) { + if (!in_array(date("Y-m-d", strtotime($switch["timestamp"])), $daysRaindrops)) { + $daysRaindrops[] = date("Y-m-d", strtotime($switch["timestamp"])); + $amountDaysRaindrops[(int)date('N', strtotime($switch["timestamp"])) - 1]++; + } + + $switchesDaysRaindrops[(int)date('N', strtotime($switch["timestamp"])) - 1]++; + } + + $switchDoWRaindrops = [ + $switchesDaysRaindrops[0] / $amountDaysRaindrops[0], + $switchesDaysRaindrops[1] / $amountDaysRaindrops[1], + $switchesDaysRaindrops[2] / $amountDaysRaindrops[2], + $switchesDaysRaindrops[3] / $amountDaysRaindrops[3], + $switchesDaysRaindrops[4] / $amountDaysRaindrops[4], + $switchesDaysRaindrops[5] / $amountDaysRaindrops[5], + $switchesDaysRaindrops[6] / $amountDaysRaindrops[6] + ]; + + $switchDoW = [ + $switchDoWCloudburst[0] + $switchDoWRaindrops[0], + $switchDoWCloudburst[1] + $switchDoWRaindrops[1], + $switchDoWCloudburst[2] + $switchDoWRaindrops[2], + $switchDoWCloudburst[3] + $switchDoWRaindrops[3], + $switchDoWCloudburst[4] + $switchDoWRaindrops[4], + $switchDoWCloudburst[5] + $switchDoWRaindrops[5], + $switchDoWCloudburst[6] + $switchDoWRaindrops[6] + ]; + + ?> + <script> + const ctx1 = document.getElementById('graph-01').getContext('2d'); + window.chart01 = [ + { + label: "Switches per week", + data: JSON.parse(`<?= json_encode($switchDoW) ?>`), + backgroundColor: [ + 'rgba(255, 99, 132, 0.2)', + 'rgba(255, 159, 64, 0.2)', + 'rgba(255, 205, 86, 0.2)', + 'rgba(75, 192, 192, 0.2)', + 'rgba(54, 162, 235, 0.2)', + 'rgba(153, 102, 255, 0.2)', + 'rgba(201, 203, 207, 0.2)' + ], + borderColor: [ + 'rgb(255, 99, 132)', + 'rgb(255, 159, 64)', + 'rgb(255, 205, 86)', + 'rgb(75, 192, 192)', + 'rgb(54, 162, 235)', + 'rgb(153, 102, 255)', + 'rgb(201, 203, 207)' + ], + borderWidth: 1 + } + ]; + const graph1 = new Chart(ctx1, { + type: 'bar', + data: { + labels: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], + datasets: window.chart01 + }, + options: { + animation: { + duration: 0 + }, + scales: { + y: { + grid: { + color: "rgba(255,255,255,0.25)" + } + } + }, + plugins: { + legend: { + display: false + }, + tooltip: { + intersect: false + } + } + } + }); + </script> + + <h3 style="margin-top: 15px;">Switches per hour of the day</h3> + <canvas id="graph-02" style="width: 100%; height: 300px; max-height: 100%;"></canvas> + <?php + + $switchHoursRaindrops = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + $switchHoursCloudburst = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + foreach ($switchesCloudburst as $switch) { + $switchHoursCloudburst[(int)date('G', strtotime($switch["timestamp"]))]++; + } + + foreach ($switchesRaindrops as $switch) { + $switchHoursRaindrops[(int)date('G', strtotime($switch["timestamp"]))]++; + } + + $switchHours = [ + $switchHoursRaindrops[0] + $switchHoursCloudburst[0], + $switchHoursRaindrops[1] + $switchHoursCloudburst[1], + $switchHoursRaindrops[2] + $switchHoursCloudburst[2], + $switchHoursRaindrops[3] + $switchHoursCloudburst[3], + $switchHoursRaindrops[4] + $switchHoursCloudburst[4], + $switchHoursRaindrops[5] + $switchHoursCloudburst[5], + $switchHoursRaindrops[6] + $switchHoursCloudburst[6], + $switchHoursRaindrops[7] + $switchHoursCloudburst[7], + $switchHoursRaindrops[8] + $switchHoursCloudburst[8], + $switchHoursRaindrops[9] + $switchHoursCloudburst[9], + $switchHoursRaindrops[10] + $switchHoursCloudburst[10], + $switchHoursRaindrops[11] + $switchHoursCloudburst[11], + $switchHoursRaindrops[12] + $switchHoursCloudburst[12], + $switchHoursRaindrops[13] + $switchHoursCloudburst[13], + $switchHoursRaindrops[14] + $switchHoursCloudburst[14], + $switchHoursRaindrops[15] + $switchHoursCloudburst[15], + $switchHoursRaindrops[16] + $switchHoursCloudburst[16], + $switchHoursRaindrops[17] + $switchHoursCloudburst[17], + $switchHoursRaindrops[18] + $switchHoursCloudburst[18], + $switchHoursRaindrops[19] + $switchHoursCloudburst[19], + $switchHoursRaindrops[20] + $switchHoursCloudburst[20], + $switchHoursRaindrops[21] + $switchHoursCloudburst[21], + $switchHoursRaindrops[22] + $switchHoursCloudburst[22], + $switchHoursRaindrops[23] + $switchHoursCloudburst[23], + ]; + + ?> + <script> + const ctx2 = document.getElementById('graph-02').getContext('2d'); + window.chart02 = [ + { + label: "Switches per hour of day", + data: JSON.parse(`<?= json_encode($switchHours) ?>`), + borderColor: "rgb(160,255,153)" + } + ]; + const graph2 = new Chart(ctx2, { + type: 'line', + data: { + labels: [ + "12am", + "1am", + "2am", + "3am", + "4am", + "5am", + "6am", + "7am", + "8am", + "9am", + "10am", + "11am", + "12pm", + "1pm", + "2pm", + "3pm", + "4pm", + "5pm", + "6pm", + "7pm", + "8pm", + "9pm", + "10pm", + "11pm" + ], + datasets: window.chart02 + }, + options: { + animation: { + duration: 0 + }, + scales: { + y: { + beginAtZero: true, + grid: { + color: "rgba(255,255,255,0.25)" + } + } + }, + plugins: { + legend: { + display: false + }, + tooltip: { + intersect: false + } + } + } + }); + </script> </div> </div> diff --git a/spec.md b/spec.md deleted file mode 100644 index 43195f1..0000000 --- a/spec.md +++ /dev/null @@ -1,253 +0,0 @@ -# ponies.equestria.horse - -## Metadata Bits -48bit bitsets are now used to define metadata that doesn't require a string input, they were 24bit in the past but were changed to 48bit after adding additional metadata. A lot of reserved values remain for future use. - -``` -...............0................................ - => Is mostly verbal in real life. - -...............1................................ - => Is mostly non verbal in real life. - -..............0................................. - => Fronts frequently. - -..............1................................. - => Fronts less frequently. - -.............0.................................. - => Is not sexually active. - -.............1.................................. - => Is sexually active. - -................00.............................. - => Doesn't need to eat food. - -................01.............................. - => Can't eat fish or meat. - -................10.............................. - => Can't eat meat. - -................11.............................. - => Can eat everything. - -..................000........................... - => Can't use magic at all. - -..................001........................... - => Magic in some cases. - -..................010........................... - => Magic using a horn. - -..................011........................... - => Magic using wings. - -..................100........................... - => Magic using wings + horn. - -..................101........................... - => Magic using another part. - -.....................000........................ - => Does not have sensitive spots at all. - -.....................001........................ - => May have one or more sensitive spot·s. - -.....................010........................ - => Has affectionately sensitive spots. - -.....................011........................ - => Has sexually sensitive spots. - -.....................100........................ - => Has both types of sensitive spots - -........................00...................... - => No direct shared memory. - -........................01...................... - => Partial direct shared memory. - -........................10...................... - => Full direct shared memory. - -..........................0..................... - => Not part of a median system. - -..........................1..................... - => Part of a median system. - -...........................00................... - => Not a little. - -...........................01................... - => Age regressor. - -...........................10................... - => Little. - -...........................11................... - => Not a little, but younger. - -.............................0.................. - => Not a protector. - -.............................1.................. - => Protector. - -..............................0................. - => Not a fictive. - -..............................1................. - => Fictive. - -...............................0................ - => No "Not talking" attribute. - -...............................1................ - => "Not talking" attribute. - -................................0............... - => Not an host. - -................................1............... - => Host. - -.............................................0.. - => Not a robot - -.............................................1.. - => Robot - -..............................................0. - => Not a plush - -..............................................1. - => Plush - -...............................................0 - => Not affected by age spells - -...............................................1 - => Affected by age spells - -.................................0001........... - => 1st species: Earth pony. - -.................................0010........... - => 1st species: Unicorn. - -.................................0011........... - => 1st species: Pegasus. - -.................................0100........... - => 1st species: Alicorn. - -.................................0101........... - => 1st species: Bat pony. - -.................................0110........... - => 1st species: Crystal pony. - -.....................................0000....... - => 2nd species: <none> - -.....................................0001....... - => 2nd species: Earth pony. - -.....................................0010....... - => 2nd species: Unicorn. - -.....................................0011....... - => 2nd species: Pegasus. - -.....................................0100....... - => 2nd species: Alicorn. - -.....................................0101....... - => 2nd species: Bat pony. - -.....................................0110....... - => 2nd species: Crystal pony. - -.........................................0000... - => 3rd species: <none> - -.........................................0001... - => 3rd species: Earth pony. - -.........................................0010... - => 3rd species: Unicorn. - -.........................................0011... - => 3rd species: Pegasus. - -.........................................0100... - => 3rd species: Alicorn. - -.........................................0101... - => 3rd species: Bat pony. - -.........................................0110... - => 3rd species: Crystal pony. - -``` -<details> -<summary>List of reserved bits</summary> - -The following bits are reserved for future use or for technical reasons: -``` -xxxxxxxxxxxxxxxx................................ <(1)> -..................110........................... <(2)> -..................111........................... <(2)> -.....................101........................ <(2)> -.....................110........................ <(2)> -.....................111........................ <(2)> -........................11...................... <(2)> -...........................11................... <(2)> -.................................0000........... <(3)> -.................................0111........... <(3)> -.................................1000........... <(3)> -.................................1001........... <(3)> -.................................1010........... <(3)> -.................................1011........... <(3)> -.................................1100........... <(3)> -.................................1101........... <(3)> -.................................1110........... <(3)> -.................................1111........... <(3)> -.....................................0111....... <(3)> -.....................................1000....... <(3)> -.....................................1001....... <(3)> -.....................................1010....... <(3)> -.....................................1011....... <(3)> -.....................................1100....... <(3)> -.....................................1101....... <(3)> -.....................................1110....... <(3)> -.....................................1111....... <(3)> -.........................................0111... <(3)> -.........................................1000... <(3)> -.........................................1001... <(3)> -.........................................1010... <(3)> -.........................................1011... <(3)> -.........................................1100... <(3)> -.........................................1101... <(3)> -.........................................1110... <(3)> -.........................................1111... <(3)> -...............................................0 <(4)> -...............................................1 <(4)> -``` - -`(1)` Reserved for extended metadata<br> -`(2)` Reserved for additional value state<br> -`(3)` Reserved for additional supported species<br> -`(4)` Reserved for additional metadata - -</details> - -The minimum value of the bitset is 0 and the maximum value 281474976710655. -For example, for Scoots, the value would be `000000000000000000000000100010101010000110000000` (9085312)
\ No newline at end of file |