toc_filter.module in TOC filter 7
Same filename and directory in other branches
Converts header tags into a linked table of contents.
File
toc_filter.moduleView source
<?php
/**
* @file
* Converts header tags into a linked table of contents.
*/
/**
* Implements hook_init().
*/
function toc_filter_init() {
if (module_exists('ctools')) {
drupal_add_js(drupal_get_path('module', 'ctools') . '/js/jump-menu.js', array(
'type' => 'file',
'every_page' => TRUE,
));
}
if (variable_get('toc_filter_smooth_scroll', '1')) {
drupal_add_js(drupal_get_path('module', 'toc_filter') . '/toc_filter.js', array(
'type' => 'file',
'every_page' => TRUE,
));
$settings = array(
'toc_filter_smooth_scroll_duration' => variable_get('toc_filter_smooth_scroll_duration', ''),
);
drupal_add_js($settings, 'setting');
}
}
/**
* Implements hook_menu().
*/
function toc_filter_menu() {
$items = array();
$items['admin/config/content/toc_filter'] = array(
'title' => 'TOC filter',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'toc_filter_admin_settings',
),
'access arguments' => array(
'administer site configuration',
),
'file' => 'toc_filter.admin.inc',
'type' => MENU_NORMAL_ITEM,
);
return $items;
}
/**
* Implements hook_filter_info().
*/
function toc_filter_filter_info() {
if (module_exists('ctools')) {
$description = t("Converts <@header_tag> tags to a linked table of contents or jump menu with an optional title. (i.e [TOC:(faq|numbered|jump-menu|menu) (title)]", array(
'@header_tag' => variable_get('toc_filter_header_tag', 'h3'),
));
}
else {
$description = t("Converts <@header_tag> tags to a linked table of contents with an optional title. (i.e [TOC:(faq|numbered) (title)]", array(
'@header_tag' => variable_get('toc_filter_header_tag', 'h3'),
));
}
$filters['toc_filter'] = array(
'title' => t('Table of contents'),
'description' => $description,
'default settings' => array(),
'settings callback' => '_toc_filter_settings_callback',
'process callback' => '_toc_filter_process_callback',
'tips callback' => '_toc_filter_tips_callback',
);
return $filters;
}
/**
* TOC filter tips.
*/
function _toc_filter_tips_callback($delta, $format, $long = FALSE) {
if (module_exists('ctools')) {
return t("Adding [TOC:(faq|ol|number|ul|bullet|jump-menu|menu) (title)] will generate a table of contents or jump menu linked to all the <@header_tag> tags with an optional title.", array(
"@header_tag" => variable_get('toc_filter_header_tag', 'h3'),
));
}
else {
return t("Adding [TOC:(faq|ol|number|ul|bullet) (title)] will generate a table of contents linked to all the <@header_tag> tags with an optional title.", array(
"@header_tag" => variable_get('toc_filter_header_tag', 'h3'),
));
}
}
/**
* TOC filter processor callback: Convert's <h2> to a linked table of contents.
*/
function _toc_filter_process_callback($text) {
if (stripos($text, '[toc') === FALSE || !preg_match('/(?:<p>)?\\s*\\[TOC(?::([a-z-]+))?([^\\]]+)?\\]\\s*(?:<\\/p>)?/i', $text, $matches)) {
if (variable_get('toc_filter_default_top', '0') === '0') {
return $text;
}
else {
return _toc_filter_process_callback('[toc:' . variable_get('toc_filter_default_type', 'ul') . "]\n" . $text);
}
}
// Must track nested filter calls which can be created by drupal_get_form('ctools_jump_menu').
static $processing = array();
$md5 = md5($text);
if (isset($processing[$md5])) {
return $text;
}
$processing[$md5] = TRUE;
$match = $matches[0];
$type = !empty($matches[1]) ? drupal_strtolower($matches[1]) : 'ul';
$title = !empty($matches[2]) ? trim($matches[2]) : '';
// Set format based on type.
switch ($type) {
case 'jump-menu':
case 'menu':
$format = 'jump_menu';
break;
case 'faq':
$format = 'faq';
break;
case 'number':
case 'ol':
$format = 'number';
break;
case 'ul':
case 'bullet':
default:
$format = 'bullet';
break;
}
$title = empty($title) ? variable_get('toc_filter_' . $format . '_default_title', '') : $title;
$is_numbered = $format == 'number' || $format == 'faq' ? TRUE : FALSE;
$header_tag = variable_get('toc_filter_header_tag', 'h3');
preg_match_all('/(<' . $header_tag . '[^>]*>)(.*?)(<\\/' . $header_tag . '>)/is', $text, $header_matches);
$targets = array();
$links = array();
for ($i = 0, $len = count($header_matches[0]); $i < $len; $i++) {
$header_match = $header_matches[0][$i];
$open_tag = $header_matches[1][$i];
$header_title = $header_matches[2][$i];
$close_tag = $header_matches[3][$i];
$header_id = preg_replace('/[^-a-z0-9]+/', '-', drupal_strtolower(trim($header_title)));
// Add header class to open tag
$open_tag_attributes = toc_filter_parse_tag_attributes($open_tag) + array(
'class' => '',
);
$open_tag_attributes['class'] .= (empty($open_tag_attributes['class']) ? '' : ' ') . ' toc-header toc-header-' . $format;
$header_replace = '<' . $header_tag . drupal_attributes($open_tag_attributes) . '>' . '<a name="' . $header_id . '" id="' . $header_id . '" class="toc-bookmark" rel="bookmark" title="' . strip_tags($header_title) . '"></a>' . ($is_numbered ? '<span class="toc-number">' . ($i + 1) . ' . </span> ' : '') . $header_title . $close_tag;
// Must manually build each link item since l() function can't generate just an anchor link (aka #anchor).
$targets['#' . $header_id] = strip_tags($header_title);
$links[] = '<a href="#' . $header_id . '">' . strip_tags($header_title, '<i><em><b><strong><br>') . '</a>';
// Add anchor before header
$back_to_top = theme('toc_filter_back_to_top', array(
'class' => $i == 0 ? 'first' : '',
));
$text = str_replace($header_match, $back_to_top . $header_replace, $text);
}
// If no TOC links found then just remove the [toc] tag and return the text.
if (empty($links)) {
return str_replace($match, '', $text);
}
// Theme list
$links_list_type = $is_numbered ? 'ol' : 'ul';
$toc_content = theme('item_list', array(
'items' => $links,
'title' => check_plain($title),
'type' => $links_list_type,
'attributes' => array(
'class' => 'toc-filter-links',
),
));
$output = theme('toc_filter', array(
'type' => $type,
'content' => $toc_content,
));
// Add jump menu with noscript list.
if ($format == 'jump_menu' && module_exists('ctools')) {
$options = array();
if ($title) {
$options['choose'] = check_plain($title);
}
ctools_include('jump-menu');
$jump_menu = drupal_get_form('ctools_jump_menu', $targets, $options);
$output = '<div class="toc-filter-jump-menu">' . drupal_render($jump_menu) . '</div>' . '<noscript>' . $output . '</noscript>';
}
// Prepend #top
$output = '<a name="top"></a>' . $output;
// Replace matched text with output.
$text = str_replace($match, $output, $text);
// Add closing back to top.
$text .= theme('toc_filter_back_to_top', array(
'class' => 'last',
));
unset($processing[$md5]);
return $text;
}
/**
* TOC filter settings callback.
*/
function _toc_filter_settings_callback() {
$form = array();
$form['toc_filter_settings'] = array(
'#type' => 'fieldset',
'#title' => t('TOC filter'),
'#description' => t('To configure this filter, please goto the global <a href="@href">TOC filter site configuration form</a>.', array(
'@href' => url('admin/config/content/toc_filter'),
)),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
return $form;
}
////////////////////////////////////////////////////////////////////////////////
// Utility functions
////////////////////////////////////////////////////////////////////////////////
/**
* Parses an xhtml tag's attributes into an associated array.
*/
function toc_filter_parse_tag_attributes($tag) {
preg_match_all('/(\\w+)\\s*=\\s*"([^"]+)"/', $tag, $matches);
$attributes = array();
for ($i = 0, $len = count($matches[1]); $i < $len; $i++) {
$attributes[$matches[1][$i]] = htmlspecialchars_decode($matches[2][$i], ENT_QUOTES);
}
return $attributes;
}
////////////////////////////////////////////////////////////////////////////////
// Theme functions
////////////////////////////////////////////////////////////////////////////////
/**
* Implements hook_theme().
*/
function toc_filter_theme() {
return array(
'toc_filter' => array(
'variables' => array(
'format' => '',
'content' => '',
),
),
'toc_filter_back_to_top' => array(
'variables' => array(
'class' => '',
),
),
);
}
/**
* Format back to top anchor link.
*/
function theme_toc_filter_back_to_top($variables) {
$class = $variables['class'] ? ' ' . $variables['class'] : '';
return '<div class="toc-filter-back-to-top' . $class . '"><a href="#top">' . t('Back to top') . '</a></div>';
}
/**
* Format TOC filter.
*/
function theme_toc_filter($variables) {
$output = '<div class="toc-filter toc-filter-' . $variables['format'] . '">';
$output .= '<div class="toc-filter-content">' . $variables['content'] . '</div>';
$output .= '</div>';
return $output;
}
Functions
Name | Description |
---|---|
theme_toc_filter | Format TOC filter. |
theme_toc_filter_back_to_top | Format back to top anchor link. |
toc_filter_filter_info | Implements hook_filter_info(). |
toc_filter_init | Implements hook_init(). |
toc_filter_menu | Implements hook_menu(). |
toc_filter_parse_tag_attributes | Parses an xhtml tag's attributes into an associated array. |
toc_filter_theme | Implements hook_theme(). |
_toc_filter_process_callback | TOC filter processor callback: Convert's <h2> to a linked table of contents. |
_toc_filter_settings_callback | TOC filter settings callback. |
_toc_filter_tips_callback | TOC filter tips. |