931 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			931 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
namespace nur\v\bs3;
 | 
						|
 | 
						|
use nur\A;
 | 
						|
use nur\b\ValueException;
 | 
						|
use nur\func;
 | 
						|
use nur\md;
 | 
						|
use nur\v\model\IFormManager;
 | 
						|
use nur\v\v;
 | 
						|
 | 
						|
class Bs3FormManagerOrig implements IFormManager {
 | 
						|
  const TYPE_BASIC = "basic";
 | 
						|
  const TYPE_HORIZONTAL = "horizontal";
 | 
						|
  const TYPE_INLINE = "inline";
 | 
						|
  const TYPE_NAVBAR = "navbar";
 | 
						|
 | 
						|
  const DEFAULT_TYPE = self::TYPE_HORIZONTAL;
 | 
						|
 | 
						|
  function __construct($options=null, ?array $schema=null) {
 | 
						|
    $this->resetManager($options, $schema);
 | 
						|
  }
 | 
						|
 | 
						|
  const MANAGER_OPTIONS_SCHEMA = [
 | 
						|
    "type" => [null, null, "type de formulaire par défaut"],
 | 
						|
  ];
 | 
						|
 | 
						|
  /** @var array */
 | 
						|
  protected $stack;
 | 
						|
 | 
						|
  /** @var array */
 | 
						|
  protected $options;
 | 
						|
 | 
						|
  protected $type;
 | 
						|
  /** @var bool est-ce un type "horizontal" par opposition à "inline" */
 | 
						|
  protected $horizt;
 | 
						|
  protected $fgs_class, $fgl_class, $fgc_class;
 | 
						|
  protected $auto_hide_label;
 | 
						|
 | 
						|
  /** @var array */
 | 
						|
  protected $schema;
 | 
						|
 | 
						|
  function setState(array $options, ?array $schema): void {
 | 
						|
    $type = $options["type"];
 | 
						|
    switch ($type) {
 | 
						|
    case self::TYPE_BASIC:
 | 
						|
    case "b":
 | 
						|
    case "form":
 | 
						|
    case "f":
 | 
						|
      $this->type = self::TYPE_BASIC;
 | 
						|
      $this->horizt = true;
 | 
						|
      $this->fgs_class = false;
 | 
						|
      $this->fgl_class = false;
 | 
						|
      $this->fgc_class = false;
 | 
						|
      break;
 | 
						|
    case self::TYPE_HORIZONTAL:
 | 
						|
    case "horiz":
 | 
						|
    case "h":
 | 
						|
      $this->type = self::TYPE_HORIZONTAL;
 | 
						|
      $this->horizt = true;
 | 
						|
      $this->fgs_class = true;
 | 
						|
      $this->fgl_class = true;
 | 
						|
      $this->fgc_class = true;
 | 
						|
      break;
 | 
						|
    case self::TYPE_INLINE:
 | 
						|
    case "i":
 | 
						|
      $this->type = self::TYPE_INLINE;
 | 
						|
      $this->horizt = false;
 | 
						|
      $this->fgs_class = false;
 | 
						|
      $this->fgl_class = false;
 | 
						|
      $this->fgc_class = false;
 | 
						|
      break;
 | 
						|
    case self::TYPE_NAVBAR:
 | 
						|
    case "n":
 | 
						|
      $this->type = self::TYPE_NAVBAR;
 | 
						|
      $this->horizt = false;
 | 
						|
      $this->fgs_class = false;
 | 
						|
      $this->fgl_class = false;
 | 
						|
      $this->fgc_class = false;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      throw new ValueException("$type: invalid form type");
 | 
						|
    }
 | 
						|
    $this->auto_hide_label = true;
 | 
						|
    $this->options = $options;
 | 
						|
    $this->schema = $schema;
 | 
						|
  }
 | 
						|
 | 
						|
  function resetManager($options=null, ?array $schema=null): void {
 | 
						|
    $this->stack = [];
 | 
						|
    md::ensure_schema($options, self::MANAGER_OPTIONS_SCHEMA);
 | 
						|
    $this->options = $options;
 | 
						|
    $this->schema = $schema;
 | 
						|
    $this->inForm = false;
 | 
						|
    $this->inSection = false;
 | 
						|
    $this->inGroup = false;
 | 
						|
  }
 | 
						|
 | 
						|
  function push($options=null, ?array $schema=null): void {
 | 
						|
    A::push($this->stack, [
 | 
						|
      "options" => $this->options,
 | 
						|
      "type" => $this->type,
 | 
						|
      "horizt" => $this->horizt,
 | 
						|
      "fgs_class" => $this->fgs_class,
 | 
						|
      "fgl_class" => $this->fgl_class,
 | 
						|
      "fgc_class" => $this->fgc_class,
 | 
						|
      "auto_hide_label" => $this->auto_hide_label,
 | 
						|
      "schema" => $this->schema,
 | 
						|
    ]);
 | 
						|
    if ($options !== null) {
 | 
						|
      md::ensure_schema($options, self::MANAGER_OPTIONS_SCHEMA);
 | 
						|
      $this->options = $options;
 | 
						|
    }
 | 
						|
    if ($schema !== null) $this->schema = $schema;
 | 
						|
  }
 | 
						|
 | 
						|
  function pop(): void {
 | 
						|
    $last = A::pop($this->stack);
 | 
						|
    if ($last !== null) {
 | 
						|
      [
 | 
						|
        "options" => $this->options,
 | 
						|
        "type" => $this->type,
 | 
						|
        "horizt" => $this->horizt,
 | 
						|
        "fgs_class" => $this->fgs_class,
 | 
						|
        "fgl_class" => $this->fgl_class,
 | 
						|
        "fgc_class" => $this->fgc_class,
 | 
						|
        "auto_hide_label" => $this->auto_hide_label,
 | 
						|
        "schema" => $this->schema,
 | 
						|
      ] = $last;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  protected static function build_attrs(?array $attrs, array $options, ?array $set_keys=null, ?array $merge_keys=null): array {
 | 
						|
    A::update_nx($attrs, $options["attrs"]);
 | 
						|
    if ($set_keys !== null) {
 | 
						|
      foreach ($set_keys as $key) {
 | 
						|
        A::set_nz($attrs, $key, $options[$key]);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    if ($merge_keys !== null) {
 | 
						|
      foreach ($merge_keys as $key) {
 | 
						|
        if ($options[$key]) A::merge($attrs[$key], $options[$key]);
 | 
						|
        A::set_nz($attrs, $key, $options[$key]);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return $attrs;
 | 
						|
  }
 | 
						|
 | 
						|
  /** @var bool */
 | 
						|
  protected $inForm;
 | 
						|
 | 
						|
  /** @var array */
 | 
						|
  protected $formSuffix;
 | 
						|
 | 
						|
  function started(): bool {
 | 
						|
    return $this->inForm;
 | 
						|
  }
 | 
						|
 | 
						|
  const FORM_OPTIONS_SCHEMA = [
 | 
						|
    "type" => [null, null, "type de formulaire"],
 | 
						|
    "action" => [null, null, "action du formulaire"],
 | 
						|
    "method" => [null, null, "méthode du formulaire (post, get)"],
 | 
						|
    "upload" => [null, null, "ce formulaire est-il utilisé pour uploader des fichiers?"],
 | 
						|
    "id" => [null, null, "identifiant du formulaire"],
 | 
						|
    "enctype" => [null, null, "type d'encodage"],
 | 
						|
    "class" => [null, null, "classes CSS du formulaire"],
 | 
						|
    "style" => [null, null, "style CSS du formulaire"],
 | 
						|
    "attrs" => [null, null, "attributs HTML génériques"],
 | 
						|
    "prefix" => [null, null, "contenu à afficher avant"],
 | 
						|
    "suffix" => [null, null, "contenu à afficher après"],
 | 
						|
  ];
 | 
						|
 | 
						|
  function start($options=null, ?array $schema=null): array {
 | 
						|
    $vs = [];
 | 
						|
    if ($this->inForm) $vs[] = $this->end();
 | 
						|
 | 
						|
    $this->push(null, $schema);
 | 
						|
    $this->inForm = true;
 | 
						|
 | 
						|
    md::ensure_schema($options, self::FORM_OPTIONS_SCHEMA);
 | 
						|
    A::replace_z($options, "type", $this->options["type"]);
 | 
						|
 | 
						|
    $vs[] = q($options["prefix"]);
 | 
						|
    $this->formSuffix = q($options["suffix"]);
 | 
						|
 | 
						|
    $attrs = self::build_attrs(null, $options, [
 | 
						|
      "action", "method", "id", "enctype", "style",
 | 
						|
    ], ["class"]);
 | 
						|
    if ($options["upload"]) {
 | 
						|
      $attrs["method"] = "post";
 | 
						|
      $attrs["enctype"] = "multipart/form-data";
 | 
						|
    }
 | 
						|
    $vs[] = v::start("form", $attrs);
 | 
						|
 | 
						|
    return $vs;
 | 
						|
  }
 | 
						|
 | 
						|
  function end(): array {
 | 
						|
    if (!$this->inForm) return [];
 | 
						|
 | 
						|
    $vs = [];
 | 
						|
    if ($this->inGroup) $vs[] = $this->endGroup();
 | 
						|
    if ($this->inSection) $vs[] = $this->endSection();
 | 
						|
 | 
						|
    $vs[] = v::end("form");
 | 
						|
    $vs[] = $this->formSuffix;
 | 
						|
    $this->pop();
 | 
						|
    $this->inForm = false;
 | 
						|
    $this->formSuffix = null;
 | 
						|
 | 
						|
    return $vs;
 | 
						|
  }
 | 
						|
 | 
						|
  /** @var bool */
 | 
						|
  protected $inSection;
 | 
						|
 | 
						|
  /** @var array */
 | 
						|
  protected $sectionSuffix;
 | 
						|
 | 
						|
  const SECTION_OPTIONS_SCHEMA = [
 | 
						|
    "section" => [null, null, "libellé de la section"],
 | 
						|
    "id" => [null, null, "identifiant du champ"],
 | 
						|
    "class" => [null, null, "classes CSS du champ"],
 | 
						|
    "style" => [null, null, "style CSS du champ"],
 | 
						|
    "attrs" => [null, null, "attributs HTML génériques"],
 | 
						|
    "prefix" => [null, null, "contenu à afficher avant"],
 | 
						|
    "suffix" => [null, null, "contenu à afficher après"],
 | 
						|
  ];
 | 
						|
 | 
						|
  function section($options=null): array {
 | 
						|
    if (!$this->inForm) return [];
 | 
						|
 | 
						|
    md::ensure_schema($options, self::SECTION_OPTIONS_SCHEMA);
 | 
						|
 | 
						|
    $vs = [];
 | 
						|
    if ($this->inGroup) $vs[] = $this->endGroup();
 | 
						|
    if ($this->inSection) $vs[] = $this->endSection();
 | 
						|
    $this->inSection = true;
 | 
						|
 | 
						|
    $vs[] = q($options["prefix"]);
 | 
						|
    $this->sectionSuffix = q($options["suffix"]);
 | 
						|
 | 
						|
    $attrs = self::build_attrs(null, $options, [
 | 
						|
      "id", "style",
 | 
						|
    ], ["class"]);
 | 
						|
    $vs[] = v::tag("h2", [$attrs, q($options["section"])]);
 | 
						|
 | 
						|
    return $vs;
 | 
						|
  }
 | 
						|
  function endSection(): array {
 | 
						|
    if (!$this->inSection) return [];
 | 
						|
 | 
						|
    $vs = [$this->sectionSuffix];
 | 
						|
    $this->inSection = false;
 | 
						|
    $this->sectionSuffix = null;
 | 
						|
 | 
						|
    return $vs;
 | 
						|
  }
 | 
						|
 | 
						|
  /** @var bool */
 | 
						|
  protected $inGroup;
 | 
						|
  /** @var array */
 | 
						|
  protected $groupSuffix;
 | 
						|
 | 
						|
  const GROUP_OPTIONS_SCHEMA = [
 | 
						|
    "label" => [null, null, "libellé du groupe"],
 | 
						|
    "prefix" => [null, null, "contenu à afficher avant"],
 | 
						|
    "suffix" => [null, null, "contenu à afficher après"],
 | 
						|
  ];
 | 
						|
 | 
						|
  function group($options=null): array {
 | 
						|
    if (!$this->inForm) return [];
 | 
						|
 | 
						|
    md::ensure_schema($options, self::GROUP_OPTIONS_SCHEMA);
 | 
						|
 | 
						|
    $vs = [];
 | 
						|
    if ($this->inGroup) $vs[] = $this->endGroup();
 | 
						|
    $this->inGroup = true;
 | 
						|
 | 
						|
    $vs[] = q($options["prefix"]);
 | 
						|
    $this->groupSuffix = q($options["suffix"]);
 | 
						|
 | 
						|
    $vs[] = v::start("p", q($options["label"]));
 | 
						|
 | 
						|
    return $vs;
 | 
						|
  }
 | 
						|
  function endGroup(): array {
 | 
						|
    if (!$this->inGroup) return [];
 | 
						|
 | 
						|
    $vs = ["</p>", $this->groupSuffix];
 | 
						|
    $this->inGroup = false;
 | 
						|
    $this->groupSuffix = null;
 | 
						|
 | 
						|
    return $vs;
 | 
						|
  }
 | 
						|
 | 
						|
  const PREFIX = "\n";
 | 
						|
 | 
						|
  /** @var array contenu à afficher avant chaque élément de formulaire */
 | 
						|
  protected $prefix = [self::PREFIX];
 | 
						|
 | 
						|
  function setPrefix(?string $prefix=null) {
 | 
						|
    if ($prefix === null) $prefix = static::PREFIX;
 | 
						|
    $this->prefix = q($prefix);
 | 
						|
  }
 | 
						|
 | 
						|
  const SUFFIX = null;
 | 
						|
 | 
						|
  /** @var array contenu à afficher après chaque élément de formulaire */
 | 
						|
  protected $suffix = [self::SUFFIX];
 | 
						|
 | 
						|
  function setSuffix(?string $suffix=null) {
 | 
						|
    if ($suffix === null) $suffix = static::SUFFIX;
 | 
						|
    $this->suffix = q($suffix);
 | 
						|
  }
 | 
						|
 | 
						|
  const FIXED_OPTIONS_SCHEMA = [
 | 
						|
    "label" => [null, null, "libellé"],
 | 
						|
    "id" => [null, null, "identifiant"],
 | 
						|
    "value" => [null, null, "valeur"],
 | 
						|
    "class" => [null, null, "classes CSS du champ"],
 | 
						|
    "style" => [null, null, "style CSS du champ"],
 | 
						|
    "attrs" => [null, null, "attributs HTML génériques"],
 | 
						|
    "prefix" => [null, null, "contenu à afficher avant"],
 | 
						|
    "suffix" => [null, null, "contenu à afficher après"],
 | 
						|
  ];
 | 
						|
 | 
						|
  function fixed($label, string $name, $value, ?array $options=null): array {
 | 
						|
    md::ensure_schema($options, self::FIXED_OPTIONS_SCHEMA);
 | 
						|
    A::set_nz($options, "label", $label);
 | 
						|
    A::set_nz($options, "name", $name);
 | 
						|
    A::set_nz($options, "value", $value);
 | 
						|
    A::replace_n_indirect($options, "id", "name");
 | 
						|
 | 
						|
    $label = $options["label"];
 | 
						|
    $vs = [$this->prefix, q($options["prefix"])];
 | 
						|
    if ($label) $vs[] = v::start("label", q($label));
 | 
						|
 | 
						|
    $attrs = self::build_attrs(null, $options, [
 | 
						|
      "id", "style",
 | 
						|
    ], ["class"]);
 | 
						|
    $vs[] = v::tag("span", [$attrs, q($options["value"])]);
 | 
						|
 | 
						|
    if ($label) $vs[] = v::end("label");
 | 
						|
    $vs[] = q($options["suffix"]);
 | 
						|
    $vs[] = $this->suffix;
 | 
						|
    return $vs;
 | 
						|
  }
 | 
						|
 | 
						|
  const HIDDEN_OPTIONS_SCHEMA = [
 | 
						|
    "id" => [null, null, "identifiant du champ"],
 | 
						|
    "name" => [null, null, "nom du champ"],
 | 
						|
    "value" => [null, null, "valeur du champ"],
 | 
						|
    "attrs" => [null, null, "attributs HTML génériques"],
 | 
						|
  ];
 | 
						|
 | 
						|
  function hidden(string $name, $value, ?array $options=null): array {
 | 
						|
    md::ensure_schema($options, self::HIDDEN_OPTIONS_SCHEMA);
 | 
						|
    A::set_nz($options, "name", $name);
 | 
						|
    A::set_nz($options, "value", $value);
 | 
						|
 | 
						|
    $attrs = self::build_attrs(["type" => "hidden"], $options, [
 | 
						|
      "id", "name", "value",
 | 
						|
    ]);
 | 
						|
    return v::tag1("input", $attrs);
 | 
						|
  }
 | 
						|
  function hiddens(array $values, string ...$names): array {
 | 
						|
    if (!$names) $names = array_keys($values);
 | 
						|
    $vs = [];
 | 
						|
    foreach ($names as $name) {
 | 
						|
      $value = A::get($values, $name);
 | 
						|
      $vs[] = $this->hidden($name, $value);
 | 
						|
    }
 | 
						|
    return $vs;
 | 
						|
  }
 | 
						|
 | 
						|
  const INPUT_OPTIONS_SCHEMA = [
 | 
						|
    "label" => [null, null, "libellé du champ"],
 | 
						|
    "id" => [null, null, "identifiant du champ"],
 | 
						|
    "name" => [null, null, "nom du champ"],
 | 
						|
    "value" => [null, null, "valeur par défaut du champ"],
 | 
						|
    "placeholder" => [null, null, "valeur suggérée"],
 | 
						|
    "required" => [null, null, "ce champ est-il requis?"],
 | 
						|
    "accesskey" => [null, null, "touche d'accès rapide"],
 | 
						|
    "tabindex" => [null, null, "index de tabulation"],
 | 
						|
    "class" => [null, null, "classes CSS du champ"],
 | 
						|
    "style" => [null, null, "style CSS du champ"],
 | 
						|
    "attrs" => [null, null, "attributs HTML génériques"],
 | 
						|
    "prefix" => [null, null, "contenu à afficher avant"],
 | 
						|
    "suffix" => [null, null, "contenu à afficher après"],
 | 
						|
  ];
 | 
						|
 | 
						|
  protected function _input(string $type, $label, string $name, $value, ?array $options=null): array {
 | 
						|
    md::ensure_schema($options, self::INPUT_OPTIONS_SCHEMA);
 | 
						|
    A::set_nz($options, "label", $label);
 | 
						|
    A::set_nz($options, "name", $name);
 | 
						|
    A::set_nz($options, "value", $value);
 | 
						|
    A::replace_n_indirect($options, "id", "name");
 | 
						|
 | 
						|
    $label = $options["label"];
 | 
						|
    $vs = [$this->prefix, q($options["prefix"])];
 | 
						|
    if ($label) $vs[] = v::start("label", q($label));
 | 
						|
 | 
						|
    $attrs = self::build_attrs(["type" => $type], $options, [
 | 
						|
      "id", "name", "value", "placeholder", "accesskey", "tabindex", "style",
 | 
						|
    ], ["class"]);
 | 
						|
    if ($options["required"]) $attrs["required"] = "required";
 | 
						|
    $vs[] = v::tag1("input", $attrs);
 | 
						|
 | 
						|
    if ($label) $vs[] = v::end("label");
 | 
						|
    $vs[] = q($options["suffix"]);
 | 
						|
    $vs[] = $this->suffix;
 | 
						|
    return $vs;
 | 
						|
  }
 | 
						|
 | 
						|
  function text($label, string $name, $value, ?array $options=null): array {
 | 
						|
    return $this->_input("text", $label, $name, $value, $options);
 | 
						|
  }
 | 
						|
  function texts($label, array $values, string ...$names): array {
 | 
						|
    if (!$names) $names = array_keys($values);
 | 
						|
    $vs = [];
 | 
						|
    if ($label) $vs[] = q($label);
 | 
						|
    foreach ($names as $name) {
 | 
						|
      $value = A::get($values, $name);
 | 
						|
      $vs[] = $this->text(null, $name, $value);
 | 
						|
    }
 | 
						|
    return $vs;
 | 
						|
  }
 | 
						|
 | 
						|
  function password($label, string $name, $value, ?array $options=null): array {
 | 
						|
    return $this->_input("password", $label, $name, $value, $options);
 | 
						|
  }
 | 
						|
 | 
						|
  const SELECT_OPTIONS_SCHEMA = [
 | 
						|
    "label" => [null, null, "libellé du champ"],
 | 
						|
    "id" => [null, null, "identifiant du champ"],
 | 
						|
    "name" => [null, null, "nom du champ"],
 | 
						|
    "value" => [null, null, "valeur par défaut du champ"],
 | 
						|
    "required" => [null, null, "ce champ est-il requis?"],
 | 
						|
    "accesskey" => [null, null, "touche d'accès rapide"],
 | 
						|
    "tabindex" => [null, null, "index de tabulation"],
 | 
						|
    "items" => [null, null, "liste d'éléments à afficher"],
 | 
						|
    "items_func" => [null, null, "fonction fournissant une liste d'éléments à afficher"],
 | 
						|
    "item_value_key" => [null, null, "clé de la valeur d'un élément"],
 | 
						|
    "item_value_func" => [null, null, "fonction fournissant la clé de la valeur d'un élément"],
 | 
						|
    "item_text_key" => [null, null, "clé du libellé d'un élément"],
 | 
						|
    "item_text_func" => [null, null, "fonction fournissant la clé du libellé d'un élément"],
 | 
						|
    "no_item_value" => [null, null, "valeur si aucun élément n'est sélectionné"],
 | 
						|
    "no_item_text" => [null, null, "libellé si aucun élément n'est sélectionné"],
 | 
						|
    "class" => [null, null, "classes CSS du champ"],
 | 
						|
    "style" => [null, null, "style CSS du champ"],
 | 
						|
    "attrs" => [null, null, "attributs HTML génériques"],
 | 
						|
    "prefix" => [null, null, "contenu à afficher avant"],
 | 
						|
    "suffix" => [null, null, "contenu à afficher après"],
 | 
						|
    "option_prefix" => [null, null, "contenu à afficher avant chaque option"],
 | 
						|
    "option_suffix" => [null, null, "contenu à afficher après chaque option"],
 | 
						|
  ];
 | 
						|
 | 
						|
  private function vof(
 | 
						|
    array $options,
 | 
						|
    ?string $value_key=null,
 | 
						|
    $value_func=null,
 | 
						|
    $item=null, ?string $item_key=null, ?int $item_index = null,
 | 
						|
    ...$item_func_args) {
 | 
						|
    if ($value_key !== null) {
 | 
						|
      $value = A::get($options, $value_key);
 | 
						|
      if ($value !== null) return $value;
 | 
						|
    }
 | 
						|
    if ($value_func !== null) {
 | 
						|
      $func = A::get($options, $value_func);
 | 
						|
      if ($func !== null) {
 | 
						|
        func::ensure_func($func, $this, $item_func_args);
 | 
						|
        $value = func::call($func, ...$item_func_args);
 | 
						|
        if ($value !== null) return $value;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    if (A::is_array($item)) {
 | 
						|
      $array_item = A::with($item);
 | 
						|
      if ($item_key !== null) {
 | 
						|
        $value_key = A::get($options, $item_key);
 | 
						|
        if ($value_key !== null) {
 | 
						|
          $value = A::get($array_item, $value_key);
 | 
						|
          if ($value !== null) return $value;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      if ($item_index !== null) {
 | 
						|
        $index = 0;
 | 
						|
        foreach ($array_item as $value) {
 | 
						|
          if ($index === $item_index) {
 | 
						|
            return $value;
 | 
						|
          }
 | 
						|
          $index++;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return $item;
 | 
						|
  }
 | 
						|
 | 
						|
  function select($label, string $name, $value, ?array $options=null): array {
 | 
						|
    md::ensure_schema($options, self::SELECT_OPTIONS_SCHEMA);
 | 
						|
    A::set_nz($options, "label", $label);
 | 
						|
    A::set_nz($options, "name", $name);
 | 
						|
    A::set_nz($options, "value", $value);
 | 
						|
    A::replace_n_indirect($options, "id", "name");
 | 
						|
 | 
						|
    $value = $options["value"];
 | 
						|
    $items = $this->vof($options, "items", "items_func");
 | 
						|
    $no_item_value = $options["no_item_value"];
 | 
						|
    $no_item_text = $options["no_item_text"];
 | 
						|
 | 
						|
    $oos = array();
 | 
						|
    if ($no_item_value !== null) {
 | 
						|
      $oos[] = array(
 | 
						|
        "value" => $no_item_value,
 | 
						|
        "text" => $no_item_text,
 | 
						|
        "selected" => $value == $no_item_value,
 | 
						|
      );
 | 
						|
    }
 | 
						|
    foreach ($items as $key => $item) {
 | 
						|
      $item_value = $this->vof($options, null, "item_value_func", $item, "item_value_key", 0, $item, $key);
 | 
						|
      $item_text = $this->vof($options, null, "item_text_func", $item, "item_text_key", 1, $item, $key);
 | 
						|
      if (!$item_text) $item_text = $item_value;
 | 
						|
      $oos[] = array(
 | 
						|
        "value" => $item_value,
 | 
						|
        "text" => $item_text,
 | 
						|
        "selected" => $value == $item_value,
 | 
						|
      );
 | 
						|
    }
 | 
						|
 | 
						|
    $label = $options["label"];
 | 
						|
    $vs = [$this->prefix, q($options["prefix"])];
 | 
						|
    if ($label) $vs[] = v::start("label", q($label));
 | 
						|
 | 
						|
    $attrs = self::build_attrs(null, $options, [
 | 
						|
      "id", "name", "value", "accesskey", "tabindex", "style",
 | 
						|
    ], ["class"]);
 | 
						|
    if ($options["required"]) $attrs["required"] = "required";
 | 
						|
    $vs[] = v::start("select", $attrs);
 | 
						|
    foreach ($oos as $oo) {
 | 
						|
      $vs[] = q($options["option_prefix"]);
 | 
						|
      $vs[] = v::tag("option", [
 | 
						|
        "value" => $oo["value"],
 | 
						|
        "selected" => $oo["selected"]? "selected": false,
 | 
						|
        q($oo["text"]),
 | 
						|
      ]);
 | 
						|
      $vs[] = q($options["option_suffix"]);
 | 
						|
    }
 | 
						|
    $vs[] = v::end("select");
 | 
						|
 | 
						|
    if ($label) $vs[] = v::end("label");
 | 
						|
    $vs[] = q($options["suffix"]);
 | 
						|
    $vs[] = $this->suffix;
 | 
						|
    return $vs;
 | 
						|
  }
 | 
						|
 | 
						|
  const CHECKBOX_OPTIONS_SCHEMA = [
 | 
						|
    "text" => [null, null, "libellé de la case à cocher"],
 | 
						|
    "id" => [null, null, "identifiant de la case à cocher"],
 | 
						|
    "name" => [null, null, "nom du champ"],
 | 
						|
    "value" => [null, null, "valeur du champ"],
 | 
						|
    "checked" => [null, null, "la case est-elle cochée?"],
 | 
						|
    "accesskey" => [null, null, "touche d'accès rapide"],
 | 
						|
    "tabindex" => [null, null, "index de tabulation"],
 | 
						|
    "class" => [null, null, "classes CSS du champ"],
 | 
						|
    "style" => [null, null, "style CSS du champ"],
 | 
						|
    "attrs" => [null, null, "attributs HTML génériques"],
 | 
						|
    "prefix" => [null, null, "contenu à afficher avant"],
 | 
						|
    "suffix" => [null, null, "contenu à afficher après"],
 | 
						|
  ];
 | 
						|
 | 
						|
  function checkbox($text, string $name, $value, ?bool $checked=null, ?array $options=null): array {
 | 
						|
    md::ensure_schema($options, self::CHECKBOX_OPTIONS_SCHEMA);
 | 
						|
    A::set_nz($options, "text", $text);
 | 
						|
    A::set_nz($options, "name", $name);
 | 
						|
    A::set_nz($options, "value", $value);
 | 
						|
    A::set_nz($options, "checked", $checked);
 | 
						|
    A::replace_n_indirect($options, "id", "name");
 | 
						|
 | 
						|
    $text = $options["text"];
 | 
						|
    $vs = [q($options["prefix"])];
 | 
						|
    if ($text) $vs[] = v::start("label");
 | 
						|
 | 
						|
    $attrs = self::build_attrs(["type" => "checkbox"], $options, [
 | 
						|
      "id", "name", "value", "accesskey", "tabindex", "style",
 | 
						|
    ], ["class"]);
 | 
						|
    if ($options["checked"]) $attrs["checked"] = "checked";
 | 
						|
    $vs[] = v::tag1("input", $attrs);
 | 
						|
 | 
						|
    if ($text) {
 | 
						|
      $vs[] = q($text);
 | 
						|
      $vs[] = v::end("label");
 | 
						|
    }
 | 
						|
    $vs[] = q($options["suffix"]);
 | 
						|
    return $vs;
 | 
						|
  }
 | 
						|
 | 
						|
  const CHECKBOXES_OPTIONS_SCHEMA = [
 | 
						|
    "label" => [null, null, "libellé du champ"],
 | 
						|
    "id" => [null, null, "identifiant du champ"],
 | 
						|
    "name" => [null, null, "nom du champ"],
 | 
						|
    "values" => [null, null, "valeurs cochées par défaut"],
 | 
						|
    "accesskey" => [null, null, "touche d'accès rapide"],
 | 
						|
    "tabindex" => [null, null, "index de tabulation"],
 | 
						|
    "items" => [null, null, "liste d'éléments à afficher"],
 | 
						|
    "items_func" => [null, null, "fonction fournissant une liste d'éléments à afficher"],
 | 
						|
    "item_value_key" => [null, null, "clé de la valeur d'un élément"],
 | 
						|
    "item_value_func" => [null, null, "fonction fournissant la clé de la valeur d'un élément"],
 | 
						|
    "item_text_key" => [null, null, "clé du libellé d'un élément"],
 | 
						|
    "item_text_func" => [null, null, "fonction fournissant la clé du libellé d'un élément"],
 | 
						|
    "class" => [null, null, "classes CSS du champ"],
 | 
						|
    "style" => [null, null, "style CSS du champ"],
 | 
						|
    "attrs" => [null, null, "attributs HTML génériques"],
 | 
						|
    "prefix" => [null, null, "contenu à afficher avant"],
 | 
						|
    "suffix" => [null, null, "contenu à afficher après"],
 | 
						|
    "checkbox_prefix" => [null, null, "contenu à afficher avant chaque case à cocher"],
 | 
						|
    "checkbox_suffix" => [null, null, "contenu à afficher après chaque case à cocher"],
 | 
						|
  ];
 | 
						|
 | 
						|
  function checkboxes($label, string $name, $values, ?array $options=null): array {
 | 
						|
    md::ensure_schema($options, self::CHECKBOXES_OPTIONS_SCHEMA);
 | 
						|
    A::set_nz($options, "label", $label);
 | 
						|
    A::set_nz($options, "name", $name);
 | 
						|
    A::set_nz($options, "values", $values);
 | 
						|
 | 
						|
    $name = $options["name"];
 | 
						|
    $values = A::with($options["values"]);
 | 
						|
    $items = $this->vof($options, "items", "items_func");
 | 
						|
 | 
						|
    $oos = array();
 | 
						|
    foreach ($items as $key => $item) {
 | 
						|
      $item_value = $this->vof($options, null, "item_value_func", $item, "item_value_key", 0, $item, $key);
 | 
						|
      $item_text = $this->vof($options, null, "item_text_func", $item, "item_text_key", 1, $item, $key);
 | 
						|
      if (!$item_text) $item_text = $item_value;
 | 
						|
      $oos[] = array(
 | 
						|
        "name" => "${name}[$key]",
 | 
						|
        "value" => $item_value,
 | 
						|
        "text" => $item_text,
 | 
						|
        "checked" => in_array($item_value, $values),
 | 
						|
      );
 | 
						|
    }
 | 
						|
 | 
						|
    $label = $options["label"];
 | 
						|
    $vs = [$this->prefix, q($options["prefix"])];
 | 
						|
    if ($label) $vs[] = v::start("label", q($label));
 | 
						|
 | 
						|
    $attrs = self::build_attrs(null, $options, [
 | 
						|
      "id", "accesskey", "tabindex", "style",
 | 
						|
    ], ["class"]);
 | 
						|
    $first = true;
 | 
						|
    foreach ($oos as $oo) {
 | 
						|
      $vs[] = q($options["checkbox_prefix"]);
 | 
						|
      $vs[] = $this->checkbox($oo["text"],
 | 
						|
        $oo["name"], $oo["value"], $oo["checked"],
 | 
						|
        ["attrs" => $attrs]);
 | 
						|
      $vs[] = q($options["checkbox_suffix"]);
 | 
						|
      if ($first && $label) $vs[] = v::end("label");
 | 
						|
      $attrs["id"] = false; # id uniquement sur le premier
 | 
						|
      unset($attrs["accesskey"]); # accesskey uniquement sur le premier
 | 
						|
      unset($attrs["tabindex"]); # accesskey uniquement sur le premier
 | 
						|
      $first = false;
 | 
						|
    }
 | 
						|
 | 
						|
    if ($first && $label) $vs[] = v::end("label");
 | 
						|
    $vs[] = q($options["suffix"]);
 | 
						|
    $vs[] = $this->suffix;
 | 
						|
    return $vs;
 | 
						|
  }
 | 
						|
 | 
						|
  const RADIOBUTTON_OPTIONS_SCHEMA = [
 | 
						|
    "text" => [null, null, "libellé du bouton radio"],
 | 
						|
    "id" => [null, null, "identifiant du bouton radio"],
 | 
						|
    "name" => [null, null, "nom du champ"],
 | 
						|
    "value" => [null, null, "valeur du champ"],
 | 
						|
    "checked" => [null, null, "le bouton est-il sélectionné?"],
 | 
						|
    "accesskey" => [null, null, "touche d'accès rapide"],
 | 
						|
    "tabindex" => [null, null, "index de tabulation"],
 | 
						|
    "class" => [null, null, "classes CSS du champ"],
 | 
						|
    "style" => [null, null, "style CSS du champ"],
 | 
						|
    "attrs" => [null, null, "attributs HTML génériques"],
 | 
						|
    "prefix" => [null, null, "contenu à afficher avant"],
 | 
						|
    "suffix" => [null, null, "contenu à afficher après"],
 | 
						|
  ];
 | 
						|
 | 
						|
  function radiobutton($text, string $name, $value, ?bool $checked=null, ?array $options=null): array {
 | 
						|
    md::ensure_schema($options, self::RADIOBUTTON_OPTIONS_SCHEMA);
 | 
						|
    A::set_nz($options, "text", $text);
 | 
						|
    A::set_nz($options, "name", $name);
 | 
						|
    A::set_nz($options, "value", $value);
 | 
						|
    A::set_nz($options, "checked", $checked);
 | 
						|
    A::replace_n_indirect($options, "id", "name");
 | 
						|
 | 
						|
    $text = $options["text"];
 | 
						|
    $vs = [q($options["prefix"])];
 | 
						|
    if ($text) $vs[] = v::start("label");
 | 
						|
 | 
						|
    $attrs = self::build_attrs(["type" => "radio"], $options, [
 | 
						|
      "id", "name", "value", "accesskey", "tabindex", "style",
 | 
						|
    ], ["class"]);
 | 
						|
    if ($options["checked"]) $attrs["checked"] = "checked";
 | 
						|
    $vs[] = v::tag1("input", $attrs);
 | 
						|
 | 
						|
    if ($text) {
 | 
						|
      $vs[] = q($text);
 | 
						|
      $vs[] = v::end("label");
 | 
						|
    }
 | 
						|
    $vs[] = q($options["suffix"]);
 | 
						|
    return $vs;
 | 
						|
  }
 | 
						|
 | 
						|
  const RADIOBUTTONS_OPTIONS_SCHEMA = [
 | 
						|
    "label" => [null, null, "libellé du champ"],
 | 
						|
    "id" => [null, null, "identifiant du champ"],
 | 
						|
    "name" => [null, null, "nom du champ"],
 | 
						|
    "value" => [null, null, "valeur cochée par défaut"],
 | 
						|
    "accesskey" => [null, null, "touche d'accès rapide"],
 | 
						|
    "tabindex" => [null, null, "index de tabulation"],
 | 
						|
    "items" => [null, null, "liste d'éléments à afficher"],
 | 
						|
    "items_func" => [null, null, "fonction fournissant une liste d'éléments à afficher"],
 | 
						|
    "item_value_key" => [null, null, "clé de la valeur d'un élément"],
 | 
						|
    "item_value_func" => [null, null, "fonction fournissant la clé de la valeur d'un élément"],
 | 
						|
    "item_text_key" => [null, null, "clé du libellé d'un élément"],
 | 
						|
    "item_text_func" => [null, null, "fonction fournissant la clé du libellé d'un élément"],
 | 
						|
    "class" => [null, null, "classes CSS du champ"],
 | 
						|
    "style" => [null, null, "style CSS du champ"],
 | 
						|
    "attrs" => [null, null, "attributs HTML génériques"],
 | 
						|
    "prefix" => [null, null, "contenu à afficher avant"],
 | 
						|
    "suffix" => [null, null, "contenu à afficher après"],
 | 
						|
    "radiobutton_prefix" => [null, null, "contenu à afficher avant chaque bouton radio"],
 | 
						|
    "radiobutton_suffix" => [null, null, "contenu à afficher après chaque bouton radio"],
 | 
						|
  ];
 | 
						|
 | 
						|
  function radiobuttons($label, string $name, $value, ?array $options): array {
 | 
						|
    md::ensure_schema($options, self::RADIOBUTTONS_OPTIONS_SCHEMA);
 | 
						|
    A::set_nz($options, "label", $label);
 | 
						|
    A::set_nz($options, "name", $name);
 | 
						|
    A::set_nz($options, "value", $value);
 | 
						|
    A::replace_n_indirect($options, "id", "name");
 | 
						|
 | 
						|
    $id = $options["id"];
 | 
						|
    $name = $options["name"];
 | 
						|
    $value = $options["value"];
 | 
						|
    $items = $this->vof($options, "items", "items_func");
 | 
						|
 | 
						|
    $oos = array();
 | 
						|
    $i = 0;
 | 
						|
    foreach ($items as $key => $item) {
 | 
						|
      $item_value = $this->vof($options, null, "item_value_func", $item, "item_value_key", 0, $item, $key);
 | 
						|
      $item_text = $this->vof($options, null, "item_text_func", $item, "item_text_key", 1, $item, $key);
 | 
						|
      if (!$item_text) $item_text = $item_value;
 | 
						|
      $oos[] = array(
 | 
						|
        "id" => "$id$i",
 | 
						|
        "name" => "$name",
 | 
						|
        "value" => $item_value,
 | 
						|
        "text" => $item_text,
 | 
						|
        "checked" => $value === $item_value,
 | 
						|
      );
 | 
						|
      $i++;
 | 
						|
    }
 | 
						|
 | 
						|
    $label = $options["label"];
 | 
						|
    $vs = [$this->prefix, q($options["prefix"])];
 | 
						|
    if ($label) $vs[] = v::start("label", q($label));
 | 
						|
 | 
						|
    $attrs = self::build_attrs(null, $options, [
 | 
						|
      "accesskey", "tabindex", "style",
 | 
						|
    ], ["class"]);
 | 
						|
    $first = true;
 | 
						|
    foreach ($oos as $oo) {
 | 
						|
      $vs[] = q($options["radiobutton_prefix"]);
 | 
						|
      $vs[] = $this->radiobutton($oo["text"],
 | 
						|
        $oo["name"], $oo["value"], $oo["checked"],
 | 
						|
        ["id" => $oo["id"], "attrs" => $attrs]);
 | 
						|
      $vs[] = q($options["radiobutton_suffix"]);
 | 
						|
      if ($first && $label) $vs[] = v::end("label");
 | 
						|
      unset($attrs["accesskey"]); # accesskey uniquement sur le premier
 | 
						|
      unset($attrs["tabindex"]); # accesskey uniquement sur le premier
 | 
						|
      $first = false;
 | 
						|
    }
 | 
						|
 | 
						|
    if ($first && $label) $vs[] = v::end("label");
 | 
						|
    $vs[] = q($options["suffix"]);
 | 
						|
    $vs[] = $this->suffix;
 | 
						|
    return $vs;
 | 
						|
  }
 | 
						|
 | 
						|
  const TEXTAREA_OPTIONS_SCHEMA = [
 | 
						|
    "label" => [null, null, "libellé du champ"],
 | 
						|
    "id" => [null, null, "identifiant du champ"],
 | 
						|
    "name" => [null, null, "nom du champ"],
 | 
						|
    "value" => [null, null, "valeur par défaut du champ"],
 | 
						|
    "placeholder" => [null, null, "valeur suggérée"],
 | 
						|
    "required" => [null, null, "ce champ est-il requis?"],
 | 
						|
    "accesskey" => [null, null, "touche d'accès rapide"],
 | 
						|
    "tabindex" => [null, null, "index de tabulation"],
 | 
						|
    "class" => [null, null, "classes CSS du champ"],
 | 
						|
    "style" => [null, null, "style CSS du champ"],
 | 
						|
    "attrs" => [null, null, "attributs HTML génériques"],
 | 
						|
    "prefix" => [null, null, "contenu à afficher avant"],
 | 
						|
    "suffix" => [null, null, "contenu à afficher après"],
 | 
						|
  ];
 | 
						|
 | 
						|
  function textarea($label, string $name, $value, ?array $options=null): array {
 | 
						|
    md::ensure_schema($options, self::TEXTAREA_OPTIONS_SCHEMA);
 | 
						|
    A::set_nz($options, "label", $label);
 | 
						|
    A::set_nz($options, "name", $name);
 | 
						|
    A::set_nz($options, "value", $value);
 | 
						|
    A::replace_n_indirect($options, "id", "name");
 | 
						|
 | 
						|
    $label = $options["label"];
 | 
						|
    $vs = [$this->prefix, q($options["prefix"])];
 | 
						|
    if ($label) $vs[] = v::start("label", q($label));
 | 
						|
 | 
						|
    $attrs = self::build_attrs(null, $options, [
 | 
						|
      "id", "name", "placeholder", "accesskey", "tabindex", "style",
 | 
						|
    ], ["class"]);
 | 
						|
    if ($options["required"]) $attrs["required"] = "required";
 | 
						|
    $vs[] = v::tag("textarea", [$attrs, q($options["value"])]);
 | 
						|
 | 
						|
    if ($label) $vs[] = v::end("label");
 | 
						|
    $vs[] = q($options["suffix"]);
 | 
						|
    $vs[] = $this->suffix;
 | 
						|
    return $vs;
 | 
						|
  }
 | 
						|
 | 
						|
  const FILE_OPTIONS_SCHEMA = [
 | 
						|
    "label" => [null, null, "libellé du champ"],
 | 
						|
    "id" => [null, null, "identifiant du champ"],
 | 
						|
    "name" => [null, null, "nom du champ"],
 | 
						|
    "accept" => [".pdf,image/*", "types MIME acceptés"],
 | 
						|
    "required" => [null, null, "ce champ est-il requis?"],
 | 
						|
    "accesskey" => [null, null, "touche d'accès rapide"],
 | 
						|
    "tabindex" => [null, null, "index de tabulation"],
 | 
						|
    "class" => [null, null, "classes CSS du champ"],
 | 
						|
    "style" => [null, null, "style CSS du champ"],
 | 
						|
    "attrs" => [null, null, "attributs HTML génériques"],
 | 
						|
    "prefix" => [null, null, "contenu à afficher avant"],
 | 
						|
    "suffix" => [null, null, "contenu à afficher après"],
 | 
						|
  ];
 | 
						|
 | 
						|
  function file($label, string $name, ?array $options=null): array {
 | 
						|
    md::ensure_schema($options, self::FILE_OPTIONS_SCHEMA);
 | 
						|
    A::set_nz($options, "label", $label);
 | 
						|
    A::set_nz($options, "name", $name);
 | 
						|
    A::replace_n_indirect($options, "id", "name");
 | 
						|
 | 
						|
    $label = $options["label"];
 | 
						|
    $vs = [$this->prefix, q($options["prefix"])];
 | 
						|
    if ($label) $vs[] = v::start("label", q($label));
 | 
						|
 | 
						|
    $attrs = self::build_attrs(["type" => "file"], $options, [
 | 
						|
      "id", "name", "accept", "accesskey", "tabindex", "style",
 | 
						|
    ], ["class"]);
 | 
						|
    if ($options["required"]) $attrs["required"] = "required";
 | 
						|
    $vs[] = v::tag1("input", $attrs);
 | 
						|
 | 
						|
    if ($label) $vs[] = v::end("label");
 | 
						|
    $vs[] = q($options["suffix"]);
 | 
						|
    $vs[] = $this->suffix;
 | 
						|
    return $vs;
 | 
						|
  }
 | 
						|
 | 
						|
  const SUBMIT_OPTIONS_SCHEMA = [
 | 
						|
    "submit" => [null, null, "libellé du bouton de soumission"],
 | 
						|
    "id" => [null, null, "identifiant du bouton"],
 | 
						|
    "name" => [null, null, "nom du champ"],
 | 
						|
    "value" => [null, null, "valeur du champ"],
 | 
						|
    "formmethod" => [null, null, "méthode de soumission (post, get)"],
 | 
						|
    "formaction" => [null, null, "action pour la soumission"],
 | 
						|
    "formenctype" => [null, null, "type d'encodage pour la soumission"],
 | 
						|
    "accesskey" => [null, null, "touche d'accès rapide"],
 | 
						|
    "tabindex" => [null, null, "index de tabulation"],
 | 
						|
    "type" => [null, null, "type de bouton (submit, reset)"],
 | 
						|
    "class" => [null, null, "classes CSS du bouton"],
 | 
						|
    "style" => [null, null, "style CSS du bouton"],
 | 
						|
    "attrs" => [null, null, "attributs HTML génériques"],
 | 
						|
    "prefix" => [null, null, "contenu à afficher avant"],
 | 
						|
    "suffix" => [null, null, "contenu à afficher après"],
 | 
						|
  ];
 | 
						|
 | 
						|
  function submit($options=null): array {
 | 
						|
    md::ensure_schema($options, self::SUBMIT_OPTIONS_SCHEMA);
 | 
						|
    A::replace_z($options, "type", "submit");
 | 
						|
 | 
						|
    $vs = [$this->prefix, q($options["prefix"])];
 | 
						|
 | 
						|
    $attrs = self::build_attrs(null, $options, [
 | 
						|
      "type", "id", "name", "value", "formaction", "formmethod", "formenctype",
 | 
						|
      "accesskey", "tabindex", "style",
 | 
						|
    ], ["class"]);
 | 
						|
    $vs[] = v::tag("button", [$attrs, q($options["submit"])]);
 | 
						|
 | 
						|
    $vs[] = q($options["suffix"]);
 | 
						|
    $vs[] = $this->suffix;
 | 
						|
    return $vs;
 | 
						|
  }
 | 
						|
 | 
						|
  const RESET_OPTIONS_SCHEMA = [
 | 
						|
    "reset" => [null, null, "libellé du bouton de soumission"],
 | 
						|
    "id" => [null, null, "identifiant du bouton"],
 | 
						|
    "name" => [null, null, "nom du champ"],
 | 
						|
    "value" => [null, null, "valeur du champ"],
 | 
						|
    "formmethod" => [null, null, "méthode de soumission (post, get)"],
 | 
						|
    "formaction" => [null, null, "action pour la soumission"],
 | 
						|
    "formenctype" => [null, null, "type d'encodage pour la soumission"],
 | 
						|
    "accesskey" => [null, null, "touche d'accès rapide"],
 | 
						|
    "tabindex" => [null, null, "index de tabulation"],
 | 
						|
    "type" => [null, null, "type de bouton (submit, reset)"],
 | 
						|
    "class" => [null, null, "classes CSS du bouton"],
 | 
						|
    "style" => [null, null, "style CSS du bouton"],
 | 
						|
    "attrs" => [null, null, "attributs HTML génériques"],
 | 
						|
    "prefix" => [null, null, "contenu à afficher avant"],
 | 
						|
    "suffix" => [null, null, "contenu à afficher après"],
 | 
						|
  ];
 | 
						|
 | 
						|
  function reset($options=null): array {
 | 
						|
    md::ensure_schema($options, self::RESET_OPTIONS_SCHEMA);
 | 
						|
    A::replace_z($options, "type", "reset");
 | 
						|
 | 
						|
    $vs = [$this->prefix, q($options["prefix"])];
 | 
						|
 | 
						|
    $attrs = self::build_attrs(null, $options, [
 | 
						|
      "type", "id", "name", "value", "formaction", "formmethod", "formenctype",
 | 
						|
      "accesskey", "tabindex", "style",
 | 
						|
    ], ["class"]);
 | 
						|
    $vs[] = v::tag("button", [$attrs, q($options["reset"])]);
 | 
						|
 | 
						|
    $vs[] = q($options["suffix"]);
 | 
						|
    $vs[] = $this->suffix;
 | 
						|
    return $vs;
 | 
						|
  }
 | 
						|
}
 |