* @license GPL2 or any later version
*/
class LinkElement extends WidgetElement implements ArrayAccess
{
use AttributesArrayAccessTrait;
/**
* Create link by parsing a html chunk.
*
* @param String $html HTML chunk to parse
* @param Icon $icon Optional icon
* @return LinkElement Link element from parsed html
* @throws Exception if html can not be parsed
*/
public static function fromHTML($html, \Icon $icon = null)
{
$matched = preg_match('~((?:\s+\w+=".*?")+)>\s*(?P)~s', $html, $match);
if (!$matched) {
throw new Exception('Could not parse html');
}
$attributes = self::parseAttributes($match['attributes']);
$url = $attributes['href'] ?: '#';
unset($attributes['href']);
return new self(html_entity_decode($match['label']), html_entity_decode($url), $icon, $attributes);
}
/**
* Parse a string of html attributes into an associative array.
*
* @param String $text String of html attributes
* @return Array parsed attributes as key => value pairs
* @see https://gist.github.com/rodneyrehm/3070128
*/
protected static function parseAttributes($text)
{
$attributes = [];
$pattern = '#(?(DEFINE)
(?[a-zA-Z][a-zA-Z0-9-:]*)
(?"[^"]+")
(?\'[^\']+\')
(?[^\s>]+)
(?((?&value_double)|(?&value_single)|(?&value_none)))
)
(?(?&name))(=(?(?&value)))?#xs';
if (preg_match_all($pattern, $text, $matches, PREG_SET_ORDER)) {
foreach ($matches as $match) {
$attributes[$match['n']] = isset($match['v'])
? html_entity_decode(trim($match['v'], '\'"'))
: null;
}
}
return $attributes;
}
public $url;
public $label;
public $icon;
public $active = false;
public $as_button = false;
/**
* create a link for a widget
*
* @param String $label Label/content of the link
* @param String $url URL/Location of the link (raw url, no entities)
* @param Icon $icon Icon for the link
* @param array $attributes HTML-attributes for the a-tag in an associative array.
*/
public function __construct($label, $url, \Icon $icon = null, $attributes = [])
{
parent::__construct();
$this->label = $label;
$this->url = $url;
$this->attributes = $attributes;
$this->icon = $icon;
}
/**
* Sets the active state of the element.
*
* @param bool $active Active state (optional, defaults to true)
* @return LinkElement instance to allow chaining
*/
public function setActive($active = true)
{
$this->active = $active;
return $this;
}
/**
* Sets the dialog options for the element. Passing false as $state will
* reset the dialog options to "none".
*
* @param mixed $active Dialog options (optional, defaults to blank/standard
* dialog)
* @return LinkElement instance to allow chaining
*/
public function asDialog($state = '')
{
if ($state !== false) {
$this->attributes['data-dialog'] = $state;
} else {
unset($this->attributes['data-dialog']);
}
return $this;
}
/**
* Defines whether the link should be rendered as a button/form with POST
* method.
*
* @param bool $active State (optional, defaults to true)
* @return LinkElement instance to allow chaining
*/
public function asButton($state = true)
{
$this->as_button = $state;
return $this;
}
/**
* Sets the target attribute of the element.
*
* @param string $target Target attribute
* @return LinkElement instance to allow chaining
*/
public function setTarget($target)
{
if ($target) {
$this->attributes['target'] = $target;
} else {
unset($this->attributes['target']);
}
return $this;
}
/**
* Adds a css class to the rendered element.
*
* @param string $clas CSS class to add
* @return LinkElement instance to allow chaining
*/
public function addClass($class)
{
$this->attributes['class'] = isset($this->attributes['class'])
? $this->attributes['class'] . " " . $class
: $class;
return $this;
}
/**
* Set disabled state
*
* @param boolean $state Disabled state
* @return LinkElement instance to allow chaining
*/
public function setDisabled($state = true)
{
if ($state) {
$this->attributes['disabled'] = true;
} else {
unset($this->attributes['disabled']);
}
return $this;
}
/**
* Returns whether the element is disabled.
*
* @return bool
*/
public function isDisabled()
{
return isset($this->attributes['disabled']) && $this->attributes['disabled'] !== false;
}
/**
* Renders the element.
*
* @return string
*/
public function render()
{
$disabled = $this->isDisabled();
if ($this->as_button && !$disabled) {
return $this->renderButton();
}
if ($this->active) {
$this->addClass('active');
}
$attributes = (array) $this->attributes;
if ($disabled) {
$tag = 'span';
} else {
$tag = 'a';
$attributes['href'] = $this->url;
}
return sprintf(
'<%1$s %2$s>%3$s%1$s>',
$tag,
arrayToHtmlAttributes($attributes),
htmlReady($this->label)
);
}
public function renderWithIcon($icon)
{
$disabled = $this->isDisabled();
if ($this->as_button && !$disabled) {
return $this->renderButton($icon);
}
if ($this->active) {
$this->addClass('active');
}
$attributes = (array) $this->attributes;
if ($disabled) {
$tag = 'span';
} else {
$tag = 'a';
$attributes['href'] = $this->url;
}
return sprintf(
'<%1$s %2$s>%3$s%4$s%1$s>',
$tag,
arrayToHtmlAttributes($attributes),
$icon->asImg(),
htmlReady($this->label)
);
}
/**
* Renders the element as a button/form.
*
* @return string
*/
protected function renderButton(?Icon $icon = null)
{
return sprintf(
'',
htmlReady($this->url),
arrayToHtmlAttributes((array) $this->attributes),
$icon?->asImg() ?? '',
htmlReady($this->label)
);
}
/**
* Returns whether the given url is valid.
*
* @param string $url URL to test
* @return bool
*/
protected function isURL($url)
{
return filter_var($url, FILTER_VALIDATE_URL) !== false;
}
}