You are here

headinganchors.module in Table of Contents 5

Same filename and directory in other branches
  1. 5.2 headinganchors.module
  2. 6.2 headinganchors.module

This is a module which takes <h2> and <h3> headings and adds id attributes to them to allow them to be linked to in a URL. The following rules are used to create an attribute:

  • All non alphanumerics are removed
  • Case is preserved to allow for some measure of readability
  • HTML tags are stripped from in the middle of heading tags for the anchors

Example: <h2>I Link To <a href="http://www.csaonline.ca/">csaonline.ca</a></h2> becomes: <h2 id="ILinkTocsaonline.ca">I Link To <a href="http://www.csaonline.ca/">csaonline.ca</a></h2> and can be referenced with /url#ILinkTocsaonline.ca

Multiple headings with the same text will have the same ID. This should be changed to allow multiple subheadings.

This file was built from the filter example on the Drupal website.

File

headinganchors.module
View source
<?php

/**
 * @file
 * This is a module which takes <h2> and <h3> headings and adds id attributes to them
 * to allow them to be linked to in a URL. The following rules are used to create an
 * attribute:
 * - All non alphanumerics are removed
 * - Case is preserved to allow for some measure of readability
 * - HTML tags are stripped from in the middle of heading tags for the anchors
 *
 * Example: <h2>I Link To <a href="http://www.csaonline.ca/">csaonline.ca</a></h2>
 * becomes:
 * <h2 id="ILinkTocsaonline.ca">I Link To <a href="http://www.csaonline.ca/">csaonline.ca</a></h2>
 * and can be referenced with /url#ILinkTocsaonline.ca
 *
 * Multiple headings with the same text will have the same ID. This should be changed to allow
 * multiple subheadings.
 *
 * This file was built from the filter example on the Drupal website.
 */

/**
 * Implementation of hook_help().
 *
 */
function headinganchors_help($section) {
  switch ($section) {
    case 'admin/modules#description':

      // This description is shown in the listing at admin/modules.
      return t('A module to create named anchors based on HTML headings.');
  }
}

/**
 * Implementation of hook_filter_tips().
 */
function headinganchors_filter_tips($format, $long = FALSE) {
  if ($long) {
    return t('Every instance of second and third level headings (&lt;h2&gt; and &lt;h3&gt;) will be modified to include an id attribute for anchor linking.');
  }
  else {
    return t('Insert heading anchors automatically.');
  }
}

/**
 * Implementation of hook_filter().
 *
 * The bulk of filtering work is done here.
 */
function headinganchors_filter($op, $delta = 0, $format = -1, $text = '') {
  if ($op == 'list') {
    return array(
      0 => t('Headings to Anchors'),
    );
  }

  // We currently only define one filter, so we don't need to switch on $delta
  switch ($op) {

    // Admin interface description
    case 'description':
      return t('Inserts named anchors for heading tags automatically.');

    // Ensure that the content is regenerated on every preview
    case 'no cache':
      return TRUE;

    // Need to find out if this is required
    case 'prepare':
      return $text;

    // Here, we pull out the required information and add the anchor
    case 'process':

      // matches[1] is the level of the heading
      // matches[2] is the text of the heading
      // matches[][$count] is the current heading being modified
      $matches = array();

      // In the future, there should probably be some way to define which headings are modified
      // We assume that the HTML is valid and that the tags match each other.
      preg_match_all("/<h([2-3])>(.*)<\\/h[2-3]>/i", $text, $matches, PREG_PATTERN_ORDER);
      $count = 0;
      $anchors = array();
      foreach ($matches[0] as $match) {

        // Pull out the level and the heading
        $level = $matches[1][$count];
        $heading = $matches[2][$count];

        // We do strip_tags to get rid of any extra HTML as we don't want that in the id.
        // Strip out amp; from entity as it's ugly
        $anchor = preg_replace("/&amp;/", "", strip_tags($matches[2][$count]));

        // Then, we punt that result and strip out anything which isn't alphanumeric
        $anchor = preg_replace("/[^A-Za-z0-9]/", "", $anchor);

        // Remove leading digits
        $anchor = preg_replace("/^[0-9]+/", "", $anchor);

        // Look for duplicate ID's, and add -number to make them unique
        if (array_key_exists($anchor, $anchors)) {
          $anchors[$anchor]++;
        }
        else {
          $anchors[$anchor] = 0;
        }
        if ($anchors[$anchor] > 0) {
          $anchor .= "-" . $anchors[$anchor];
        }
        $pattern = "/<h" . $level . ">" . preg_quote($heading, '/') . "<\\/h" . $level . ">/";

        // Now, append everything together to create the new heading
        $replacement = "<h" . $level . " id=\"" . $anchor . "\">" . $heading . "</h" . $level . ">";

        // Now, find the heading we were editing and replace it in the text.
        // Limit of one so that identical headers are only replaced one at a time
        $text = preg_replace($pattern, $replacement, $text, 1);
        $count++;
      }
      return $text;
  }
}

Functions

Namesort descending Description
headinganchors_filter Implementation of hook_filter().
headinganchors_filter_tips Implementation of hook_filter_tips().
headinganchors_help Implementation of hook_help().