/[drupal]/contributions/modules/tableofcontents/tableofcontents.module
ViewVC logotype

Contents of /contributions/modules/tableofcontents/tableofcontents.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.4 - (show annotations) (download) (as text)
Wed May 16 20:04:16 2007 UTC (2 years, 6 months ago) by deviantintegral
Branch: MAIN
CVS Tags: HEAD
Changes since 1.3: +85 -16 lines
File MIME type: text/x-php
#124900: Allows for the user to define settings relating to the rendering of the ToC.
1 <?php
2 // $Id: tableofcontents.module,v 1.3 2007/05/16 17:41:24 deviantintegral Exp $
3
4 /**
5 * @file
6 * This is a module to generate a table of contents section based on <h[2-3]>
7 * tags. It currently depends on the headinganchors.module for hotlinking to work properly.
8 * I need to learn how to properly create a module dependancy or redesign to put both filters
9 * in the same module.
10 *
11 * For an example, see http://www.csaonline.ca/clublist.
12 */
13
14 /**
15 * Implementation of hook_help().
16 */
17 function tableofcontents_help($section) {
18 switch ($section) {
19 case 'admin/modules#description':
20 // This description is shown in the listing at admin/modules.
21 return t('A module to create a table of contents based on HTML header tags.');
22 }
23 }
24
25 /**
26 * Implementation of hook_filter_tips().
27 */
28 function tableofcontents_filter_tips($format, $long = FALSE) {
29 if ($long) {
30 return t('Every instance of "&lt;!--tableofcontents--&gt;" in the input text will be replaced with a table of contents.');
31 }
32 else {
33 return t('Insert &lt;!--tableofcontents--&gt; to insert a dynamic TOC.');
34 }
35 }
36
37 /**
38 * Implementation of hook_filter().
39 *
40 * This is where we do the building of the TOC and replace the toc tags as needed.
41 */
42 function tableofcontents_filter($op, $delta = 0, $format = -1, $text = '') {
43 if ($op == 'list') {
44 return array(
45 0 => t('Table of Contents'));
46 }
47
48 // This is where we store TOC options set by the user
49 global $toc_options;
50
51 switch ($op) {
52 case 'description':
53 return t('Inserts a table of contents in the place of &lt;!--tableofcontents--&gt; tags.');
54
55 // Ensure that the data is regenerated on every preview
56 case 'no cache':
57 return TRUE;
58
59 // We'll use the bytes 0xFE and 0xFF to replace < and > here. These bytes
60 // are not valid in UTF-8 data and thus unlikely to cause problems.
61 case 'prepare':
62 $options_regex = "(( [A-z]+: [A-z0-9 ]+;)+)?";
63 preg_match_all('!<\!--tableofcontents' . $options_regex . '-->!', $text, $options_str, PREG_PATTERN_ORDER);
64 preg_match_all('/([A-z]+): ([A-z0-9 ]+);/', $options_str[1][0], $options, PREG_PATTERN_ORDER);
65
66 $toc_options = array();
67 for($i = 0; $i < sizeof($options[1]); $i++) {
68 $toc_options[$options[1][$i]] = $options[2][$i];
69 }
70
71 /**
72 * Allowed options
73 */
74
75 $allowed_options = array("title", "list", "minlevel", "maxlevel");
76
77 // Check for invalid options
78 foreach ($toc_options as $key=>$value) {
79 if (!in_array($key, $allowed_options)) {
80 form_set_error("Table of Contents", t("%key is an invalid option.", array("%key" => $key)));
81 }
82 }
83
84 // Process default options
85 if (!array_key_exists("title", $toc_options)) {
86 $toc_options["title"] = "Table of Contents";
87 }
88 // Translate ToC
89 $toc_options["title"] = t($toc_options["title"]);
90
91 if (!array_key_exists("list", $toc_options)) {
92 $toc_options["list"] = "ol";
93 }
94
95 if (!array_key_exists("minlevel", $toc_options)) {
96 $toc_options["minlevel"] = "2";
97 }
98
99 if (!array_key_exists("maxlevel", $toc_options)) {
100 $toc_options["maxlevel"] = "3";
101 }
102
103 // Process allowed options
104 if (($toc_options["list"] != "ol") && ($toc_options["list"] != "ul")) {
105 form_set_error("Table of Contents", t("%key is an invalid list option. Please choose 'ol' or 'ul'.", array("%key" => $toc_options["list"])));
106 }
107
108 if (!($toc_options["minlevel"] >= 1 && $toc_options["minlevel"] <= 5)) {
109 form_set_error("Table of Contents", t("%key is an invalid minimum level option. Please choose a number between 1 and 5.", array("%key" => $toc_options["minlevel"])));
110 }
111
112 if (!($toc_options["maxlevel"] >= $toc_options["minlevel"] && $toc_options["maxlevel"] <= 5)) {
113 form_set_error("Table of Contents", t("%key is an invalid maximum depth option. Please choose a number between %min and 5.",
114 array("%min" => $toc_options["minlevel"], "%key" => $toc_options["maxlevel"])));
115 }
116
117 return preg_replace('!<\!--tableofcontents' . $options_regex . '-->!', '\xFE!--tableofcontents--\xFF', $text);
118
119 case 'process':
120 // We will build an array which looks like array(array('level' => 1, 'heading' => $text), ...);
121 $toc = array();
122 $matches = array();
123
124 /*
125 * $i is the index of the heading found
126 * $matches[0][$i] -> Whole string matched
127 * $matches[1][$i] -> First heading level
128 * $matches[2][$i] -> Whole string of attributes
129 * $matches[3][$i] -> id attibute, used for anchor
130 * $matches[4][$i] -> Text of id attribute
131 * $matches[5][$i] -> Text inside of h tag
132 * $matches[6][$i] -> Close heading level, should be equal to open level
133 */
134
135 preg_match_all('/<h([' .
136 $toc_options["minlevel"] . '-' .
137 $toc_options["maxlevel"] . '])( .*(id="([^"]+)" ?.*))?>(.*)<\/h([' .
138 $toc_options["minlevel"] . '-' .
139 $toc_options["maxlevel"] . '])>/', $text, $matches, PREG_PATTERN_ORDER);
140
141 $anchors = array();
142 for ($i = 0; $i < sizeof($matches[0]); $i++) {
143 // Strip HTML and non alphanumerics
144 $level = $matches[1][$i];
145 $heading = strip_tags($matches[5][$i]);
146 $anchor = $matches[4][$i];
147 array_push($toc, array(
148 'level' => $level,
149 'heading' => $heading,
150 'anchor' => $anchor)
151 );
152 }
153 // Build HTML
154 // We probably need to find a way to allow styling of this based on the theme.
155 // The obvious example is to put a shaded background behind the TOC.
156 // In the future it would also be nice to have collapseable headings using javascript
157 // for those really long documents.
158 $toc_html = "<div class=\"toc\">\n<h" .
159 $toc_options["minlevel"] . ">" .
160 $toc_options["title"] . "</h" .
161 $toc_options["minlevel"] . ">\n<" .
162 $toc_options["list"] . ">\n";
163
164 $depth = $toc_options["minlevel"];
165 foreach ($toc as $title) {
166 // We need to allow for nested lists
167 // It should be trivial to make the heading levels shown in the TOC user customizable
168 $curdepth = $title['level'];
169 if ($curdepth <= $toc_options["maxlevel"]) {
170 if ($curdepth > $depth) {
171 $toc_html .= "<" . $toc_options["list"] . ">\n";
172 }
173 else if ($curdepth < $depth) {
174 $toc_html .= "</" . $toc_options["list"] . ">\n";
175 }
176 $depth = $curdepth;
177 // Insert the list element.
178 $toc_html .= "<li><a href=\"#".$title['anchor']."\">".$title['heading']."</a></li>\n";
179 }
180 }
181 // Did we recurse back out to h2 tags? If not, close open lists.
182 while ($depth > $toc_options["minlevel"]) {
183 $toc_html .= "</" . $toc_options["list"] . ">\n";
184 $depth = $depth -1;
185 }
186 $toc_html .= "</div>\n";
187 // Find our previously changed string and replace with our generated TOC.
188 return str_replace('\xFE!--tableofcontents--\xFF', $toc_html, $text);
189 }
190 }
191 ?>

  ViewVC Help
Powered by ViewVC 1.1.2