mirror of
https://github.com/Sarjuuk/aowow.git
synced 2025-11-29 15:58:16 +08:00
Future/Frontend
* create php classes, each mirroring a js object * each frontend class implements __toString and json_serialize and as such can be directly used by the template * also allows for sane object creation before js screams in agony * usage TBD
This commit is contained in:
69
includes/components/frontend/announcement.class.php
Normal file
69
includes/components/frontend/announcement.class.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class Announcement implements \JsonSerializable
|
||||
{
|
||||
public const MODE_PAGE_TOP = 0;
|
||||
public const MODE_CONTENT_TOP = 1;
|
||||
|
||||
public const STATUS_DISABLED = 0;
|
||||
public const STATUS_ENABLED = 1;
|
||||
public const STATUS_DELETED = 2;
|
||||
|
||||
public readonly int $status;
|
||||
private bool $editable = false;
|
||||
|
||||
public function __construct(
|
||||
public readonly int $id,
|
||||
private string $name,
|
||||
private LocString $text,
|
||||
private int $mode = self::MODE_CONTENT_TOP,
|
||||
int $status = self::STATUS_ENABLED,
|
||||
private string $style = '')
|
||||
{
|
||||
// a negative id displays ENABLE/DISABLE and DELETE links for this announcement
|
||||
// TODO - the ugroup check mirrors the js. Add other checks like ownership status? (ownership currently not stored)
|
||||
if (User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU) /* && User::$id == $authorId */)
|
||||
$this->editable = true;
|
||||
|
||||
if ($this->mode != self::MODE_PAGE_TOP && $this->mode != self::MODE_CONTENT_TOP)
|
||||
$this->mode = self::MODE_PAGE_TOP;
|
||||
|
||||
if ($status != self::STATUS_DISABLED && $status != self::STATUS_ENABLED && $status != self::STATUS_DELETED)
|
||||
$this->status = self::STATUS_DELETED;
|
||||
else
|
||||
$this->status = $status;
|
||||
}
|
||||
|
||||
public function jsonSerialize() : array
|
||||
{
|
||||
$json = array(
|
||||
'parent' => 'announcement-' . abs($this->id),
|
||||
'id' => $this->editable ? -$this->id : $this->id,
|
||||
'mode' => $this->mode,
|
||||
'status' => $this->status,
|
||||
'name' => $this->name,
|
||||
'text' => (string)$this->text // force LocString to naive string for display
|
||||
);
|
||||
|
||||
if ($this->style)
|
||||
$json['style'] = $this->style;
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
public function __toString() : string
|
||||
{
|
||||
if ($this->status == self::STATUS_DELETED)
|
||||
return '';
|
||||
|
||||
return "new Announcement(".Util::toJSON($this).");\n";
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
50
includes/components/frontend/book.class.php
Normal file
50
includes/components/frontend/book.class.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class Book implements \JsonSerializable
|
||||
{
|
||||
public function __construct(
|
||||
private array $pages, // js:array of html
|
||||
private string $parent = 'book-generic', // HTMLNode.id
|
||||
private ?int $page = null) // start page; defaults to 1
|
||||
{
|
||||
if (!$this->parent)
|
||||
trigger_error(self::class.'::__construct - initialized without parent element', E_USER_WARNING);
|
||||
|
||||
if (!$this->pages)
|
||||
trigger_error(self::class.'::__construct - initialized without content', E_USER_WARNING);
|
||||
else
|
||||
$this->pages = Util::parseHtmlText($this->pages);
|
||||
}
|
||||
|
||||
public function &iterate() : \Generator
|
||||
{
|
||||
reset($this->pages);
|
||||
|
||||
foreach ($this->pages as $idx => &$page)
|
||||
yield $idx => $page;
|
||||
}
|
||||
|
||||
public function jsonSerialize() : array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($this as $prop => $val)
|
||||
if ($val !== null && $prop[0] != '_')
|
||||
$result[$prop] = $val;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function __toString() : string
|
||||
{
|
||||
return "new Book(".Util::toJSON($this).");\n";
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
159
includes/components/frontend/iconelement.class.php
Normal file
159
includes/components/frontend/iconelement.class.php
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class IconElement
|
||||
{
|
||||
public const SIZE_SMALL = 0;
|
||||
public const SIZE_MEDIUM = 1;
|
||||
public const SIZE_LARGE = 2;
|
||||
|
||||
private const CREATE_ICON_TPL = "\$WH.ge('%s%d').appendChild(%s.createIcon(%s));\n";
|
||||
|
||||
private int $idx = 0;
|
||||
private string $href = '';
|
||||
private bool $noIcon = false;
|
||||
|
||||
public readonly string $quality;
|
||||
public readonly ?string $align;
|
||||
public readonly int $size;
|
||||
|
||||
public function __construct(
|
||||
public readonly int $type,
|
||||
public readonly int $typeId,
|
||||
public readonly string $text,
|
||||
public readonly int|string $num = '',
|
||||
public readonly int|string $qty = '',
|
||||
?string $quality = null,
|
||||
int $size = self::SIZE_MEDIUM,
|
||||
bool $link = true,
|
||||
string $url = '',
|
||||
?string $align = null,
|
||||
public readonly string $element = 'icontab-icon',
|
||||
public ?string $extraText = null
|
||||
)
|
||||
{
|
||||
if (is_numeric($quality))
|
||||
$this->quality = 'q'.$quality;
|
||||
else if ($quality !== null)
|
||||
$this->quality = 'q';
|
||||
else
|
||||
$this->quality = '';
|
||||
|
||||
if ($size < self::SIZE_SMALL || $size > self::SIZE_LARGE)
|
||||
{
|
||||
trigger_error('IconElement::__construct - invalid icon size '.$size.'. Normalied to 1 [small]', E_USER_WARNING);
|
||||
$this->size = self::SIZE_SMALL;
|
||||
}
|
||||
else
|
||||
$this->size = $size;
|
||||
|
||||
if ($align && !in_array($align, ['left', 'right', 'center', 'justify']))
|
||||
{
|
||||
trigger_error('IconElement::__construct - unset invalid align value "'.$align.'".', E_USER_WARNING);
|
||||
$this->align = null;
|
||||
}
|
||||
else
|
||||
$this->align = $align;
|
||||
|
||||
if ($type && $typeId && !Type::validateIds($type, $typeId))
|
||||
{
|
||||
$link = false;
|
||||
trigger_error('IconElement::__construct - invalid typeId '.$typeId.' for '.Type::getFileString($type).'.', E_USER_WARNING);
|
||||
}
|
||||
else if (!$type || !$typeId)
|
||||
$link = false;
|
||||
|
||||
if ($link || $url)
|
||||
$this->href = $url ?: '?'.Type::getFileString($this->type).'='.$this->typeId;
|
||||
|
||||
// see Spell/Tools having icon container but no actual icon and having to be inline with other IconElements
|
||||
$this->noIcon = !$typeId || !Type::hasIcon($type);
|
||||
}
|
||||
|
||||
public function renderContainer(int $lpad = 0, int &$iconIdxOffset = 0, bool $rowWrap = false) : string
|
||||
{
|
||||
if (!$this->noIcon)
|
||||
$this->idx = ++$iconIdxOffset;
|
||||
|
||||
$dom = new \DOMDocument('1.0', 'UTF-8');
|
||||
|
||||
$td = $dom->createElement('td');
|
||||
$th = $dom->createElement('th');
|
||||
|
||||
if ($this->noIcon) // see Spell/Tools or AchievementCriteria having no actual icon, but placeholder
|
||||
{
|
||||
$ul = $dom->createElement('ul');
|
||||
$li = $dom->createElement('li');
|
||||
$var = $dom->createElement('var', ' ');
|
||||
$li->appendChild($var);
|
||||
$ul->appendChild($li);
|
||||
$th->appendChild($ul);
|
||||
}
|
||||
else
|
||||
{
|
||||
$th->setAttribute('id', $this->element . $this->idx);
|
||||
if ($this->align)
|
||||
$th->setAttribute('align', $this->align);
|
||||
}
|
||||
|
||||
if ($this->href)
|
||||
($a = $dom->createElement('a', $this->text))->setAttribute('href', $this->href);
|
||||
else
|
||||
$a = $dom->createTextNode($this->text);
|
||||
|
||||
if ($this->quality)
|
||||
{
|
||||
($sp = $dom->createElement('span'))->setAttribute('class', $this->quality);
|
||||
$sp->appendChild($a);
|
||||
$td->appendChild($sp);
|
||||
}
|
||||
else
|
||||
$td->appendChild($a);
|
||||
|
||||
// extraText can be HTML, so import it as a fragment
|
||||
if ($this->extraText)
|
||||
{
|
||||
$fragment = $dom->createDocumentFragment();
|
||||
$fragment->appendXML(' '.$this->extraText);
|
||||
$td->appendChild($fragment);
|
||||
}
|
||||
// only for objectives list..?
|
||||
if ($this->num && $this->size == self::SIZE_SMALL)
|
||||
$td->appendChild($dom->createTextNode(' ('.$this->num.')'));
|
||||
|
||||
if ($rowWrap)
|
||||
{
|
||||
$tr = $dom->createElement('tr');
|
||||
$tr->appendChild($th);
|
||||
$tr->appendChild($td);
|
||||
$dom->append($tr);
|
||||
}
|
||||
else
|
||||
$dom->append($th, $td);
|
||||
|
||||
return str_repeat(' ', $lpad) . $dom->saveHTML();
|
||||
}
|
||||
|
||||
// $WH.ge('icontab-icon1').appendChild(g_spells.createIcon(40120, 1, '1-4', 0));
|
||||
|
||||
public function renderJS(int $lpad = 0) : string
|
||||
{
|
||||
if ($this->noIcon)
|
||||
return '';
|
||||
|
||||
$params = [$this->typeId, $this->size];
|
||||
if ($this->num || $this->qty)
|
||||
$params[] = is_numeric($this->num) ? $this->num : "'".$this->num."'";
|
||||
if ($this->qty)
|
||||
$params[] = is_numeric($this->qty) ? $this->qty : "'".$this->qty."'";
|
||||
|
||||
return str_repeat(' ', $lpad) . sprintf(self::CREATE_ICON_TPL, $this->element, $this->idx, Type::getJSGlobalString($this->type), implode(', ', $params));
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
49
includes/components/frontend/infoboxmarkup.class.php
Normal file
49
includes/components/frontend/infoboxmarkup.class.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class InfoboxMarkup extends Markup
|
||||
{
|
||||
public function __construct(private array $items = [], array $opts, string $parent = '')
|
||||
{
|
||||
parent::__construct('', $opts, $parent);
|
||||
}
|
||||
|
||||
public function addItem(string $item, ?int $pos = null) : void
|
||||
{
|
||||
if (is_null($pos) || $pos >= count($this->items))
|
||||
$this->items[] = $item;
|
||||
else
|
||||
array_splice($this->items, $pos, 0, $item);
|
||||
}
|
||||
|
||||
public function append(string $text) : self
|
||||
{
|
||||
if ($this->items && !$this->__text)
|
||||
$this->replace('[ul][li]' . implode('[/li][li]', $this->items) . '[/li][/ul]');
|
||||
|
||||
return parent::append($text);
|
||||
}
|
||||
|
||||
public function __toString() : string
|
||||
{
|
||||
if ($this->items && !$this->__text)
|
||||
$this->replace('[ul][li]' . implode('[/li][li]', $this->items) . '[/li][/ul]');
|
||||
|
||||
return parent::__toString();
|
||||
}
|
||||
|
||||
public function getJsGlobals() : array
|
||||
{
|
||||
if ($this->items && !$this->__text)
|
||||
$this->replace('[ul][li]' . implode('[/li][li]', $this->items) . '[/li][/ul]');
|
||||
|
||||
return parent::getJsGlobals();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
174
includes/components/frontend/listview.class.php
Normal file
174
includes/components/frontend/listview.class.php
Normal file
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class Listview implements \JsonSerializable
|
||||
{
|
||||
public const MODE_DEFAULT = 0;
|
||||
public const MODE_CHECKBOX = 1;
|
||||
public const MODE_DIV = 2;
|
||||
public const MODE_TILED = 3;
|
||||
public const MODE_CALENDAR = 4;
|
||||
public const MODE_FLEXGRID = 5;
|
||||
|
||||
private const TEMPLATES = array(
|
||||
'achievement' => ['template' => 'achievement', 'id' => 'achievements', 'name' => '$LANG.tab_achievements' ],
|
||||
'areatrigger' => ['template' => 'areatrigger', 'id' => 'areatrigger', ],
|
||||
'calendar' => ['template' => 'holidaycal', 'id' => 'calendar', 'name' => '$LANG.tab_calendar' ],
|
||||
'class' => ['template' => 'classs', 'id' => 'classes', 'name' => '$LANG.tab_classes' ],
|
||||
'commentpreview' => ['template' => 'commentpreview', 'id' => 'comments', 'name' => '$LANG.tab_comments' ],
|
||||
'npc' => ['template' => 'npc', 'id' => 'npcs', 'name' => '$LANG.tab_npcs' ],
|
||||
'currency' => ['template' => 'currency', 'id' => 'currencies', 'name' => '$LANG.tab_currencies' ],
|
||||
'emote' => ['template' => 'emote', 'id' => 'emotes', ],
|
||||
'enchantment' => ['template' => 'enchantment', 'id' => 'enchantments', ],
|
||||
'event' => ['template' => 'holiday', 'id' => 'holidays', 'name' => '$LANG.tab_holidays' ],
|
||||
'faction' => ['template' => 'faction', 'id' => 'factions', 'name' => '$LANG.tab_factions' ],
|
||||
'genericmodel' => ['template' => 'genericmodel', 'id' => 'same-model-as', 'name' => '$LANG.tab_samemodelas' ],
|
||||
'icongallery' => ['template' => 'icongallery', 'id' => 'icons', ],
|
||||
'item' => ['template' => 'item', 'id' => 'items', 'name' => '$LANG.tab_items' ],
|
||||
'itemset' => ['template' => 'itemset', 'id' => 'itemsets', 'name' => '$LANG.tab_itemsets' ],
|
||||
'mail' => ['template' => 'mail', 'id' => 'mails', ],
|
||||
'model' => ['template' => 'model', 'id' => 'gallery', 'name' => '$LANG.tab_gallery' ],
|
||||
'object' => ['template' => 'object', 'id' => 'objects', 'name' => '$LANG.tab_objects' ],
|
||||
'pet' => ['template' => 'pet', 'id' => 'hunter-pets', 'name' => '$LANG.tab_pets' ],
|
||||
'profile' => ['template' => 'profile', 'id' => 'profiles', 'name' => '$LANG.tab_profiles' ],
|
||||
'quest' => ['template' => 'quest', 'id' => 'quests', 'name' => '$LANG.tab_quests' ],
|
||||
'race' => ['template' => 'race', 'id' => 'races', 'name' => '$LANG.tab_races' ],
|
||||
'replypreview' => ['template' => 'replypreview', 'id' => 'comment-replies', 'name' => '$LANG.tab_commentreplies'],
|
||||
'reputationhistory' => ['template' => 'reputationhistory', 'id' => 'reputation', 'name' => '$LANG.tab_reputation' ],
|
||||
'screenshot' => ['template' => 'screenshot', 'id' => 'screenshots', 'name' => '$LANG.tab_screenshots' ],
|
||||
'skill' => ['template' => 'skill', 'id' => 'skills', 'name' => '$LANG.tab_skills' ],
|
||||
'sound' => ['template' => 'sound', 'id' => 'sounds', 'name' => '$LANG.types[19][2]' ],
|
||||
'spell' => ['template' => 'spell', 'id' => 'spells', 'name' => '$LANG.tab_spells' ],
|
||||
'title' => ['template' => 'title', 'id' => 'titles', 'name' => '$LANG.tab_titles' ],
|
||||
'topusers' => ['template' => 'topusers', 'id' => 'topusers', 'name' => '$LANG.topusers' ],
|
||||
'video' => ['template' => 'video', 'id' => 'videos', 'name' => '$LANG.tab_videos' ],
|
||||
'zone' => ['template' => 'zone', 'id' => 'zones', 'name' => '$LANG.tab_zones' ],
|
||||
'guide' => ['template' => 'guide', 'id' => 'guides', ]
|
||||
);
|
||||
|
||||
private string $id = '';
|
||||
private ?string $name = null;
|
||||
private ?array $data = null; // js:array of object <RowDefinitions>
|
||||
private ?string $tabs = null; // js:Object; instance of "Tabs"
|
||||
private ?string $parent = 'lv-generic'; // HTMLNode.id; can be null but is pretty much always 'lv-generic'
|
||||
private ?string $template = null;
|
||||
private ?int $mode = null; // js:int; defaults to MODE_DEFAULT
|
||||
private ?string $note = null; // text in top band
|
||||
|
||||
private ?int $poundable = null; // 0 (no); 1 (always); 2 (yes, w/o sorting); defaults to 1
|
||||
private ?int $searchable = null; // js:bool; defaults to FALSE
|
||||
private ?int $filtrable = null; // js:bool; defaults to FALSE
|
||||
private ?int $sortable = null; // js:bool; defaults to FALSE
|
||||
private ?int $searchDelay = null; // in ms; defalts to 333
|
||||
private ?int $clickable = null; // js:bool; defaults to TRUE
|
||||
private ?int $hideBands = null; // js:int; 1:top, 2:bottom, 3:both;
|
||||
private ?int $hideNav = null; // js:int; 1:top, 2:bottom, 3:both;
|
||||
private ?int $hideHeader = null; // js:bool
|
||||
private ?int $hideCount = null; // js:bool
|
||||
private ?int $debug = null; // js:bool
|
||||
private ?int $_truncated = null; // js:bool; adds predefined note to top band, because there was too much data to display
|
||||
private ?int $_errors = null; // js:bool; adds predefined note to top band, because there was an error
|
||||
private ?int $_petTalents = null; // js:bool; applies modifier for talent levels
|
||||
|
||||
private ?int $nItemsPerPage = null; // js:int; defaults to 50
|
||||
private ?int $_totalCount = null; // js:int; used by loot and comments
|
||||
private ?array $clip = null; // js:array of int {w:<width>, h:<height>}
|
||||
private ?string $customPound = null;
|
||||
private ?string $genericlinktype = null; // sometimes set when expecting to display model
|
||||
private ?array $_upgradeIds = null; // js:array of int (itemIds)
|
||||
|
||||
private null|array|string $extraCols = null; // js:callable or js:array of object <ColumnDefinition>
|
||||
private null|array|string $visibleCols = null; // js:callable or js:array of string <colIds>
|
||||
private null|array|string $hiddenCols = null; // js:callable or js:array of string <colIds>
|
||||
private null|array|string $sort = null; // js:callable or js:array of colIndizes
|
||||
|
||||
private ?string $onBeforeCreate = null; // js:callable
|
||||
private ?string $onAfterCreate = null; // js:callable
|
||||
private ?string $onNoData = null; // js:callable
|
||||
private ?string $computeDataFunc = null; // js:callable
|
||||
private ?string $onSearchSubmit = null; // js:callable
|
||||
private ?string $createNote = null; // js:callable
|
||||
private ?string $createCbControls = null; // js:callable
|
||||
private ?string $customFilter = null; // js:callable
|
||||
private ?string $getItemLink = null; // js:callable
|
||||
private ?array $sortOptions = null; // js:array of object {id:<colId>, name:<name>, hidden:<bool>, type:"text", sortFunc:<callable>}
|
||||
|
||||
private string $__addIn = '';
|
||||
|
||||
public function __construct(array $opts, string $template = '', string $addIn = '')
|
||||
{
|
||||
if ($template && isset(self::TEMPLATES[$template]))
|
||||
foreach (self::TEMPLATES[$template] as $k => $v)
|
||||
$this->$k = $v;
|
||||
|
||||
foreach ($opts as $k => $v)
|
||||
{
|
||||
if (property_exists($this, $k))
|
||||
{
|
||||
// reindex arrays to force json_encode to treat them as arrays
|
||||
if (is_array($v)) // in_array($k, ['data', 'extraCols', 'visibleCols', 'hiddenCols', 'sort', 'sortOptions']))
|
||||
$v = array_values($v);
|
||||
$this->$k = $v;
|
||||
}
|
||||
else
|
||||
trigger_error(self::class.'::__construct - unrecognized option: ' . $k);
|
||||
}
|
||||
|
||||
if ($addIn && !Template\PageTemplate::test('listviews/', $addIn.'.tpl'))
|
||||
trigger_error('Nonexistent Listview addin requested: template/listviews/'.$addIn.'.tpl', E_USER_ERROR);
|
||||
else if ($addIn)
|
||||
$this->__addIn = 'template/listviews/'.$addIn.'.tpl';
|
||||
}
|
||||
|
||||
public function &iterate() : \Generator
|
||||
{
|
||||
reset($this->data);
|
||||
|
||||
foreach ($this->data as $idx => &$row)
|
||||
yield $idx => $row;
|
||||
}
|
||||
|
||||
public function getTemplate() : string
|
||||
{
|
||||
return $this->template;
|
||||
}
|
||||
|
||||
public function setTabs(string $tabVar) : void
|
||||
{
|
||||
if ($tabVar[0] !== '$') // expects a jsVar, which we denote with a prefixed $
|
||||
$tabVar = '$' . $tabVar;
|
||||
|
||||
$this->tabs = $tabVar;
|
||||
}
|
||||
|
||||
public function setError() : void
|
||||
{
|
||||
$this->_errors = 1;
|
||||
}
|
||||
|
||||
public function jsonSerialize() : array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($this as $prop => $val)
|
||||
if ($val !== null && substr($prop, 0, 2) != '__')
|
||||
$result[$prop] = $val;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function __toString() : string
|
||||
{
|
||||
if ($this->__addIn)
|
||||
include($this->__addIn);
|
||||
|
||||
return "new Listview(".Util::toJSON($this).");\n";
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
291
includes/components/frontend/markup.class.php
Normal file
291
includes/components/frontend/markup.class.php
Normal file
@@ -0,0 +1,291 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class Markup implements \JsonSerializable
|
||||
{
|
||||
private const DB_TAG_PATTERN = '/(?<!\\\\)\[(npc|object|item|itemset|quest|spell|zone|faction|pet|achievement|statistic|title|event|class|race|skill|currency|emote|enchantment|money|sound|icondb)=(-?\d+)[^\]]*\]/i';
|
||||
|
||||
// const val
|
||||
public const MARKUP_MODE_COMMENT = 1;
|
||||
public const MARKUP_MODE_ARTICLE = 2;
|
||||
public const MARKUP_MODE_QUICKFACTS = 3;
|
||||
public const MARKUP_MODE_SIGNATURE = 4;
|
||||
public const MARKUP_MODE_REPLY = 5;
|
||||
|
||||
// js var
|
||||
public const MODE_COMMENT = '$Markup.MODE_COMMENT';
|
||||
public const MODE_ARTICLE = '$Markup.MODE_ARTICLE';
|
||||
public const MODE_QUICKFACTS = '$Markup.MODE_QUICKFACTS';
|
||||
public const MODE_SIGNATURE = '$Markup.MODE_SIGNATURE';
|
||||
public const MODE_REPLY = '$Markup.MODE_REPLY';
|
||||
|
||||
// const val
|
||||
public const MARKUP_CLASS_ADMIN = 40;
|
||||
public const MARKUP_CLASS_STAFF = 30;
|
||||
public const MARKUP_CLASS_PREMIUM = 20;
|
||||
public const MARKUP_CLASS_USER = 10;
|
||||
public const MARKUP_CLASS_PENDING = 1;
|
||||
|
||||
// js var
|
||||
public const CLASS_ADMIN = '$Markup.CLASS_ADMIN';
|
||||
public const CLASS_STAFF = '$Markup.CLASS_STAFF';
|
||||
public const CLASS_PREMIUM = '$Markup.CLASS_PREMIUM';
|
||||
public const CLASS_USER = '$Markup.CLASS_USER';
|
||||
public const CLASS_PENDING = '$Markup.CLASS_PENDING';
|
||||
|
||||
// options
|
||||
private ?string $prepend = null; // html in front of article
|
||||
private ?string $append = null; // html trailing the article
|
||||
private ?int $locale = null; // forces tooltips in the article to adhere to another locale
|
||||
private ?int $inBlog = null; // js:bool; unused by aowow
|
||||
private ?string $mode = null; // defaults to Markup.MODE_ARTICLE, which is what we want.
|
||||
private ?string $allow = null; // defaults to Markup.CLASS_STAFF
|
||||
private ?int $roles = null; // if allow is null, get allow from roles (user group); also mode will be set to MODE_ARTICLE for staff groups
|
||||
private ?int $stopAtBreak = null; // js:bool; only parses text until substring "[break]" is encountered; some debug option...?
|
||||
private ?string $highlight = null; // HTMLNode selector
|
||||
|
||||
private ?int $skipReset = null; // js:bool; unsure, if TRUE the next settings in this block get skipped
|
||||
private ?string $uid = null; // defaults to 'abc'; unsure, key under which media is stored and referenced in g_screenshots and g_videos
|
||||
private ?string $root = null; // unsure, something to with Markup Tags that need to be subordinate to other tags (e.g.: [li] to [ol])
|
||||
private ?int $preview = null; // unsure, appends '-preview' to the div created by the [tabs] tag and prevents scrolling. Forum feature?
|
||||
private ?int $dbpage = null; // js:bool; set on db type detail pages; adds article edit links to admin menu
|
||||
|
||||
protected string $__text;
|
||||
|
||||
private string $__parent = 'article-generic';
|
||||
|
||||
public function __construct(string $text, array $opts, string $parent = '')
|
||||
{
|
||||
foreach ($opts as $k => $v)
|
||||
{
|
||||
if (property_exists($this, $k))
|
||||
$this->$k = $v;
|
||||
else
|
||||
trigger_error(self::class.'::__construct - unrecognized option: ' . $k);
|
||||
}
|
||||
|
||||
$this->__text = $text;
|
||||
|
||||
if ($parent)
|
||||
$this->__parent = $parent;
|
||||
}
|
||||
|
||||
public function getJsGlobals() : array
|
||||
{
|
||||
return $this->_parseTags();
|
||||
}
|
||||
|
||||
public function getParent() : string
|
||||
{
|
||||
return $this->__parent;
|
||||
}
|
||||
|
||||
|
||||
/***********************/
|
||||
/* Markup tag handling */
|
||||
/***********************/
|
||||
|
||||
private function _parseTags(array &$jsg = []) : array
|
||||
{
|
||||
return self::parseTags($this->__text, $jsg);
|
||||
}
|
||||
|
||||
public static function parseTags(string $text, array &$jsg = []) : array
|
||||
{
|
||||
$jsGlobals = [];
|
||||
|
||||
if (preg_match_all(self::DB_TAG_PATTERN, $text, $matches, PREG_SET_ORDER))
|
||||
{
|
||||
foreach ($matches as $match)
|
||||
{
|
||||
if ($match[1] == 'statistic')
|
||||
$match[1] = 'achievement';
|
||||
else if ($match[1] == 'icondb')
|
||||
$match[1] = 'icon';
|
||||
|
||||
if ($match[1] == 'money')
|
||||
{
|
||||
if (stripos($match[0], 'items'))
|
||||
{
|
||||
if (preg_match('/items=([0-9,]+)/i', $match[0], $submatch))
|
||||
{
|
||||
$sm = explode(',', $submatch[1]);
|
||||
for ($i = 0; $i < count($sm); $i+=2)
|
||||
$jsGlobals[Type::ITEM][$sm[$i]] = $sm[$i];
|
||||
}
|
||||
}
|
||||
|
||||
if (stripos($match[0], 'currency'))
|
||||
{
|
||||
if (preg_match('/currency=([0-9,]+)/i', $match[0], $submatch))
|
||||
{
|
||||
$sm = explode(',', $submatch[1]);
|
||||
for ($i = 0; $i < count($sm); $i+=2)
|
||||
$jsGlobals[Type::CURRENCY][$sm[$i]] = $sm[$i];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ($type = Type::getIndexFrom(Type::IDX_FILE_STR, $match[1]))
|
||||
$jsGlobals[$type][$match[2]] = $match[2];
|
||||
}
|
||||
}
|
||||
|
||||
Util::mergeJsGlobals($jsg, $jsGlobals);
|
||||
|
||||
return $jsGlobals;
|
||||
}
|
||||
|
||||
private function _stripTags(array $jsgData = []) : string
|
||||
{
|
||||
return self::stripTags($this->__text, $jsgData);
|
||||
}
|
||||
|
||||
public static function stripTags(string $text, array $jsgData = []) : string
|
||||
{
|
||||
// replace DB Tags
|
||||
$text = preg_replace_callback(self::DB_TAG_PATTERN, function ($match) use ($jsgData) {
|
||||
if ($match[1] == 'statistic')
|
||||
$match[1] = 'achievement';
|
||||
else if ($match[1] == 'icondb')
|
||||
$match[1] = 'icon';
|
||||
else if ($match[1] == 'money')
|
||||
{
|
||||
$moneys = [];
|
||||
if (stripos($match[0], 'items'))
|
||||
{
|
||||
if (preg_match('/items=([0-9,]+)/i', $match[0], $submatch))
|
||||
{
|
||||
$sm = explode(',', $submatch[1]);
|
||||
for ($i = 0; $i < count($sm); $i += 2)
|
||||
{
|
||||
if (!empty($jsgData[Type::ITEM][1][$sm[$i]]))
|
||||
$moneys[] = $jsgData[Type::ITEM][1][$sm[$i]]['name'];
|
||||
else
|
||||
$moneys[] = Util::ucFirst(Lang::game('item')).' #'.$sm[$i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (stripos($match[0], 'currency'))
|
||||
{
|
||||
if (preg_match('/currency=([0-9,]+)/i', $match[0], $submatch))
|
||||
{
|
||||
$sm = explode(',', $submatch[1]);
|
||||
for ($i = 0; $i < count($sm); $i += 2)
|
||||
{
|
||||
if (!empty($jsgData[Type::CURRENCY][1][$sm[$i]]))
|
||||
$moneys[] = $jsgData[Type::CURRENCY][1][$sm[$i]]['name'];
|
||||
else
|
||||
$moneys[] = Util::ucFirst(Lang::game('curency')).' #'.$sm[$i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Lang::concat($moneys);
|
||||
}
|
||||
if ($type = Type::getIndexFrom(Type::IDX_FILE_STR, $match[1]))
|
||||
{
|
||||
if (!empty($jsgData[$type][1][$match[2]]))
|
||||
return $jsgData[$type][1][$match[2]]['name'];
|
||||
else
|
||||
return Util::ucFirst(Lang::game($match[1])).' #'.$match[2];
|
||||
}
|
||||
|
||||
trigger_error('Markup::stripTags() - encountered unhandled db-tag: '.var_export($match));
|
||||
return '';
|
||||
}, $text);
|
||||
|
||||
// replace line endings
|
||||
$text = str_replace('[br]', "\n", $text);
|
||||
|
||||
// strip other Tags
|
||||
$stripped = '';
|
||||
$inTag = false;
|
||||
for ($i = 0; $i < strlen($text); $i++)
|
||||
{
|
||||
if ($text[$i] == '[' && (!$i || $text[$i - 1] != '\\'))
|
||||
$inTag = true;
|
||||
if (!$inTag)
|
||||
$stripped .= $text[$i];
|
||||
if ($inTag && $text[$i] == ']' && (!$i || $text[$i - 1] != '\\'))
|
||||
$inTag = false;
|
||||
}
|
||||
|
||||
return $stripped;
|
||||
}
|
||||
|
||||
|
||||
/*********************/
|
||||
/* String Operations */
|
||||
/*********************/
|
||||
|
||||
public function append(string $text) : self
|
||||
{
|
||||
$this->__text .= $text;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function prepend(string $text) : self
|
||||
{
|
||||
$this->__text = $text . $this->__text;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function apply(\Closure $fn) : void
|
||||
{
|
||||
$this->__text = $fn($this->__text);
|
||||
}
|
||||
|
||||
public function replace(string $middle, int $offset = 0, ?int $len = null) : self
|
||||
{
|
||||
// y no mb_substr_replace >:(
|
||||
$start = $end = '';
|
||||
|
||||
if ($offset < 0)
|
||||
$offset = mb_strlen($this->__text) + $offset;
|
||||
|
||||
$start = mb_substr($this->__text, 0, $offset);
|
||||
|
||||
if (!is_null($len) && $len >= 0)
|
||||
$end = mb_substr($this->__text, $offset + $len);
|
||||
else if (!is_null($len) && $len < 0)
|
||||
$end = mb_substr($this->__text, $offset + mb_strlen($this->__text) + $len);
|
||||
|
||||
$this->__text = $start . $middle . $end;
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function cleanText() : string
|
||||
{
|
||||
// break script-tags, unify newlines
|
||||
$val = preg_replace(['/script\s*\>/i', "/\r\n/", "/\r/"], ['script>', "\n", "\n"], $this->__text);
|
||||
|
||||
return strtr(Util::jsEscape($val), ['script>' => 'scr"+"ipt>']);
|
||||
}
|
||||
|
||||
public function jsonSerialize() : array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($this as $prop => $val)
|
||||
if ($val !== null && $prop[0] != '_')
|
||||
$result[$prop] = $val;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function __toString() : string
|
||||
{
|
||||
if ($this->jsonSerialize())
|
||||
return 'Markup.printHtml("'.$this->cleanText().'", "'.$this->__parent.'", '.Util::toJSON($this).");\n";
|
||||
|
||||
return 'Markup.printHtml("'.$this->cleanText().'", "'.$this->__parent."\");\n";
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
70
includes/components/frontend/summary.class.php
Normal file
70
includes/components/frontend/summary.class.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class Summary implements \JsonSerializable
|
||||
{
|
||||
private string $id = ''; // HTMLNode.id
|
||||
private ?string $parent = ''; // HTMLNode.id; if set $id is created and attached here instead of searched for
|
||||
private string $template = ''; //
|
||||
private ?int $editable = null; // js:bool; defaults to TRUE
|
||||
private ?int $draggable = null; // js:bool; defaults to $editable
|
||||
private ?int $searchable = null; // js:bool; defaults to $editable && $draggable
|
||||
private ?int $weightable = null; // js:bool; defaults to $editable
|
||||
private ?int $textable = null; // js:bool; defaults to FALSE
|
||||
private ?int $enhanceable = null; // js:bool; defaults to $editable
|
||||
private ?int $level = null; // js:int; defaults to 80
|
||||
private array $groups = []; // js:array; defaults to GET-params
|
||||
private ?array $weights = null; // js:array; defaults to GET-params
|
||||
|
||||
public function __construct(array $opts)
|
||||
{
|
||||
foreach ($opts as $k => $v)
|
||||
{
|
||||
if (property_exists($this, $k))
|
||||
$this->$k = $v;
|
||||
else
|
||||
trigger_error(self::class.'::__construct - unrecognized option: ' . $k);
|
||||
}
|
||||
|
||||
if (!$this->template)
|
||||
trigger_error(self::class.'::__construct - initialized without template', E_USER_WARNING);
|
||||
if (!$this->id)
|
||||
trigger_error(self::class.'::__construct - initialized without HTMLNode#id to reference', E_USER_WARNING);
|
||||
}
|
||||
|
||||
public function &iterate() : \Generator
|
||||
{
|
||||
reset($this->groups);
|
||||
|
||||
foreach ($this->groups as $idx => &$group)
|
||||
yield $idx => $group;
|
||||
}
|
||||
|
||||
public function addGroup(array $group) : void
|
||||
{
|
||||
$this->groups[] = $group;
|
||||
}
|
||||
|
||||
public function jsonSerialize() : array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($this as $prop => $val)
|
||||
if ($val !== null && $prop[0] != '_')
|
||||
$result[$prop] = $val;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function __toString() : string
|
||||
{
|
||||
return "new Summary(".Util::toJSON($this).");\n";
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
142
includes/components/frontend/tabs.class.php
Normal file
142
includes/components/frontend/tabs.class.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class Tabs implements \JsonSerializable, \Countable
|
||||
{
|
||||
private array $__tabs = [];
|
||||
|
||||
private string $parent = ''; // HTMLNode
|
||||
private ?int $poundable = null; // js:bool
|
||||
private ?int $forceScroll = null; // js:bool
|
||||
private ?int $noScroll = null; // js:bool
|
||||
private ?string $trackable = null; // String to track in Google Analytics .. often a DB Type
|
||||
|
||||
private ?string $onLoad = null; // js::callable
|
||||
private ?string $onShow = null; // js::callable
|
||||
private ?string $onHide = null; // js::callable
|
||||
|
||||
public function __construct(array $opts, public readonly string $__tabVar = 'myTabs', private bool $__forceTabs = false)
|
||||
{
|
||||
foreach ($opts as $k => $v)
|
||||
{
|
||||
if (property_exists($this, $k))
|
||||
$this->$k = $v;
|
||||
else
|
||||
trigger_error(self::class.'::__construct - unrecognized option: ' . $k);
|
||||
}
|
||||
}
|
||||
|
||||
public function &iterate() : \Generator
|
||||
{
|
||||
reset($this->__tabs);
|
||||
|
||||
foreach ($this->__tabs as $idx => &$tab)
|
||||
yield $idx => $tab;
|
||||
}
|
||||
|
||||
public function addListviewTab(Listview $lv) : void
|
||||
{
|
||||
$this->__tabs[] = $lv;
|
||||
}
|
||||
|
||||
public function addDataTab(string $id, string $name, string $data) : void
|
||||
{
|
||||
$this->__tabs[] = ['id' => $id, 'name' => $name, 'data' => $data];
|
||||
$this->__forceTabs = true; // otherwise a single DataTab could not be accessed
|
||||
}
|
||||
|
||||
public function getDataContainer() : \Generator
|
||||
{
|
||||
foreach ($this->__tabs as $tab)
|
||||
if (is_array($tab))
|
||||
yield '<div class="text tabbed-contents" id="tab-'.$tab['id'].'" style="display:none;">'.$tab['data'].'</div>';
|
||||
}
|
||||
|
||||
public function getFlush() : string
|
||||
{
|
||||
if ($this->isTabbed())
|
||||
return $this->__tabVar.".flush();";
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public function isTabbed() : bool
|
||||
{
|
||||
return count($this->__tabs) > 1 || $this->__forceTabs;
|
||||
}
|
||||
|
||||
|
||||
/***********************/
|
||||
/* enable deep cloning */
|
||||
/***********************/
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
foreach ($this->__tabs as $idx => $tab)
|
||||
{
|
||||
if (is_array($tab))
|
||||
continue;
|
||||
|
||||
$this->__tabs[$idx] = clone $tab;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************/
|
||||
/* make countable */
|
||||
/******************/
|
||||
|
||||
public function count() : int
|
||||
{
|
||||
return count($this->__tabs);
|
||||
}
|
||||
|
||||
|
||||
/************************/
|
||||
/* make Tabs stringable */
|
||||
/************************/
|
||||
|
||||
public function jsonSerialize() : array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($this as $prop => $val)
|
||||
if ($val !== null && $prop[0] != '_')
|
||||
$result[$prop] = $val;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function __toString() : string
|
||||
{
|
||||
$result = '';
|
||||
|
||||
if ($this->isTabbed())
|
||||
$result .= "var ".$this->__tabVar." = new Tabs(".Util::toJSON($this).");\n";
|
||||
|
||||
foreach ($this->__tabs as $tab)
|
||||
{
|
||||
if (is_array($tab))
|
||||
{
|
||||
$n = $tab['name'][0] == '$' ? substr($tab['name'], 1) : "'".$tab['name']."'";
|
||||
$result .= $this->__tabVar.".add(".$n.", { id: '".$tab['id']."' });\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($this->isTabbed())
|
||||
$tab->setTabs($this->__tabVar);
|
||||
|
||||
$result .= $tab; // Listview::__toString here
|
||||
}
|
||||
}
|
||||
|
||||
return $result . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
60
includes/components/frontend/tooltip.class.php
Normal file
60
includes/components/frontend/tooltip.class.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class Tooltip implements \JsonSerializable
|
||||
{
|
||||
private ?string $name = null;
|
||||
private ?string $tooltip = null;
|
||||
private ?\StdClass $map = null; // secondary tooltip
|
||||
private ?string $icon = null;
|
||||
private ?int $quality = null; // icon border color coded
|
||||
private ?bool $daily = null;
|
||||
private ?array $spells = null;
|
||||
private ?string $buff = null;
|
||||
private ?array $buffspells = null;
|
||||
|
||||
public function __construct(private string $__powerTpl, private string $__subject, array $opts = [])
|
||||
{
|
||||
foreach ($opts as $k => $v)
|
||||
{
|
||||
if (property_exists($this, $k))
|
||||
$this->$k = $v;
|
||||
else
|
||||
trigger_error(self::class.'::__construct - unrecognized option: ' . $k);
|
||||
}
|
||||
}
|
||||
|
||||
public function jsonSerialize() : array
|
||||
{
|
||||
$out = [];
|
||||
|
||||
$locString = Lang::getLocale()->json();
|
||||
|
||||
foreach ($this as $k => $v)
|
||||
{
|
||||
if ($v === null || $k[0] == '_')
|
||||
continue;
|
||||
|
||||
if ($k == 'icon')
|
||||
$out[$k] = rawurldecode($v);
|
||||
else if ($k == 'quality' || $k == 'map' || $k == 'daily')
|
||||
$out[$k] = $v;
|
||||
else
|
||||
$out[$k . '_' . $locString] = $v;
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function __toString() : string
|
||||
{
|
||||
return sprintf($this->__powerTpl, Util::toJSON($this->__subject, JSON_AOWOW_POWER), Lang::getLocale()->value, Util::toJSON($this, JSON_AOWOW_POWER))."\n";
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
Reference in New Issue
Block a user