summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
Diffstat (limited to 'includes')
-rw-r--r--includes/Parsedown.php1994
-rw-r--r--includes/banner.inc8
-rw-r--r--includes/bitset.inc65
-rw-r--r--includes/details.inc112
-rw-r--r--includes/footer.inc3
-rw-r--r--includes/fullbanner.inc63
-rw-r--r--includes/functions.inc13
-rw-r--r--includes/maintenance/clearUnused.php34
-rw-r--r--includes/member.inc66
-rw-r--r--includes/navigation.inc13
-rw-r--r--includes/pages.json13
-rw-r--r--includes/planner.inc132
-rw-r--r--includes/refresh.php22
-rw-r--r--includes/sysbanner.inc2
-rw-r--r--includes/system/species.inc4
15 files changed, 2334 insertions, 210 deletions
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("'", "&apos;", getMemberPronouns($memberData['pronouns'])["possessive_det"])) . " species indicates that " . str_replace("'", "&apos;", getMemberPronouns($memberData['pronouns'])["subjective"]) . ' ' . (getMemberPronouns($memberData["pronouns"])["third"] ? "doesn&apos;t" : "don&apos;t") . " need to eat food, therefore " . str_replace("'", "&apos;", getMemberPronouns($memberData['pronouns'])["subjective"]) . " can eat all foods.'>" . $lang["details"]["food_states"][0] . "</span>",
- 1 => "<span data-bs-toggle='tooltip' title='" . ucfirst(str_replace("'", "&apos;", getMemberPronouns($memberData['pronouns'])["subjective"])) . " can&apos;t eat fish or meat.'>" . $lang["details"]["food_states"][1] . "</span>",
- 2 => "<span data-bs-toggle='tooltip' title='" . ucfirst(str_replace("'", "&apos;", getMemberPronouns($memberData['pronouns'])["subjective"])) . " can&apos;t eat meat, but can eat fish.'>" . $lang["details"]["food_states"][2] . "</span>",
- 3 => "<span data-bs-toggle='tooltip' title='" . ucfirst(str_replace("'", "&apos;", 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("'", "&apos;", $pronouns["possessive_det"])) . " species indicates that " . str_replace("'", "&apos;", $pronouns["subjective"]) . ' ' . ($pronouns["third"] ? "doesn&apos;t" : "don&apos;t") . " need to eat food, therefore " . str_replace("'", "&apos;", $pronouns["subjective"]) . " can eat all foods.'>" . $lang["details"]["food_states"][0] . "</span>",
+ 1 => "<span data-bs-toggle='tooltip' title='" . ucfirst(str_replace("'", "&apos;", $pronouns["subjective"])) . " can&apos;t eat fish or meat.'>" . $lang["details"]["food_states"][1] . "</span>",
+ 2 => "<span data-bs-toggle='tooltip' title='" . ucfirst(str_replace("'", "&apos;", $pronouns["subjective"])) . " can&apos;t eat meat, but can eat fish.'>" . $lang["details"]["food_states"][2] . "</span>",
+ 3 => "<span data-bs-toggle='tooltip' title='" . ucfirst(str_replace("'", "&apos;", $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("'", "&apos;", ucfirst(getMemberPronouns($memberData['pronouns'])["subjective"])) . (getMemberPronouns($memberData["pronouns"])["third"] ? " doesn&apos;t directly share" : " don&apos;t directly share") . " memory with " . str_replace("'", "&apos;", 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("'", "&apos;", ucfirst(getMemberPronouns($memberData['pronouns'])["subjective"])) . (getMemberPronouns($memberData["pronouns"])["third"] ? " doesn&apos;t directly shares" : " don&apos;t directly share") . " all memories with " . str_replace("'", "&apos;", 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("'", "&apos;", ucfirst(getMemberPronouns($memberData['pronouns'])["subjective"])) . (getMemberPronouns($memberData["pronouns"])["third"] ? " shares" : " share") . " all memories with " . str_replace("'", "&apos;", getMemberPronouns($memberData['pronouns'])["possessive_det"]) . " headmates.'>" . $lang["details"]["memory_states"][2] . "</span>",
+ 0 => "<span data-bs-toggle='tooltip' title='" . str_replace("'", "&apos;", ucfirst($pronouns["subjective"])) . ($pronouns["third"] ? " doesn&apos;t directly share" : " don&apos;t directly share") . " memory with " . str_replace("'", "&apos;", $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("'", "&apos;", ucfirst($pronouns["subjective"])) . ($pronouns["third"] ? " doesn&apos;t directly shares" : " don&apos;t directly share") . " all memories with " . str_replace("'", "&apos;", $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("'", "&apos;", ucfirst($pronouns["subjective"])) . ($pronouns["third"] ? " shares" : " share") . " all memories with " . str_replace("'", "&apos;", $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("'", "&apos;", ucfirst(getMemberPronouns($memberData['pronouns'])["subjective"])) . (getMemberPronouns($memberData["pronouns"])["third"] ? " does" : " do") . " not have sensitive spots on " . str_replace("'", "&apos;", getMemberPronouns($memberData['pronouns'])["possessive_det"]) . " body.'>None</span>",
- 1 => "<span data-bs-toggle='tooltip' title='" . str_replace("'", "&apos;", ucfirst(getMemberPronouns($memberData['pronouns'])["subjective"])) . " may have (a) sensitive spot·s on " . str_replace("'", "&apos;", getMemberPronouns($memberData['pronouns'])["possessive_det"]) . " body.'>Maybe</span>",
- 2 => "<span data-bs-toggle='tooltip' title='" . str_replace("'", "&apos;", ucfirst(getMemberPronouns($memberData['pronouns'])["subjective"])) . (getMemberPronouns($memberData["pronouns"])["third"] ? " has" : " have") . " (a) sensitive spot·s on " . str_replace("'", "&apos;", getMemberPronouns($memberData['pronouns'])["possessive_det"]) . " body, and playing with it/them will make " . str_replace("'", "&apos;", getMemberPronouns($memberData['pronouns'])["object"]) . " feel affectionate: extremely safe and cared for.'>Affectionate</span>",
- 3 => "<span data-bs-toggle='tooltip' title='" . str_replace("'", "&apos;", ucfirst(getMemberPronouns($memberData['pronouns'])["subjective"])) . (getMemberPronouns($memberData["pronouns"])["third"] ? " has" : " have") . " (a) sensitive spot·s on " . str_replace("'", "&apos;", getMemberPronouns($memberData['pronouns'])["possessive_det"]) . " body, and playing with it/them will make " . str_replace("'", "&apos;", getMemberPronouns($memberData['pronouns'])["object"]) . " feel sexually pleasured.'>Sexual</span>",
- 4 => "<span data-bs-toggle='tooltip' title='" . str_replace("'", "&apos;", ucfirst(getMemberPronouns($memberData['pronouns'])["subjective"])) . (getMemberPronouns($memberData["pronouns"])["third"] ? " has" : " have") . " (a) sensitive spot·s on " . str_replace("'", "&apos;", getMemberPronouns($memberData['pronouns'])["possessive_det"]) . " body, and playing with it/them will make " . str_replace("'", "&apos;", 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("'", "&apos;", ucfirst($pronouns["subjective"])) . " never age" . str_replace("'", "&apos;", $pronouns["third"] ? "s" : "") . " and " . str_replace("'", "&apos;", $pronouns["third"] ? "doesn't" : "don't") . " have a defined age, making " . str_replace("'", "&apos;", $pronouns["object"]) . " pretty much eternal.'>Eternal</span>" ?>
+ <?php elseif ($metadata["birth"]["age"] <= 0): ?>
-
- <?php else: ?>
- <?= "<span data-bs-toggle='tooltip' title='" . str_replace("'", "&apos;", ucfirst(getMemberPronouns($memberData['pronouns'])["subjective"])) . str_replace("'", "&apos;", getMemberPronouns($memberData["pronouns"])["third"] ? " doesn't" : " don't") . " age like the body does, making " . str_replace("'", "&apos;", getMemberPronouns($memberData['pronouns'])["object"]) . " stay " . $metadata["birth"]["age"] . " years old for " . str_replace("'", "&apos;", 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("'", "&apos;", ucfirst($pronouns["subjective"])) . str_replace("'", "&apos;", $pronouns["third"] ? " doesn't" : " don't") . " age like the body does, making " . str_replace("'", "&apos;", $pronouns["object"]) . " stay " . $metadata["birth"]["age"] . " years old for " . str_replace("'", "&apos;", $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("'", "&apos;", ucfirst(getMemberPronouns($memberData['pronouns'])["subjective"])) . " age" . str_replace("'", "&apos;", getMemberPronouns($memberData["pronouns"])["third"] ? "s" : "") . " like the body does, making " . str_replace("'", "&apos;", 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("'", "&apos;", ucfirst($pronouns["subjective"])) . " age" . str_replace("'", "&apos;", $pronouns["third"] ? "s" : "") . " like the body does, making " . str_replace("'", "&apos;", $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"]);
}))
];