You are here

collapse_text.module in Collapse Text 6

collapse_text is an input filter that allows text to be collapsible

File

collapse_text.module
View source
<?php

/**
 * @file
 * collapse_text is an input filter that allows text to be collapsible
 */

/**
 * Implementation of hook_filter_tips().
 */
function collapse_text_filter_tips($delta, $format, $long = false) {
  if ($long) {
    return t('Enclose sections of text in [collapse] and [/collapse] to ' + 'turn them into collapsible sections.  If you use [collapse collapsed] ' + 'and [/collapse], the section will start out collapsed. You may ' + 'specify a title with [collapse title=some title] ' + '(or [collapse collapsed title=some title]). If no title is specified, ' + 'the title will be taken from the first header ' + '(&lt;h1&gt;, &lt;h2&gt;, &lt;h3&gt;, ...) found. In the absence of a ' + 'header, a default title is used.');
  }
  else {
    return t('Make collapsible text blocks using [collapse] and [/collapse].');
  }
}

/**
 * Implementation of hook_filter().
 */
function collapse_text_filter($op, $delta = 0, $format = -1, $text = '') {
  switch ($op) {
    case 'list':
      return array(
        0 => t('Collapse text'),
      );
      break;
    case 'description':
      return t('Make collapsing text sections');
      break;
    case 'settings':

      // This filter has no user settings.
      break;
    case 'no cache':
      return FALSE;
      break;
    case 'prepare':
      return $text;
      break;
    case 'process':
      return collapse_text_process($text);
      break;
    default:
      return $text;
      break;
  }
}
function collapse_text_process($text) {
  $text = collapse_text_process_recurs($text);

  // remove non-escaped collapse open tags
  $text = preg_replace('/(?<!\\\\)
                 \\[
                    collapse
                    (\\ collapsed)?
                    (?:\\ style=([^\\] ]*))?
                    (?:\\ title=([^\\]]*))?
                 \\]/smx', "", $text);

  // remove non-escaped collapse close tags
  $text = preg_replace('/(?<!\\\\)\\[\\/collapse\\]/smx', "", $text);

  // remove backslash on escaped open tags
  $text = preg_replace('/\\\\
                 (\\[
                    collapse
                    (?:\\ collapsed)?
                    (?:\\ style=([^\\] ]*))?
                    (?:\\ title=[^\\]]*)?
                 \\])/smx', '$1', $text);

  // remove backslash on escaped close tags
  $text = preg_replace('/\\\\(\\[\\/collapse\\])/smx', "\$1", $text);
  return $text;
}

/**
 * Provides a layer of encapsulation for the regex call.
 */
function collapse_text_process_recurs($text) {

  // Per #259535 and #233877, add ability to specify title
  // in collapse text. Thanks rivena, Justyn
  // Per #233877, add ability to have nested sections.
  $text = preg_replace_callback('/
               (?:<p(?:\\s[^>]*)?>)?       # (remove paragraph if right before)
               (?<!\\\\)                  # make sure the tag is not escaped with a backslash
               \\[                         # look for an opening bracket
                  collapse                # followed by the word `collapse`
                  (\\ collapsed)?          # followed by (optionally) a space and the word `collapsed` (captured)
                  (?:\\ style=([^\\] ]*))?  # followed by (optionally) a space and a style, consisting of any
                                          #   characters except a close bracket (captured)
                  (?:\\ title=([^\\]]*))?   # followed by (optionally) a space and a title, consisting of any
               \\]                         # followed by a closing bracket
               (?:<\\/p\\s*>)?              # (remove paragraph if right after)
               ( (?: [^[]                 # followed by either a non open bracket,
                     | \\\\\\[             #   or an escaped open bracket
                     | \\[(?!\\/?collapse)  #   or a non collapse tag
                     | (?R) )+ )          #   or the expression recursively run.
               (?:<p(?:\\s[^>]*)?>)?       # (remove paragraph if right before)
               (?<!\\\\)                  # make sure the tag is not escaped with a backslash
               \\[\\/collapse\\]             # a closing "tag", which is a slash followed by `collapse` in brackets
               (?:<\\/p\\s*>)?              # (remove paragraph if right after)
              /smx', "_collapse_text_replace_callback", $text);
  return $text;
}
function _collapse_text_replace_callback($matches) {
  global $base_url;

  // 2008-12-15 REMorse (no issue number) added space to make
  // $collapsed work
  $collapsed = $matches[1] == ' collapsed';
  $style = trim($matches[2]);
  $title = trim($matches[3]);
  $interior = $matches[4];
  if (empty($title)) {

    // If a title is not supplied, look for a header (<h1>, <h2> ...)
    // and use it as the title.
    $h_matches = array();
    preg_match('/(<h\\d[^>]*>(.+?)<\\/h\\d>)/smi', $interior, $h_matches);
    $title = strip_tags($h_matches[2]);

    // If we get the title from the first header tag,
    // then we should remove the header tag so it's not repeated
    if (!empty($title)) {
      $replacement = "";
      $occ = 1;
      $interior = str_replace($h_matches[0], $replacement, $interior, $occ);
    }
  }
  if (empty($title)) {

    // If there is still no title, provide some default text.
    // Added call to t() per #256176 yngens
    $title = t('Use the arrow to expand or collapse this section');
  }
  $form = array(
    '#prefix' => '<form action="' . $base_url . '/">',
    '#suffix' => '</form>',
    '#theme' => 'collapse_text_fieldset',
  );
  $form['fieldset'] = array(
    '#type' => 'fieldset',
    '#title' => $title,
    '#collapsible' => true,
    '#collapsed' => $collapsed,
  );
  if (!empty($style)) {
    $form['fieldset']['#attributes'] = array(
      'class' => collapse_text_id_safe($style),
    );
  }
  $form['fieldset']['text_contents'] = array(
    '#type' => 'markup',
    '#prefix' => '<div class="collapse-text">',
    '#value' => collapse_text_process_recurs($interior),
    '#suffix' => '</div>',
  );
  return drupal_render($form);
}
function collapse_text_preprocess_page(&$variables) {
  global $theme;

  // Add collapse.js if a collapsible fieldset is found in a region or the main content.
  if (strpos($variables['scripts'], 'misc/collapse.js') === FALSE) {
    $regions = array_keys(system_region_list($theme));
    $regions[] = 'content';
    foreach ($regions as $region) {

      // Using stripos() is much faster then executing preg_match() on every page.
      if (stripos($variables[$region], '<fieldset') !== FALSE && stripos($variables[$region], 'collapsible') !== FALSE) {
        drupal_add_js('misc/collapse.js', 'core');
        $variables['scripts'] = drupal_get_js();
        return;
      }
    }
  }
}

/**
 * Implementation of hook_theme().
 */
function collapse_text_theme($existing, $type, $theme, $path) {
  return array(
    'collapse_text_fieldset' => array(
      'arguments' => array(
        'element',
      ),
    ),
  );
}

/**
 * Theme a section of collapsible text. By default, this function calls the
 * default 'theme_fieldset' implementation, but this function can be overridden
 * to implement a custom theme just for collapsed text.
 *
 * @param $element
 *   An associative array containing the properties of the element.
 *   Properties used: attributes, title, value, description, children, collapsible, collapsed
 * @return
 *   A themed HTML string representing the collapsed text.
 *
 * @ingroup themeable
 */
function theme_collapse_text_fieldset($element) {
  return drupal_render($element);
}

/**
 * Converts a string to a suitable html ID attribute.
 * Copied from zen_id_safe() in the Zen theme.
 *
 * http://www.w3.org/TR/html4/struct/global.html#h-7.5.2 specifies what makes a
 * valid ID attribute in HTML. This function:
 *
 * - Ensure an ID starts with an alpha character by optionally adding an 'id'.
 * - Replaces any character except alphanumeric characters with dashes.
 * - Converts entire string to lowercase.
 *
 * @param $string
 *   The string
 * @return
 *   The converted string
 */
function collapse_text_id_safe($string) {

  // Replace with dashes anything that isn't A-Z, numbers, dashes, or underscores.
  $string = strtolower(preg_replace('/[^a-zA-Z0-9-]+/', '-', $string));

  // If the first character is not a-z, add 'id' in front.
  if (!ctype_lower($string[0])) {

    // Don't use ctype_alpha since its locale aware.
    $string = 'id' . $string;
  }
  return $string;
}

Functions

Namesort descending Description
collapse_text_filter Implementation of hook_filter().
collapse_text_filter_tips Implementation of hook_filter_tips().
collapse_text_id_safe Converts a string to a suitable html ID attribute. Copied from zen_id_safe() in the Zen theme.
collapse_text_preprocess_page
collapse_text_process
collapse_text_process_recurs Provides a layer of encapsulation for the regex call.
collapse_text_theme Implementation of hook_theme().
theme_collapse_text_fieldset Theme a section of collapsible text. By default, this function calls the default 'theme_fieldset' implementation, but this function can be overridden to implement a custom theme just for collapsed text.
_collapse_text_replace_callback