aboutsummaryrefslogtreecommitdiff
path: root/lib/classes/HTMLAttributes.php
diff options
context:
space:
mode:
authorJan-Hendrik Willms <tleilax+studip@gmail.com>2024-11-11 13:56:06 +0000
committerJan-Hendrik Willms <tleilax+studip@gmail.com>2024-11-11 13:56:06 +0000
commit6bc51a71aa09d4f3e7797631517707851ce19acf (patch)
tree6757e5da72274ecfba34056e0cc1dfe26188912f /lib/classes/HTMLAttributes.php
parentbdcccdeed469faf21bdc9f85ca2caddb32825a9f (diff)
set default icon size to 20, fixes #2801
Closes #2801 Merge request studip/studip!1908
Diffstat (limited to 'lib/classes/HTMLAttributes.php')
-rw-r--r--lib/classes/HTMLAttributes.php218
1 files changed, 218 insertions, 0 deletions
diff --git a/lib/classes/HTMLAttributes.php b/lib/classes/HTMLAttributes.php
new file mode 100644
index 0000000..d9500d1
--- /dev/null
+++ b/lib/classes/HTMLAttributes.php
@@ -0,0 +1,218 @@
+<?php
+/**
+ * @author Jan-Hendrik Willms <tleilax+studip@gmail.com>
+ * @license GPL2 or any later version
+ * @since Stud.IP 5.5
+ */
+class HTMLAttributes implements Stringable, ArrayAccess
+{
+ protected static $handlers = [
+ 'class' => [self::class, 'handleCSSClass'],
+ 'style' => [self::class, 'handleCSSStyle'],
+ ];
+
+ public static function from(array $attributes): HTMLAttributes
+ {
+ return new static($attributes);
+ }
+
+ public static function merge(array ...$attr): HTMLAttributes
+ {
+ $result = new static(array_pop($attr));
+ foreach ($attr as $a) {
+ $result->addAttributes($a);
+ }
+ return $result;
+ }
+
+ public static function addHandler(string $key, callable $handler): void
+ {
+ $key = self::sanitizeKey($key);
+ self::$handlers[$key] = $handler;
+ }
+
+ public static function removeHandler(string $key): void
+ {
+ $key = self::sanitizeKey($key);
+ if (isset(self::$handlers[$key])) {
+ unset(self::$handlers[$key]);
+ }
+ }
+
+ public static function sanitizeKey(string $key): string
+ {
+ return strtolower(trim($key));
+ }
+
+ public static function handleCSSClass($value, string $previous, bool $replace = false): string
+ {
+ $classes = [];
+ if (!$replace) {
+ $classes = explode(' ', $previous);
+ $classes = array_filter($classes);
+ }
+
+ foreach ((array) $value as $class) {
+ if (!in_array($class, $classes)) {
+ $classes[] = $class;
+ }
+ }
+
+ return implode(' ', $classes);
+ }
+
+ public static function parseCSSStyles(string $style): array
+ {
+ $temp = explode(';', $style);
+ $temp = array_filter($temp);
+
+ $styles = [];
+ foreach ($temp as $item) {
+ [$k, $v] = array_map('trim', explode(':', $item, 2));
+ $styles[$k] = $v;
+ }
+ return $styles;
+ }
+
+ public static function renderCSSStyles(array $styles): string
+ {
+ $rows = [];
+ foreach ($styles as $key => $value) {
+ $rows[] = "{$key}:{$value}";
+ }
+ return implode(';', $rows);
+ }
+
+ public static function handleCSSStyle($value, string $previous, bool $replace = false): string
+ {
+ $styles = array_merge(
+ $replace ? [] : self::parseCSSStyles($previous),
+ self::parseCSSStyles($value)
+ );
+ return self::renderCSSStyles($styles);
+ }
+
+ protected $attributes = [];
+
+ final public function __construct(array $attributes = [])
+ {
+ $this->addAttributes($attributes);
+ }
+
+ public function isAttributeValid($value, ?string $key = null): bool
+ {
+ return isset($value) && $value !== false;
+ }
+
+ public function hasAttribute(string $key): bool
+ {
+ $key = $this->sanitizeKey($key);
+ return isset($this->attributes[$key]);
+ }
+
+ public function setAttribute(string $key, $value): void
+ {
+ $this->addAttribute($key, $value, true);
+ }
+
+ public function addAttribute(string $key, $value, bool $replace = false): bool
+ {
+ $key = $this->sanitizeKey($key);
+
+ if (!$this->isAttributeValid($value, $key)) {
+ return false;
+ }
+
+ if (isset(self::$handlers[$key])) {
+ $this->attributes[$key] = self::$handlers[$key]($value, $this->attributes[$key] ?? '', $replace);
+ } else {
+ $this->attributes[$key] = $value;
+ }
+
+ return true;
+ }
+
+ public function setAttributes(array $attributes): void
+ {
+ $this->addAttributes($attributes, true);
+ }
+
+ public function addAttributes(array $attributes, bool $replace = false): void
+ {
+ foreach ($attributes as $key => $value) {
+ $this->addAttribute($key, $value, $replace);
+ }
+ }
+
+ public function getAttribute(string $key)
+ {
+ if (!$this->hasAttribute($key)) {
+ return null;
+ }
+
+ $key = $this->sanitizeKey($key);
+ return $this->attributes[$key];
+ }
+
+ public function getAttributes(): array
+ {
+ return $this->attributes;
+ }
+
+ public function removeAttribute(string $key): bool
+ {
+ if (!$this->hasAttribute($key)) {
+ return false;
+ }
+
+ $key = $this->sanitizeKey($key);
+ unset($this->attributes[$key]);
+ return true;
+ }
+
+ public function asString(): string
+ {
+ // Filter empty attributes
+ $attributes = array_filter($this->attributes, function ($value) {
+ return isset($value) && $value !== false;
+ });
+
+ // Actual conversion
+ $result = [];
+ foreach ($attributes as $key => $value) {
+ if ($value === true) {
+ $result[] = htmlReady($key);
+ } else {
+ $result[] = sprintf('%s="%s"', htmlReady($key), htmlReady($value));
+ }
+ }
+ return implode(' ', $result);
+ }
+
+ public function __toString()
+ {
+ return $this->asString();
+ }
+
+ # ArrayAccess
+
+ public function offsetExists($offset): bool
+ {
+ return $this->hasAttribute($offset);
+ }
+
+ public function offsetGet($offset): mixed
+ {
+ return $this->getAttribute($offset);
+ }
+
+ public function offsetSet($offset, $value): void
+ {
+ $this->addAttribute($offset, $value);
+ }
+
+ public function offsetUnset($offset): void
+ {
+ $this->removeAttribute($offset);
+ }
+}