summaryrefslogtreecommitdiffstats
path: root/vendor/ipl/html/src/FormElement
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--vendor/ipl/html/src/FormElement/BaseFormElement.php348
-rw-r--r--vendor/ipl/html/src/FormElement/ButtonElement.php8
-rw-r--r--vendor/ipl/html/src/FormElement/CheckboxElement.php125
-rw-r--r--vendor/ipl/html/src/FormElement/DateElement.php8
-rw-r--r--vendor/ipl/html/src/FormElement/FormElements.php502
-rw-r--r--vendor/ipl/html/src/FormElement/HiddenElement.php8
-rw-r--r--vendor/ipl/html/src/FormElement/InputElement.php49
-rw-r--r--vendor/ipl/html/src/FormElement/LocalDateTimeElement.php52
-rw-r--r--vendor/ipl/html/src/FormElement/NumberElement.php8
-rw-r--r--vendor/ipl/html/src/FormElement/PasswordElement.php55
-rw-r--r--vendor/ipl/html/src/FormElement/SelectElement.php144
-rw-r--r--vendor/ipl/html/src/FormElement/SelectOption.php45
-rw-r--r--vendor/ipl/html/src/FormElement/SubFormElement.php57
-rw-r--r--vendor/ipl/html/src/FormElement/SubmitButtonElement.php27
-rw-r--r--vendor/ipl/html/src/FormElement/SubmitElement.php50
-rw-r--r--vendor/ipl/html/src/FormElement/TextElement.php8
-rw-r--r--vendor/ipl/html/src/FormElement/TextareaElement.php24
-rw-r--r--vendor/ipl/html/src/FormElement/TimeElement.php8
18 files changed, 1526 insertions, 0 deletions
diff --git a/vendor/ipl/html/src/FormElement/BaseFormElement.php b/vendor/ipl/html/src/FormElement/BaseFormElement.php
new file mode 100644
index 0000000..c75a636
--- /dev/null
+++ b/vendor/ipl/html/src/FormElement/BaseFormElement.php
@@ -0,0 +1,348 @@
+<?php
+
+namespace ipl\Html\FormElement;
+
+use ipl\Html\Attribute;
+use ipl\Html\Attributes;
+use ipl\Html\BaseHtmlElement;
+use ipl\Html\Contract\FormElement;
+use ipl\Html\Contract\ValueCandidates;
+use ipl\Html\Form;
+use ipl\Stdlib\Messages;
+use ipl\Validator\ValidatorChain;
+
+abstract class BaseFormElement extends BaseHtmlElement implements FormElement, ValueCandidates
+{
+ use Messages;
+
+ /** @var string Description of the element */
+ protected $description;
+
+ /** @var string Label of the element */
+ protected $label;
+
+ /** @var string Name of the element */
+ protected $name;
+
+ /** @var bool Whether the element is ignored */
+ protected $ignored = false;
+
+ /** @var bool Whether the element is required */
+ protected $required = false;
+
+ /** @var null|bool Whether the element is valid; null if the element has not been validated yet, bool otherwise */
+ protected $valid;
+
+ /** @var ValidatorChain Registered validators */
+ protected $validators;
+
+ /** @var mixed Value of the element */
+ protected $value;
+
+ /** @var array Value candidates of the element */
+ protected $valueCandidates = [];
+
+ /**
+ * Create a new form element
+ *
+ * @param string $name Name of the form element
+ * @param mixed $attributes Attributes of the form element
+ */
+ public function __construct($name, $attributes = null)
+ {
+ if ($attributes !== null) {
+ $this->addAttributes($attributes);
+ }
+ $this->setName($name);
+ }
+
+ public function getDescription()
+ {
+ return $this->description;
+ }
+
+ /**
+ * Set the description of the element
+ *
+ * @param string $description
+ *
+ * @return $this
+ */
+ public function setDescription($description)
+ {
+ $this->description = $description;
+
+ return $this;
+ }
+
+ public function getLabel()
+ {
+ return $this->label;
+ }
+
+ /**
+ * Set the label of the element
+ *
+ * @param string $label
+ *
+ * @return $this
+ */
+ public function setLabel($label)
+ {
+ $this->label = $label;
+
+ return $this;
+ }
+
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Set the name for the element
+ *
+ * @param string $name
+ *
+ * @return $this
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+
+ return $this;
+ }
+
+ public function isIgnored()
+ {
+ return $this->ignored;
+ }
+
+ /**
+ * Set whether the element is ignored
+ *
+ * @param bool $ignored
+ *
+ * @return $this
+ */
+ public function setIgnored($ignored = true)
+ {
+ $this->ignored = (bool) $ignored;
+
+ return $this;
+ }
+
+ public function isRequired()
+ {
+ return $this->required;
+ }
+
+ /**
+ * Set whether the element is required
+ *
+ * @param bool $required
+ *
+ * @return $this
+ */
+ public function setRequired($required = true)
+ {
+ $this->required = (bool) $required;
+
+ return $this;
+ }
+
+ public function isValid()
+ {
+ if ($this->valid === null) {
+ $this->validate();
+ }
+
+ return $this->valid;
+ }
+
+ /**
+ * Get whether the element has been validated and is not valid
+ *
+ * @return bool
+ *
+ * @deprecated Use {@link hasBeenValidated()} in combination with {@link isValid()} instead
+ */
+ public function hasBeenValidatedAndIsNotValid()
+ {
+ return $this->valid !== null && ! $this->valid;
+ }
+
+ /**
+ * Get the validators
+ *
+ * @return ValidatorChain
+ */
+ public function getValidators()
+ {
+ if ($this->validators === null) {
+ $this->validators = new ValidatorChain();
+ $this->addDefaultValidators();
+ }
+
+ return $this->validators;
+ }
+
+ /**
+ * Add default validators
+ */
+ public function addDefaultValidators()
+ {
+ }
+
+ /**
+ * Set the validators
+ *
+ * @param iterable $validators
+ *
+ * @return $this
+ */
+ public function setValidators($validators)
+ {
+ $this
+ ->getValidators()
+ ->clearValidators()
+ ->addValidators($validators);
+
+ return $this;
+ }
+
+ /**
+ * Add validators
+ *
+ * @param iterable $validators
+ *
+ * @return $this
+ */
+ public function addValidators($validators)
+ {
+ $this->getValidators()->addValidators($validators);
+
+ return $this;
+ }
+
+ public function hasValue()
+ {
+ $value = $this->getValue();
+
+ return $value !== null && $value !== '' && $value !== [];
+ }
+
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ public function setValue($value)
+ {
+ if ($value === '') {
+ $this->value = null;
+ } else {
+ $this->value = $value;
+ }
+ $this->valid = null;
+
+ return $this;
+ }
+
+ public function getValueCandidates()
+ {
+ return $this->valueCandidates;
+ }
+
+ public function setValueCandidates(array $values)
+ {
+ $this->valueCandidates = $values;
+
+ return $this;
+ }
+
+ public function onRegistered(Form $form)
+ {
+ }
+
+ /**
+ * Validate the element using all registered validators
+ *
+ * @return $this
+ */
+ public function validate()
+ {
+ $this->valid = $this->getValidators()->isValid($this->getValue());
+ $this->addMessages($this->getValidators()->getMessages());
+
+ return $this;
+ }
+
+ public function hasBeenValidated()
+ {
+ return $this->valid !== null;
+ }
+
+ /**
+ * Callback for the name attribute
+ *
+ * @return Attribute|string
+ */
+ public function getNameAttribute()
+ {
+ return $this->getName();
+ }
+
+ /**
+ * Callback for the required attribute
+ *
+ * @return Attribute|null
+ */
+ public function getRequiredAttribute()
+ {
+ if ($this->isRequired()) {
+ return new Attribute('required', true);
+ }
+
+ return null;
+ }
+
+ /**
+ * Callback for the value attribute
+ *
+ * @return mixed
+ */
+ public function getValueAttribute()
+ {
+ return $this->getValue();
+ }
+
+ protected function registerValueCallback(Attributes $attributes)
+ {
+ $attributes->registerAttributeCallback(
+ 'value',
+ [$this, 'getValueAttribute'],
+ [$this, 'setValue']
+ );
+ }
+
+ protected function registerAttributeCallbacks(Attributes $attributes)
+ {
+ $this->registerValueCallback($attributes);
+
+ $attributes
+ ->registerAttributeCallback('label', null, [$this, 'setLabel'])
+ ->registerAttributeCallback('name', [$this, 'getNameAttribute'], [$this, 'setName'])
+ ->registerAttributeCallback('description', null, [$this, 'setDescription'])
+ ->registerAttributeCallback('validators', null, [$this, 'setValidators'])
+ ->registerAttributeCallback('ignore', null, [$this, 'setIgnored'])
+ ->registerAttributeCallback('required', [$this, 'getRequiredAttribute'], [$this, 'setRequired']);
+
+ $this->registerCallbacks();
+ }
+
+ /** @deprecated Use {@link registerAttributeCallbacks()} instead */
+ protected function registerCallbacks()
+ {
+ }
+}
diff --git a/vendor/ipl/html/src/FormElement/ButtonElement.php b/vendor/ipl/html/src/FormElement/ButtonElement.php
new file mode 100644
index 0000000..63ae540
--- /dev/null
+++ b/vendor/ipl/html/src/FormElement/ButtonElement.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace ipl\Html\FormElement;
+
+class ButtonElement extends BaseFormElement
+{
+ protected $tag = 'button';
+}
diff --git a/vendor/ipl/html/src/FormElement/CheckboxElement.php b/vendor/ipl/html/src/FormElement/CheckboxElement.php
new file mode 100644
index 0000000..5e16fc2
--- /dev/null
+++ b/vendor/ipl/html/src/FormElement/CheckboxElement.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace ipl\Html\FormElement;
+
+use ipl\Html\Attributes;
+
+class CheckboxElement extends InputElement
+{
+ /** @var bool Whether the checkbox is checked */
+ protected $checked = false;
+
+ /** @var string Value of the checkbox when it is checked */
+ protected $checkedValue = 'y';
+
+ /** @var string Value of the checkbox when it is not checked */
+ protected $uncheckedValue = 'n';
+
+ protected $type = 'checkbox';
+
+ /**
+ * Get whether the checkbox is checked
+ *
+ * @return bool
+ */
+ public function isChecked()
+ {
+ return $this->checked;
+ }
+
+ /**
+ * Set whether the checkbox is checked
+ *
+ * @param bool $checked
+ *
+ * @return $this
+ */
+ public function setChecked($checked)
+ {
+ $this->checked = (bool) $checked;
+
+ return $this;
+ }
+
+ /**
+ * Get the value of the checkbox when it is checked
+ *
+ * @return string
+ */
+ public function getCheckedValue()
+ {
+ return $this->checkedValue;
+ }
+
+ /**
+ * Set the value of the checkbox when it is checked
+ *
+ * @param string $checkedValue
+ *
+ * @return $this
+ */
+ public function setCheckedValue($checkedValue)
+ {
+ $this->checkedValue = $checkedValue;
+
+ return $this;
+ }
+
+ /**
+ * Get the value of the checkbox when it is not checked
+ *
+ * @return string
+ */
+ public function getUncheckedValue()
+ {
+ return $this->uncheckedValue;
+ }
+
+ /**
+ * Set the value of the checkbox when it is not checked
+ *
+ * @param string $uncheckedValue
+ *
+ * @return $this
+ */
+ public function setUncheckedValue($uncheckedValue)
+ {
+ $this->uncheckedValue = $uncheckedValue;
+
+ return $this;
+ }
+
+ public function setValue($value)
+ {
+ if (is_bool($value)) {
+ $value = $value ? $this->getCheckedValue() : $this->getUncheckedValue();
+ }
+
+ $this->setChecked($value === $this->getCheckedValue());
+
+ return parent::setValue($value);
+ }
+
+ public function getValueAttribute()
+ {
+ return $this->getCheckedValue();
+ }
+
+ protected function registerAttributeCallbacks(Attributes $attributes)
+ {
+ parent::registerAttributeCallbacks($attributes);
+
+ $attributes->registerAttributeCallback(
+ 'checked',
+ [$this, 'isChecked'],
+ [$this, 'setChecked']
+ );
+ }
+
+ public function renderUnwrapped()
+ {
+ $html = parent::renderUnwrapped();
+
+ return (new HiddenElement($this->getName(), ['value' => $this->getUncheckedValue()])) . $html;
+ }
+}
diff --git a/vendor/ipl/html/src/FormElement/DateElement.php b/vendor/ipl/html/src/FormElement/DateElement.php
new file mode 100644
index 0000000..2f73b3c
--- /dev/null
+++ b/vendor/ipl/html/src/FormElement/DateElement.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace ipl\Html\FormElement;
+
+class DateElement extends InputElement
+{
+ protected $type = 'date';
+}
diff --git a/vendor/ipl/html/src/FormElement/FormElements.php b/vendor/ipl/html/src/FormElement/FormElements.php
new file mode 100644
index 0000000..d8bb784
--- /dev/null
+++ b/vendor/ipl/html/src/FormElement/FormElements.php
@@ -0,0 +1,502 @@
+<?php
+
+namespace ipl\Html\FormElement;
+
+use InvalidArgumentException;
+use ipl\Html\Contract\FormElement;
+use ipl\Html\Contract\FormElementDecorator;
+use ipl\Html\Contract\ValueCandidates;
+use ipl\Html\Form;
+use ipl\Html\FormDecorator\DecoratorInterface;
+use ipl\Html\ValidHtml;
+use ipl\Stdlib\Events;
+use ipl\Stdlib\Plugins;
+use UnexpectedValueException;
+
+use function ipl\Stdlib\get_php_type;
+
+trait FormElements
+{
+ use Events;
+ use Plugins;
+
+ /** @var FormElementDecorator|null */
+ private $defaultElementDecorator;
+
+ /** @var bool Whether the default element decorator loader has been registered */
+ private $defaultElementDecoratorLoaderRegistered = false;
+
+ /** @var bool Whether the default element loader has been registered */
+ private $defaultElementLoaderRegistered = false;
+
+ /** @var FormElement[] */
+ private $elements = [];
+
+ /** @var array */
+ private $populatedValues = [];
+
+ /**
+ * Get all elements
+ *
+ * @return FormElement[]
+ */
+ public function getElements()
+ {
+ return $this->elements;
+ }
+
+ /**
+ * Get whether the given element exists
+ *
+ * @param string|FormElement $element
+ *
+ * @return bool
+ */
+ public function hasElement($element)
+ {
+ if (is_string($element)) {
+ return array_key_exists($element, $this->elements);
+ }
+
+ if ($element instanceof FormElement) {
+ return in_array($element, $this->elements, true);
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the element by the given name
+ *
+ * @param string $name
+ *
+ * @return FormElement
+ *
+ * @throws InvalidArgumentException If no element with the given name exists
+ */
+ public function getElement($name)
+ {
+ if (! array_key_exists($name, $this->elements)) {
+ throw new InvalidArgumentException(sprintf(
+ "Can't get element '%s'. Element does not exist",
+ $name
+ ));
+ }
+
+ return $this->elements[$name];
+ }
+
+ /**
+ * Add an element
+ *
+ * @param string|FormElement $typeOrElement Type of the element as string or an instance of FormElement
+ * @param string $name Name of the element
+ * @param mixed $options Element options as key-value pairs
+ *
+ * @return $this
+ *
+ * @throws InvalidArgumentException If $typeOrElement is neither a string nor an instance of FormElement
+ * or if $typeOrElement is a string and $name is not set
+ * or if $typeOrElement is a string but type is unknown
+ * or if $typeOrElement is an instance of FormElement but does not have a name
+ */
+ public function addElement($typeOrElement, $name = null, $options = null)
+ {
+ if (is_string($typeOrElement)) {
+ if ($name === null) {
+ throw new InvalidArgumentException(sprintf(
+ '%s expects parameter 2 to be set if parameter 1 is a string',
+ __METHOD__
+ ));
+ }
+
+ $element = $this->createElement($typeOrElement, $name, $options);
+ } elseif ($typeOrElement instanceof FormElement) {
+ $element = $typeOrElement;
+ } else {
+ throw new InvalidArgumentException(sprintf(
+ '%s() expects parameter 1 to be a string or an instance of %s, %s given',
+ __METHOD__,
+ FormElement::class,
+ get_php_type($typeOrElement)
+ ));
+ }
+
+ $this
+ ->registerElement($element) // registerElement() must be called first because of the name check
+ ->decorate($element)
+ ->addHtml($element);
+
+ return $this;
+ }
+
+ /**
+ * Create an element
+ *
+ * @param string $type Type of the element
+ * @param string $name Name of the element
+ * @param mixed $options Element options as key-value pairs
+ *
+ * @return FormElement
+ *
+ * @throws InvalidArgumentException If the type of the element is unknown
+ */
+ public function createElement($type, $name, $options = null)
+ {
+ $this->ensureDefaultElementLoaderRegistered();
+
+ $class = $this->loadPlugin('element', $type);
+
+ if (! $class) {
+ throw new InvalidArgumentException(sprintf(
+ "Can't create element of unknown type '%s",
+ $type
+ ));
+ }
+
+ /** @var FormElement $element */
+ $element = new $class($name);
+
+ if ($options !== null) {
+ $element->addAttributes($options);
+ }
+
+ return $element;
+ }
+
+ /**
+ * Register an element
+ *
+ * Registers the element for value and validation handling but does not add it to the render stack.
+ *
+ * @param FormElement $element
+ *
+ * @return $this
+ *
+ * @throws InvalidArgumentException If $element does not provide a name
+ */
+ public function registerElement(FormElement $element)
+ {
+ $name = $element->getName();
+
+ if ($name === null) {
+ throw new InvalidArgumentException(sprintf(
+ '%s expects the element to provide a name',
+ __METHOD__
+ ));
+ }
+
+ $this->elements[$name] = $element;
+
+ if (array_key_exists($name, $this->populatedValues)) {
+ $element->setValue($this->populatedValues[$name][count($this->populatedValues[$name]) - 1]);
+
+ if ($element instanceof ValueCandidates) {
+ $element->setValueCandidates($this->populatedValues[$name]);
+ }
+ }
+
+ $this->onElementRegistered($element);
+ $this->emit(Form::ON_ELEMENT_REGISTERED, [$element]);
+
+ return $this;
+ }
+
+ /**
+ * Get whether a default element decorator exists
+ *
+ * @return bool
+ */
+ public function hasDefaultElementDecorator()
+ {
+ return $this->defaultElementDecorator !== null;
+ }
+
+ /**
+ * Get the default element decorator, if any
+ *
+ * @return FormElementDecorator|null
+ */
+ public function getDefaultElementDecorator()
+ {
+ return $this->defaultElementDecorator;
+ }
+
+ /**
+ * Set the default element decorator
+ *
+ * If $decorator is a string, the decorator will be automatically created from a registered decorator loader.
+ * A loader for the namespace ipl\\Html\\FormDecorator is automatically registered by default.
+ * See {@link addDecoratorLoader()} for registering a custom loader.
+ *
+ * @param FormElementDecorator|string $decorator
+ *
+ * @return $this
+ *
+ * @throws InvalidArgumentException If $decorator is a string and can't be loaded from registered decorator loaders
+ * or if a decorator loader does not return an instance of
+ * {@link FormElementDecorator}
+ */
+ public function setDefaultElementDecorator($decorator)
+ {
+ if ($decorator instanceof FormElementDecorator || $decorator instanceof DecoratorInterface) {
+ $this->defaultElementDecorator = $decorator;
+ } else {
+ $this->ensureDefaultElementDecoratorLoaderRegistered();
+
+ $d = $this->loadPlugin('decorator', $decorator);
+
+ if (! $d instanceof FormElementDecorator && ! $d instanceof DecoratorInterface) {
+ throw new InvalidArgumentException(sprintf(
+ "Expected instance of %s for decorator '%s',"
+ . " got %s from a decorator loader instead",
+ FormElementDecorator::class,
+ $decorator,
+ get_php_type($d)
+ ));
+ }
+
+ $this->defaultElementDecorator = $d;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get the value of the element specified by name
+ *
+ * Returns $default if the element does not exist or has no value.
+ *
+ * @param string $name
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function getValue($name, $default = null)
+ {
+ if ($this->hasElement($name)) {
+ $value = $this->getElement($name)->getValue();
+ if ($value !== null) {
+ return $value;
+ }
+ }
+
+ return $default;
+ }
+
+ /**
+ * Get the values for all but ignored elements
+ *
+ * @return array Values as name-value pairs
+ */
+ public function getValues()
+ {
+ $values = [];
+ foreach ($this->getElements() as $element) {
+ if (! $element->isIgnored()) {
+ $values[$element->getName()] = $element->getValue();
+ }
+ }
+
+ return $values;
+ }
+
+ /**
+ * Populate values of registered elements
+ *
+ * @param iterable $values Values as name-value pairs
+ *
+ * @return $this
+ */
+ public function populate($values)
+ {
+ foreach ($values as $name => $value) {
+ $this->populatedValues[$name][] = $value;
+ if ($this->hasElement($name)) {
+ $this->getElement($name)->setValue($value);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get the populated value of the element specified by name
+ *
+ * Returns $default if there is no populated value for this element.
+ *
+ * @param string $name
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function getPopulatedValue($name, $default = null)
+ {
+ return isset($this->populatedValues[$name])
+ ? $this->populatedValues[$name][count($this->populatedValues[$name]) - 1]
+ : $default;
+ }
+
+ /**
+ * Clear populated value of the given element
+ *
+ * @param string $name
+ *
+ * @return $this
+ */
+ public function clearPopulatedValue($name)
+ {
+ if (! $this->hasBeenSubmitted() && isset($this->populatedValues[$name])) {
+ unset($this->populatedValues[$name]);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add all elements from the given element collection
+ *
+ * @param Form|SubFormElement $form
+ *
+ * @return $this
+ */
+ public function addElementsFrom($form)
+ {
+ foreach ($form->getElements() as $element) {
+ $this->addElement($element);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add a decorator loader
+ *
+ * @param string $namespace Namespace of the decorators
+ * @param string $postfix Decorator name postfix, if any
+ *
+ * @return $this
+ */
+ public function addDecoratorLoader($namespace, $postfix = null)
+ {
+ $this->addPluginLoader('decorator', $namespace, $postfix);
+
+ return $this;
+ }
+
+ /**
+ * Add an element loader
+ *
+ * @param string $namespace Namespace of the elements
+ * @param string $postfix Element name postfix, if any
+ *
+ * @return $this
+ */
+ public function addElementLoader($namespace, $postfix = null)
+ {
+ $this->addPluginLoader('element', $namespace, $postfix);
+
+ return $this;
+ }
+
+ /**
+ * Ensure that our default element decorator loader is registered
+ *
+ * @return $this
+ */
+ protected function ensureDefaultElementDecoratorLoaderRegistered()
+ {
+ if (! $this->defaultElementDecoratorLoaderRegistered) {
+ $this->addDefaultPluginLoader(
+ 'decorator',
+ 'ipl\\Html\\FormDecorator',
+ 'Decorator'
+ );
+
+ $this->defaultElementDecoratorLoaderRegistered = true;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Ensure that our default element loader is registered
+ *
+ * @return $this
+ */
+ protected function ensureDefaultElementLoaderRegistered()
+ {
+ if (! $this->defaultElementLoaderRegistered) {
+ $this->addDefaultPluginLoader('element', __NAMESPACE__, 'Element');
+
+ $this->defaultElementLoaderRegistered = true;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Decorate the given element
+ *
+ * @param FormElement $element
+ *
+ * @return $this
+ *
+ * @throws UnexpectedValueException If the default decorator is set but not an instance of
+ * {@link FormElementDecorator}
+ */
+ protected function decorate(FormElement $element)
+ {
+ if ($this->hasDefaultElementDecorator()) {
+ $decorator = $this->getDefaultElementDecorator();
+
+ if (! $decorator instanceof FormElementDecorator && ! $decorator instanceof DecoratorInterface) {
+ throw new UnexpectedValueException(sprintf(
+ '%s expects the default decorator to be an instance of %s, got %s instead',
+ __METHOD__,
+ FormElementDecorator::class,
+ get_php_type($decorator)
+ ));
+ }
+
+ $decorator->decorate($element);
+ }
+
+ return $this;
+ }
+
+ public function isValidEvent($event)
+ {
+ return in_array($event, [
+ Form::ON_SUCCESS,
+ Form::ON_SENT,
+ Form::ON_ERROR,
+ Form::ON_REQUEST,
+ Form::ON_VALIDATE,
+ Form::ON_ELEMENT_REGISTERED,
+ ]);
+ }
+
+ public function remove(ValidHtml $elementOrHtml)
+ {
+ if ($elementOrHtml instanceof FormElement) {
+ if ($this->hasElement($elementOrHtml)) {
+ $name = array_search($elementOrHtml, $this->elements, true);
+ if ($name !== false) {
+ unset($this->elements[$name]);
+ }
+ }
+ }
+
+ parent::remove($elementOrHtml);
+ }
+
+ /**
+ * Handler which is called after an element has been registered
+ *
+ * @param FormElement $element
+ */
+ protected function onElementRegistered(FormElement $element)
+ {
+ }
+}
diff --git a/vendor/ipl/html/src/FormElement/HiddenElement.php b/vendor/ipl/html/src/FormElement/HiddenElement.php
new file mode 100644
index 0000000..bffc7eb
--- /dev/null
+++ b/vendor/ipl/html/src/FormElement/HiddenElement.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace ipl\Html\FormElement;
+
+class HiddenElement extends InputElement
+{
+ protected $type = 'hidden';
+}
diff --git a/vendor/ipl/html/src/FormElement/InputElement.php b/vendor/ipl/html/src/FormElement/InputElement.php
new file mode 100644
index 0000000..d5f945d
--- /dev/null
+++ b/vendor/ipl/html/src/FormElement/InputElement.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace ipl\Html\FormElement;
+
+use ipl\Html\Attribute;
+use ipl\Html\Attributes;
+
+class InputElement extends BaseFormElement
+{
+ /** @var string Type of the input */
+ protected $type;
+
+ protected $tag = 'input';
+
+ /**
+ * Get the type of the input
+ *
+ * @return string
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+
+ /**
+ * Set the type of the input
+ *
+ * @param string $type
+ *
+ * @return $this
+ */
+ public function setType($type)
+ {
+ $this->type = (string) $type;
+
+ return $this;
+ }
+
+ protected function registerAttributeCallbacks(Attributes $attributes)
+ {
+ parent::registerAttributeCallbacks($attributes);
+
+ $attributes->registerAttributeCallback(
+ 'type',
+ [$this, 'getType'],
+ [$this, 'setType']
+ );
+ }
+}
diff --git a/vendor/ipl/html/src/FormElement/LocalDateTimeElement.php b/vendor/ipl/html/src/FormElement/LocalDateTimeElement.php
new file mode 100644
index 0000000..e165c7d
--- /dev/null
+++ b/vendor/ipl/html/src/FormElement/LocalDateTimeElement.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace ipl\Html\FormElement;
+
+use DateTime;
+use ipl\Validator\DateTimeValidator;
+
+class LocalDateTimeElement extends InputElement
+{
+ const FORMAT = 'Y-m-d\TH:i:s';
+
+ protected $type = 'datetime-local';
+
+ protected $defaultAttributes = ['step' => '1'];
+
+ /** @var DateTime */
+ protected $value;
+
+ public function setValue($value)
+ {
+ if (is_string($value)) {
+ $originalVal = $value;
+ $value = DateTime::createFromFormat(static::FORMAT, $value);
+ // In Chrome, if the seconds are set to 00, DateTime::createFromFormat() returns false.
+ // Create DateTime without seconds in format
+ if ($value === false) {
+ $format = substr(static::FORMAT, 0, strrpos(static::FORMAT, ':'));
+ $value = DateTime::createFromFormat($format, $originalVal);
+ }
+
+ if ($value === false) {
+ $value = $originalVal;
+ }
+ }
+
+ return parent::setValue($value);
+ }
+
+ public function getValueAttribute()
+ {
+ if (! $this->value instanceof DateTime) {
+ return $this->value;
+ }
+
+ return $this->value->format(static::FORMAT);
+ }
+
+ public function addDefaultValidators()
+ {
+ $this->getValidators()->add(new DateTimeValidator());
+ }
+}
diff --git a/vendor/ipl/html/src/FormElement/NumberElement.php b/vendor/ipl/html/src/FormElement/NumberElement.php
new file mode 100644
index 0000000..b593135
--- /dev/null
+++ b/vendor/ipl/html/src/FormElement/NumberElement.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace ipl\Html\FormElement;
+
+class NumberElement extends InputElement
+{
+ protected $type = 'number';
+}
diff --git a/vendor/ipl/html/src/FormElement/PasswordElement.php b/vendor/ipl/html/src/FormElement/PasswordElement.php
new file mode 100644
index 0000000..6e0e25f
--- /dev/null
+++ b/vendor/ipl/html/src/FormElement/PasswordElement.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace ipl\Html\FormElement;
+
+use ipl\Html\Attributes;
+use ipl\Html\Form;
+
+class PasswordElement extends InputElement
+{
+ /** @var string Dummy passwd of this element to be rendered */
+ const DUMMYPASSWORD = '_ipl_form_5847ed1b5b8ca';
+
+ protected $type = 'password';
+
+ /** @var bool Status of the form */
+ protected $isFormValid = true;
+
+ protected function registerAttributeCallbacks(Attributes $attributes)
+ {
+ parent::registerAttributeCallbacks($attributes);
+
+ $attributes->registerAttributeCallback(
+ 'value',
+ function () {
+ if ($this->hasValue() && count($this->getValueCandidates()) === 1 && $this->isFormValid) {
+ return self::DUMMYPASSWORD;
+ }
+
+ if (parent::getValue() === self::DUMMYPASSWORD) {
+ return self::DUMMYPASSWORD;
+ }
+
+ return null;
+ }
+ );
+ }
+
+ public function onRegistered(Form $form)
+ {
+ $form->on(Form::ON_VALIDATE, function ($form) {
+ $this->isFormValid = $form->isValid();
+ });
+ }
+
+ public function getValue()
+ {
+ $value = parent::getValue();
+ $candidates = $this->getValueCandidates();
+ while ($value === self::DUMMYPASSWORD) {
+ $value = array_pop($candidates);
+ }
+
+ return $value;
+ }
+}
diff --git a/vendor/ipl/html/src/FormElement/SelectElement.php b/vendor/ipl/html/src/FormElement/SelectElement.php
new file mode 100644
index 0000000..3933222
--- /dev/null
+++ b/vendor/ipl/html/src/FormElement/SelectElement.php
@@ -0,0 +1,144 @@
+<?php
+
+namespace ipl\Html\FormElement;
+
+use ipl\Html\Html;
+
+class SelectElement extends BaseFormElement
+{
+ protected $tag = 'select';
+
+ /** @var SelectOption[] */
+ protected $options = [];
+
+ protected $optionContent = [];
+
+ public function __construct($name, $attributes = null)
+ {
+ $this->getAttributes()->registerAttributeCallback(
+ 'options',
+ null,
+ [$this, 'setOptions']
+ );
+ // ZF1 compatibility:
+ $this->getAttributes()->registerAttributeCallback(
+ 'multiOptions',
+ null,
+ [$this, 'setOptions']
+ );
+
+ parent::__construct($name, $attributes);
+ }
+
+ public function hasOption($value)
+ {
+ return isset($this->options[$value]);
+ }
+
+ public function validate()
+ {
+ $value = $this->getValue();
+ if (
+ $value !== null && (
+ ! ($option = $this->getOption($value))
+ || $option->getAttributes()->has('disabled')
+ )
+ ) {
+ $this->valid = false;
+ $this->addMessage("'$value' is not allowed here");
+ } elseif ($this->isRequired() && $value === null) {
+ $this->valid = false;
+ } else {
+ parent::validate();
+ }
+
+ return $this;
+ }
+
+ public function deselect()
+ {
+ $this->setValue(null);
+
+ return $this;
+ }
+
+ public function disableOption($value)
+ {
+ if ($option = $this->getOption($value)) {
+ $option->getAttributes()->add('disabled', true);
+ }
+ if ($this->getValue() == $value) {
+ $this->valid = false;
+ $this->addMessage("'$value' is not allowed here");
+ }
+
+ return $this;
+ }
+
+ public function disableOptions($values)
+ {
+ foreach ($values as $value) {
+ $this->disableOption($value);
+ }
+
+ return $this;
+ }
+
+ /**
+ * @param $value
+ * @return SelectOption|null
+ */
+ public function getOption($value)
+ {
+ if ($this->hasOption($value)) {
+ return $this->options[$value];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * @param array $options
+ * @return $this
+ */
+ public function setOptions(array $options)
+ {
+ $this->options = [];
+ foreach ($options as $value => $label) {
+ $this->optionContent[$value] = $this->makeOption($value, $label);
+ }
+
+ return $this;
+ }
+
+ protected function makeOption($value, $label)
+ {
+ if (is_array($label)) {
+ $grp = Html::tag('optgroup', ['label' => $value]);
+ foreach ($label as $option => $val) {
+ $grp->addHtml($this->makeOption($option, $val));
+ }
+
+ return $grp;
+ } else {
+ $option = new SelectOption($value, $label);
+ $option->getAttributes()->registerAttributeCallback('selected', function () use ($option) {
+ $optionValue = $option->getValue();
+
+ return is_int($optionValue)
+ // The loose comparison is required because PHP casts
+ // numeric strings to integers if used as array keys
+ ? $this->getValue() == $optionValue
+ : $this->getValue() === $optionValue;
+ });
+ $this->options[$value] = $option;
+
+ return $this->options[$value];
+ }
+ }
+
+ protected function assemble()
+ {
+ $this->addHtml(...array_values($this->optionContent));
+ }
+}
diff --git a/vendor/ipl/html/src/FormElement/SelectOption.php b/vendor/ipl/html/src/FormElement/SelectOption.php
new file mode 100644
index 0000000..d0dbf7c
--- /dev/null
+++ b/vendor/ipl/html/src/FormElement/SelectOption.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace ipl\Html\FormElement;
+
+use ipl\Html\BaseHtmlElement;
+
+class SelectOption extends BaseHtmlElement
+{
+ protected $tag = 'option';
+
+ /** @var mixed */
+ protected $value;
+
+ /**
+ * SelectOption constructor.
+ * @param string|null $value
+ * @param string|null $label
+ */
+ public function __construct($value = null, $label = null)
+ {
+ $this->value = $value;
+ $this->add($label);
+
+ $this->getAttributes()->registerAttributeCallback('value', [$this, 'getValue']);
+ }
+
+ /**
+ * @param $label
+ * @return $this
+ */
+ public function setLabel($label)
+ {
+ $this->setContent($label);
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+}
diff --git a/vendor/ipl/html/src/FormElement/SubFormElement.php b/vendor/ipl/html/src/FormElement/SubFormElement.php
new file mode 100644
index 0000000..5e0f77e
--- /dev/null
+++ b/vendor/ipl/html/src/FormElement/SubFormElement.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace ipl\Html\FormElement;
+
+use ipl\Html\Attributes;
+
+class SubFormElement extends BaseFormElement
+{
+ use FormElements;
+
+ protected $tag = 'div';
+
+ protected $defaultAttributes = [
+ 'class' => 'ipl-subform'
+ ];
+
+ public function getValue($name = null)
+ {
+ if ($name === null) {
+ return $this->getValues();
+ } else {
+ return $this->getElement($name)->getValue();
+ }
+ }
+
+ public function setValue($value)
+ {
+ $this->populate($value);
+
+ return $this;
+ }
+
+ public function isValid()
+ {
+ foreach ($this->getElements() as $element) {
+ if (! $element->isValid()) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public function hasSubmitButton()
+ {
+ return true;
+ }
+
+ protected function registerValueCallback(Attributes $attributes)
+ {
+ $attributes->registerAttributeCallback(
+ 'value',
+ null,
+ [$this, 'setValue']
+ );
+ }
+}
diff --git a/vendor/ipl/html/src/FormElement/SubmitButtonElement.php b/vendor/ipl/html/src/FormElement/SubmitButtonElement.php
new file mode 100644
index 0000000..e94b681
--- /dev/null
+++ b/vendor/ipl/html/src/FormElement/SubmitButtonElement.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace ipl\Html\FormElement;
+
+use ipl\Html\Contract\FormSubmitElement;
+
+class SubmitButtonElement extends ButtonElement implements FormSubmitElement
+{
+ protected $defaultAttributes = ['type' => 'submit'];
+
+ protected $value = 'y';
+
+ public function setLabel($label)
+ {
+ return $this->setContent($label);
+ }
+
+ public function hasBeenPressed()
+ {
+ return (bool) $this->getValue();
+ }
+
+ public function isIgnored()
+ {
+ return true;
+ }
+}
diff --git a/vendor/ipl/html/src/FormElement/SubmitElement.php b/vendor/ipl/html/src/FormElement/SubmitElement.php
new file mode 100644
index 0000000..51d4aa5
--- /dev/null
+++ b/vendor/ipl/html/src/FormElement/SubmitElement.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace ipl\Html\FormElement;
+
+use ipl\Html\Attribute;
+use ipl\Html\Contract\FormSubmitElement;
+
+class SubmitElement extends InputElement implements FormSubmitElement
+{
+ protected $type = 'submit';
+
+ protected $buttonLabel;
+
+ public function setLabel($label)
+ {
+ $this->buttonLabel = $label;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getButtonLabel()
+ {
+ if ($this->buttonLabel === null) {
+ return $this->getName();
+ } else {
+ return $this->buttonLabel;
+ }
+ }
+
+ /**
+ * @return mixed|static
+ */
+ public function getValueAttribute()
+ {
+ return new Attribute('value', $this->getButtonLabel());
+ }
+
+ public function hasBeenPressed()
+ {
+ return $this->getButtonLabel() === $this->getValue();
+ }
+
+ public function isIgnored()
+ {
+ return true;
+ }
+}
diff --git a/vendor/ipl/html/src/FormElement/TextElement.php b/vendor/ipl/html/src/FormElement/TextElement.php
new file mode 100644
index 0000000..0e3423d
--- /dev/null
+++ b/vendor/ipl/html/src/FormElement/TextElement.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace ipl\Html\FormElement;
+
+class TextElement extends InputElement
+{
+ protected $type = 'text';
+}
diff --git a/vendor/ipl/html/src/FormElement/TextareaElement.php b/vendor/ipl/html/src/FormElement/TextareaElement.php
new file mode 100644
index 0000000..dc5c42b
--- /dev/null
+++ b/vendor/ipl/html/src/FormElement/TextareaElement.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace ipl\Html\FormElement;
+
+class TextareaElement extends BaseFormElement
+{
+ protected $tag = 'textarea';
+
+ public function setValue($value)
+ {
+ parent::setValue($value);
+
+ // A textarea's content actually is the value
+ $this->setContent($value);
+
+ return $this;
+ }
+
+ public function getValueAttribute()
+ {
+ // textarea elements don't have a value attribute
+ return null;
+ }
+}
diff --git a/vendor/ipl/html/src/FormElement/TimeElement.php b/vendor/ipl/html/src/FormElement/TimeElement.php
new file mode 100644
index 0000000..1ee0323
--- /dev/null
+++ b/vendor/ipl/html/src/FormElement/TimeElement.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace ipl\Html\FormElement;
+
+class TimeElement extends InputElement
+{
+ protected $type = 'time';
+}