Updated to Drupal 8.5. Core Media not yet in use.
[yaffs-website] / web / themes / contrib / bootstrap / src / Utility / Element.php
index 84df3f77351de0d6bef30d7063bbef089e0d9408..f0422265b661885816adb81a45bd28dba554696d 100644 (file)
@@ -1,8 +1,4 @@
 <?php
-/**
- * @file
- * Contains \Drupal\bootstrap\Utility\Element.
- */
 
 namespace Drupal\bootstrap\Utility;
 
@@ -11,6 +7,7 @@ use Drupal\Component\Render\FormattableMarkup;
 use Drupal\Component\Render\MarkupInterface;
 use Drupal\Component\Utility\Xss;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\Element as CoreElement;
 
 /**
  * Provides helper methods for Drupal render elements.
@@ -71,7 +68,7 @@ class Element extends DrupalAttributes {
    *   Throws this error when the name is a property (key starting with #).
    */
   public function &__get($key) {
-    if (\Drupal\Core\Render\Element::property($key)) {
+    if (CoreElement::property($key)) {
       throw new \InvalidArgumentException('Cannot dynamically retrieve element property. Please use \Drupal\bootstrap\Utility\Element::getProperty instead.');
     }
     $instance = new self($this->offsetGet($key, []));
@@ -92,7 +89,7 @@ class Element extends DrupalAttributes {
    *   Throws this error when the name is a property (key starting with #).
    */
   public function __set($key, $value) {
-    if (\Drupal\Core\Render\Element::property($key)) {
+    if (CoreElement::property($key)) {
       throw new \InvalidArgumentException('Cannot dynamically retrieve element property. Use \Drupal\bootstrap\Utility\Element::setProperty instead.');
     }
     $this->offsetSet($key, ($value instanceof Element ? $value->getArray() : $value));
@@ -113,7 +110,7 @@ class Element extends DrupalAttributes {
    *   Throws this error when the name is a property (key starting with #).
    */
   public function __isset($name) {
-    if (\Drupal\Core\Render\Element::property($name)) {
+    if (CoreElement::property($name)) {
       throw new \InvalidArgumentException('Cannot dynamically check if an element has a property. Use \Drupal\bootstrap\Utility\Element::unsetProperty instead.');
     }
     return parent::__isset($name);
@@ -131,7 +128,7 @@ class Element extends DrupalAttributes {
    *   Throws this error when the name is a property (key starting with #).
    */
   public function __unset($name) {
-    if (\Drupal\Core\Render\Element::property($name)) {
+    if (CoreElement::property($name)) {
       throw new \InvalidArgumentException('Cannot dynamically unset an element property. Use \Drupal\bootstrap\Utility\Element::hasProperty instead.');
     }
     parent::__unset($name);
@@ -180,7 +177,7 @@ class Element extends DrupalAttributes {
    *   The array keys of the element's children.
    */
   public function childKeys($sort = FALSE) {
-    return \Drupal\Core\Render\Element::children($this->array, $sort);
+    return CoreElement::children($this->array, $sort);
   }
 
   /**
@@ -222,7 +219,7 @@ class Element extends DrupalAttributes {
       "$prefix-primary", "$prefix-success", "$prefix-info",
       "$prefix-warning", "$prefix-danger", "$prefix-link",
       // Default should be last.
-      "$prefix-default"
+      "$prefix-default",
     ];
 
     // Set the class to "btn-default" if it shouldn't be colorized.
@@ -301,6 +298,27 @@ class Element extends DrupalAttributes {
     return $old;
   }
 
+  /**
+   * Traverses the element to find the closest button.
+   *
+   * @return \Drupal\bootstrap\Utility\Element|false
+   *   The first button element or FALSE if no button could be found.
+   */
+  public function &findButton() {
+    $button = FALSE;
+    foreach ($this->children() as $child) {
+      if ($child->isButton()) {
+        $button = $child;
+        break;
+      }
+      if ($result = &$child->findButton()) {
+        $button = $result;
+        break;
+      }
+    }
+    return $button;
+  }
+
   /**
    * Retrieves the render array for the element.
    *
@@ -319,7 +337,7 @@ class Element extends DrupalAttributes {
    * @param mixed $default
    *   Optional. The default value to use if the context $name isn't set.
    *
-   * @return mixed|NULL
+   * @return mixed|null
    *   The context value or the $default value if not set.
    */
   public function &getContext($name, $default = NULL) {
@@ -372,7 +390,7 @@ class Element extends DrupalAttributes {
    *   The array keys of the element's visible children.
    */
   public function getVisibleChildren() {
-    return \Drupal\Core\Render\Element::getVisibleChildren($this->array);
+    return CoreElement::getVisibleChildren($this->array);
   }
 
   /**
@@ -404,7 +422,8 @@ class Element extends DrupalAttributes {
    *   TRUE or FALSE.
    */
   public function isButton() {
-    return !empty($this->array['#is_button']) || $this->isType(['button', 'submit', 'reset', 'image_button']) || $this->hasClass('btn');
+    $button_types = ['button', 'submit', 'reset', 'image_button'];
+    return !empty($this->array['#is_button']) || $this->isType($button_types) || $this->hasClass('btn');
   }
 
   /**
@@ -417,7 +436,7 @@ class Element extends DrupalAttributes {
    *   Whether the given element is empty.
    */
   public function isEmpty() {
-    return \Drupal\Core\Render\Element::isEmpty($this->array);
+    return CoreElement::isEmpty($this->array);
   }
 
   /**
@@ -472,7 +491,7 @@ class Element extends DrupalAttributes {
    *   TRUE if the element is visible, otherwise FALSE.
    */
   public function isVisible() {
-    return \Drupal\Core\Render\Element::isVisibleElement($this->array);
+    return CoreElement::isVisibleElement($this->array);
   }
 
   /**
@@ -488,7 +507,7 @@ class Element extends DrupalAttributes {
    * @return $this
    */
   public function map(array $map) {
-    \Drupal\Core\Render\Element::setAttributes($this->array, $map);
+    CoreElement::setAttributes($this->array, $map);
     return $this;
   }
 
@@ -529,7 +548,7 @@ class Element extends DrupalAttributes {
    *   An array of property keys for the element.
    */
   public function properties() {
-    return \Drupal\Core\Render\Element::properties($this->array);
+    return CoreElement::properties($this->array);
   }
 
   /**
@@ -671,18 +690,25 @@ class Element extends DrupalAttributes {
    *   The name of the property to set.
    * @param mixed $value
    *   The value of the property to set.
+   * @param bool $recurse
+   *   Flag indicating wither to set the same property on child elements.
    *
    * @return $this
    */
-  public function setProperty($name, $value) {
+  public function setProperty($name, $value, $recurse = FALSE) {
     $this->array["#$name"] = $value instanceof Element ? $value->getArray() : $value;
+    if ($recurse) {
+      foreach ($this->children() as $child) {
+        $child->setProperty($name, $value, $recurse);
+      }
+    }
     return $this;
   }
 
   /**
    * Converts an element description into a tooltip based on certain criteria.
    *
-   * @param array|\Drupal\bootstrap\Utility\Element|NULL $target_element
+   * @param array|\Drupal\bootstrap\Utility\Element|null $target_element
    *   The target element render array the tooltip is to be attached to, passed
    *   by reference or an existing Element object. If not set, it will default
    *   this Element instance.
@@ -711,7 +737,7 @@ class Element extends DrupalAttributes {
     }
 
     // Allow a different element to attach the tooltip.
-    /** @var Element $target */
+    /** @var \Drupal\bootstrap\Utility\Element $target */
     if (is_object($target_element) && $target_element instanceof self) {
       $target = $target_element;
     }
@@ -745,9 +771,23 @@ class Element extends DrupalAttributes {
 
     // Return if element or target shouldn't have "simple" tooltip descriptions.
     $html = FALSE;
-    if (($input_only && !$target->hasProperty('input'))
-      // Ignore if the actual element has no #description set.
-      || !$this->hasProperty('description')
+
+    // If the description is a render array, it must first be pre-rendered so
+    // it can be later passed to Unicode::isSimple() if needed.
+    $description = $this->hasProperty('description') ? $this->getProperty('description') : FALSE;
+    if (static::isRenderArray($description)) {
+      $description = static::createStandalone($description)->renderPlain();
+    }
+
+    if (
+      // Ignore if element has no #description.
+      !$description
+
+      // Ignore if description is not a simple string or MarkupInterface.
+      || (!is_string($description) && !($description instanceof MarkupInterface))
+
+      // Ignore if element is not an input.
+      || ($input_only && !$target->hasProperty('input'))
 
       // Ignore if the target element already has a "data-toggle" attribute set.
       || $target->hasAttribute('data-toggle')
@@ -761,7 +801,7 @@ class Element extends DrupalAttributes {
       || !$target->getProperty('smart_description', TRUE)
 
       // Ignore if the description is not "simple".
-      || !Unicode::isSimple($this->getProperty('description'), $length, $allowed_tags, $html)
+      || !Unicode::isSimple($description, $length, $allowed_tags, $html)
     ) {
       // Set the both the actual element and the target element
       // #smart_description property to FALSE.
@@ -786,7 +826,7 @@ class Element extends DrupalAttributes {
     $attributes = $target->getAttributes($type);
 
     // Set the tooltip attributes.
-    $attributes['title'] = $allowed_tags !== FALSE ? Xss::filter((string) $this->getProperty('description'), $allowed_tags) : $this->getProperty('description');
+    $attributes['title'] = $allowed_tags !== FALSE ? Xss::filter((string) $description, $allowed_tags) : $description;
     $attributes['data-toggle'] = 'tooltip';
     if ($html || $allowed_tags === FALSE) {
       $attributes['data-html'] = 'true';