You are here

protected function Toc::initialize in TOC API 8

Initializes the table of content index and ensure unique header ids.

1 call to Toc::initialize()
Toc::__construct in src/Toc.php
Constructs a new TOC object.

File

src/Toc.php, line 194

Class

Toc
Defines A class that parses the header tags from an HTML document.

Namespace

Drupal\toc_api

Code

protected function initialize() {
  $this->index = [];

  // Setup an empty array of keys to track the index's keys.
  $default_keys = [];
  foreach (array_keys($this->options['headers']) as $tag) {
    $default_keys[$tag] = 0;
  }
  $index_keys = $default_keys;
  $dom = Html::load($this->source);

  // Loop through all the tags to ensure headers are found in the correct
  // order.
  $dom_nodes = $dom
    ->getElementsByTagName('*');

  /** @var \DOMElement $dom_node */
  foreach ($dom_nodes as $dom_node) {
    if (empty($this->options['headers'][$dom_node->tagName])) {
      continue;
    }

    // Set header tag and options.
    $header_tag = $dom_node->tagName;
    $header_options = $this->options['headers'][$header_tag];

    // Set header html and title.
    $header_html = '';
    foreach ($dom_node->childNodes as $child_node) {
      $header_html .= $dom_node->ownerDocument
        ->saveHTML($child_node);
    }
    $header_title = strip_tags($header_html);

    // Set header key, number, and parent.
    $header_number = NULL;
    $header_key = NULL;
    $header_path = NULL;
    $parent_key = NULL;
    $header_keys = $default_keys;
    $header_level = (int) $dom_node->tagName[1];
    for ($level = $this->options['header_min']; $level <= $this->options['header_max']; $level++) {
      $tag = "h{$level}";
      if ($level == $header_level) {

        // When header level is matched, increment the index key and set the
        // header number.
        $header_number = ++$index_keys[$tag];
      }
      elseif ($level > $header_level) {

        // Reset index keys once a header level is met.
        $index_keys[$tag] = 0;
      }
      $header_keys[$tag] = $index_keys[$tag];

      // Now set the parent key for every header level to ensure this header
      // has a parent.
      if ($level < $header_level) {
        $parent_key = implode('.', $header_keys);
        if (!isset($this->index[$parent_key])) {
          $parent_key = NULL;
        }
      }
    }
    $header_key = implode('.', $header_keys);

    // Set header parts and path from converted keys.
    $header_path = implode($this->options['number_path_separator'], $this
      ->formatter()
      ->convertHeaderKeysToValues($header_keys, $this->options));

    // Append to this header to it's parent.
    if ($parent_key) {
      $this->index[$parent_key]['children'][$header_key] = $header_key;
    }

    // Set header value based on (list) type.
    $header_value = $this
      ->formatter()
      ->convertNumberToListTypeValue($header_number, $header_options['number_type']);

    // Get and reset (unique) header id attribute.
    if ($dom_node
      ->getAttribute('id')) {
      $header_id = $dom_node
        ->getAttribute('id');
    }
    else {
      $id_type = $this->options['header_id'];
      $id_prefix = $this->options['header_id_prefix'] ?: 'section';
      switch ($id_type) {
        case 'title':
          $header_id = $this
            ->formatter()
            ->convertStringToId($header_title);
          break;
        case 'number_path':
          $header_id = $id_prefix . '-' . $header_path;
          break;
        case 'key':
        default:
          $header_id = $id_prefix . '-' . $header_key;
          break;
      }
    }
    $header_id = $this
      ->uniqueId($header_id);
    $dom_node
      ->setAttribute('id', $header_id);

    // Track the header's id and map it to the header's key.
    // This is used to lookup the parent and children relationships.
    $this->ids[$header_id] = $header_key;

    // Set header in index.
    $this->index[$header_key] = [
      'type' => $header_options['number_type'],
      'tag' => $header_tag,
      'level' => $header_level,
      'key' => $header_key,
      'keys' => $header_keys,
      'indent' => $header_level - $this->options['header_min'],
      'path' => $header_path,
      'number' => $header_number,
      'value' => $header_value,
      'parent' => $parent_key,
      'children' => [],
      'id' => $header_id,
      'title' => $header_title,
      'html' => [
        '#markup' => $header_html,
        '#allowed_tags' => $this
          ->getAllowedTags(),
      ],
      'url' => Url::fromRoute('<none>', NULL, [
        'fragment' => $header_id,
      ]),
    ];
  }
  $this->content = Html::serialize($dom);
}