diff --git a/src/Html/Renderables/Container.php b/src/Html/Renderables/Container.php
index d45e8de..2ac16e3 100644
--- a/src/Html/Renderables/Container.php
+++ b/src/Html/Renderables/Container.php
@@ -15,7 +15,7 @@ final class Container implements TransformableRenderable
/**
* @param Renderable[] $Contents
*/
- public function __construct($Contents)
+ public function __construct($Contents = [])
{
$this->Contents = $Contents;
}
@@ -67,4 +67,18 @@ final class Container implements TransformableRenderable
$this->Contents
));
}
+
+ public function replacingAll(string $search, Renderable $Replacement): Renderable
+ {
+ return new Container(\array_map(
+ function (Renderable $R) use ($search, $Replacement): Renderable {
+ if (! $R instanceof TransformableRenderable) {
+ return $R;
+ }
+
+ return $R->replacingAll($search, $Replacement);
+ },
+ $this->Contents
+ ));
+ }
}
diff --git a/src/Html/Renderables/Element.php b/src/Html/Renderables/Element.php
index 3a41df6..2df3d53 100644
--- a/src/Html/Renderables/Element.php
+++ b/src/Html/Renderables/Element.php
@@ -214,4 +214,22 @@ final class Element implements TransformableRenderable
$this->Contents
));
}
+
+ public function replacingAll(string $search, Renderable $Replacement): Renderable
+ {
+ if (! isset($this->Contents)) {
+ return $this;
+ }
+
+ return new self($this->name, $this->attributes, \array_map(
+ function (Renderable $R) use ($search, $Replacement): Renderable {
+ if (! $R instanceof TransformableRenderable) {
+ return $R;
+ }
+
+ return $R->replacingAll($search, $Replacement);
+ },
+ $this->Contents
+ ));
+ }
}
diff --git a/src/Html/Renderables/Text.php b/src/Html/Renderables/Text.php
index 54fb4d1..446121e 100644
--- a/src/Html/Renderables/Text.php
+++ b/src/Html/Renderables/Text.php
@@ -41,4 +41,50 @@ final class Text implements TransformableRenderable
{
return $Transform($this->text);
}
+
+ public function replacingAll(string $search, Renderable $Replacement): Renderable
+ {
+ $searchLen = \strlen($search);
+
+ if ($searchLen < 1) {
+ return $this;
+ }
+
+ $result = \preg_match_all(
+ '/\b'.\preg_quote($search, '/').'\b/',
+ $this->text,
+ $matches,
+ \PREG_OFFSET_CAPTURE
+ );
+
+ if (empty($result)) {
+ return $this;
+ }
+
+ $lastEndPos = 0;
+
+ $Container = new Container;
+
+ foreach ($matches[0] as $match) {
+ $pos = $match[1];
+ $endPos = $pos + $searchLen;
+
+ if ($pos !== $lastEndPos) {
+ $Container = $Container->adding(
+ new Text(\substr($this->text, $lastEndPos, $pos - $lastEndPos))
+ );
+ }
+
+ $Container = $Container->adding($Replacement);
+ $lastEndPos = $endPos;
+ }
+
+ if (\strlen($this->text) -1 !== $lastEndPos) {
+ $Container = $Container->adding(
+ new Text(\substr($this->text, $lastEndPos))
+ );
+ }
+
+ return $Container;
+ }
}
diff --git a/src/Html/TransformableRenderable.php b/src/Html/TransformableRenderable.php
index 9497dcb..957288e 100644
--- a/src/Html/TransformableRenderable.php
+++ b/src/Html/TransformableRenderable.php
@@ -22,4 +22,10 @@ interface TransformableRenderable extends Renderable
* @return Renderable
*/
public function transformingContent(\Closure $Transform): Renderable;
+
+ /**
+ * Similar to transformingContent, but replace the string $search in text content
+ * with the renderable $Replacement and return the result.
+ */
+ public function replacingAll(string $search, Renderable $Replacement): Renderable;
}