collapse_text.module in Collapse Text 6.2
Same filename and directory in other branches
collapse_text is an input filter that allows text to be collapsible
Attributions not otherwhere noted:
- #259535 and #233877, ability to specify title in tag. Thanks rivena, Justyn
- #233877 requested ability to have nested tags.
File
collapse_text.moduleView source
<?php
/**
* @file
* collapse_text is an input filter that allows text to be collapsible
*
* Attributions not otherwhere noted:
* - #259535 and #233877, ability to specify title in tag. Thanks rivena, Justyn
* - #233877 requested ability to have nested tags.
*/
// #1245584 per Nikdilis; change default text because arrow isn't actually clickable.
define('COLLAPSE_TEXT_DEFAULT_TITLE', t('Click here to expand or collapse this section'));
/**
* Implementation of hook_filter_tips().
*/
function collapse_text_filter_tips($delta, $format, $long = false) {
if ($long) {
return t('<p>You may surround a section of text with "[collapse]" and "[/collapse]" to it into a collapsible section.</p>' . '<p>You may use "[collapse]" tags within other "[collapse]" tags for nested collapsing sections.</p>' . '<p>If you start with "[collapsed]" or "[collapse collapsed]", the section will default to a collapsed state.</p>' . '<p>You may specify a title for the section in two ways. You may add a "title=" parameter to the opening tag, such as "[collapse title=<your title here>]". In this case, you should surround the title with double-quotes. If you need to include double-quotes in the title, use the html entity "&quot;". For example: \'[collapse title="&quot;Once upon a time&quot;"]\'. If a title is not specified in the "[collapse]" tag, the title will be taken from the first heading found inside the section. A heading is specified using the "<hX>" html tag, where X is a number from 1-6. The heading will be removed from the section in order to prevent duplication. If a title is not found using these two methods, a default title will be supplied.</p>' . '<p>For advanced uses, you may also add a "class=" option to specify CSS classes to be added to the section. The CSS classes should be surrounded by double-quotes, and separated by spaces; e.g. \'[collapse class="class1 class2"]\'.</p>' . '<p>You may combine these options in (almost) any order. The "collapsed" option should always come first; things will break if it comes after "title=" or "class=". If you need to have it come after the other options, you must specify it as \'collapsed="collapsed"\'; e.g. \'[collapse title="foo" collapsed="collapsed"]\'.</p>' . '<p>If you wish to put the string "[collapse" into the document, you will need to prefix it with a backslash ("\\"). The first backslash before any instance of "[collapse" or "[/collapse" will be removed, all others will remain. Thus, if you want to display "[collapse stuff here", you should enter "\\[collapse stuff here". If you wish to display "\\[collapse other stuff", you will need to put in "\\\\[collapse other stuff". If you prefix three backslashes, two will be displayed, etc.</p>' . '<p>If you prefer, you can use angle brackets ("<>") instead of straight brackets ("[]"). This module will find any instance of "<collapse" and change it to "[collapse" (also fixing the end of the tags and the closing tags).</p>' . '<p>You may override the settings of the filter on an individual basis using a "[collapse options ...]" tag. The possible options now are \'form="form"\' or \'form="noform"\', and \'default_title="..."\'. For example, \'[collapse options form="noform" default_title="Click me!"]\'. Only the first options tag will be looked at, and the settings apply for the entire text area, not just the "[collapse]" tags following the options tag. Note that surrounding <p> and <br> tags will be removed.</p>' . '<p>This module supports some historical variants of the tag as well. The following are <strong>not</strong> recommended for any new text, but are left in place so that old uses still work. The "class=" option used to called "style=", and "style=" will be changed into "class=". If you don\'t put a double-quote immediately after "class=", everything up to the end of the tag or the string "title=" will be interpreted as the class string. Similarly, if you don\'t have a double-quote immediately following "title=", everything up to the end of the tag will be used as the title. Note that in this format, "style=" <em>must</em> precede "title=".</p>');
}
else {
return t('Use [collapse] and [/collapse] to create collapsible text blocks. [collapse collapsed] or [collapsed] will start with the block closed.');
}
}
/**
* 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':
return collapse_text_settings($format);
break;
case 'no cache':
return FALSE;
break;
case 'prepare':
return collapse_text_prepare($text);
break;
case 'process':
return collapse_text_process($text, $format);
break;
default:
return $text;
break;
}
}
/**
* Implementation of hook_filter($op='settings').
*/
function collapse_text_settings($format) {
$form = array();
$form['collapse_text'] = array(
'#type' => 'fieldset',
'#title' => t('Collapse text filter'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$form['collapse_text']["collapse_text_default_title_{$format}"] = array(
'#type' => 'textfield',
'#title' => t('Default title'),
'#description' => t('If no title is supplied for a section, use this as the default. This may not be empty. The original default title is "@default_title".', array(
'@default_title' => COLLAPSE_TEXT_DEFAULT_TITLE,
)),
'#default_value' => variable_get("collapse_text_default_title_{$format}", COLLAPSE_TEXT_DEFAULT_TITLE),
'#required' => TRUE,
);
$form['collapse_text']["collapse_text_use_form_{$format}"] = array(
'#type' => 'checkbox',
'#title' => t('Surround text with an empty form tag'),
'#description' => t('Collapse text works by generating <fieldset> tags. To validate as proper HTML, these need to be within a <form> tag. This option allows you to prevent the generation of the surrounding form tag. You probably do not want to change this.'),
'#default_value' => variable_get("collapse_text_use_form_{$format}", 1),
);
return $form;
}
/**
* Implementation of hook_filter($op='prepare').
*/
function collapse_text_prepare($text) {
// fix any html style (ie, '<>' delimited) tags into our '[]' style delimited tags
$text = preg_replace('/(?<!\\\\) # not preceded by a backslash
< # an open bracket
( # start capture
\\/? # optional backslash
collapse # the string collapse
[^>]* # everything up to the closing angle bracket; note that you cannot use one inside the tag!
) # stop capture
> # close bracket
/ix', '[$1]', $text);
$text = preg_replace_callback('/(?<!\\\\) # not preceded by a backslash
\\[ # open bracket
collapse # the string collapse
[^\\]]* # everything up to a closing straight bracket; note that you cannot use one inside a tag!
\\] # closing bracket
/ix', '_collapse_text_filter_prepare_regex_callback', $text);
return $text;
}
/**
* callback function for the prepare replacement.
* attempt to clean up poorly formatted tags
*/
function _collapse_text_filter_prepare_regex_callback($matches) {
// all regexes here are running against an already extracted tag
$tag = $matches[0];
// allow the [collapsed] open tag
$tag = preg_replace('/^ # start of tag
\\[ # open bracket
( # start capture
collapsed # the string collapsed
(?: |\\]) # either a space or a close bracket
) # end capture
/ix', '[collapse $1', $tag);
// fix the collapsed element
$tag = preg_replace('/^\\[collapse collapsed( |\\])/i', '[collapse collapsed="collapsed"$1', $tag);
// fix the style element. going forward, we prefer "class=".
$tag = preg_replace('/ style=([^"].*?)(?= collapsed=| title=|\\])/i', ' class="$1"', $tag);
$tag = preg_replace('/ style="/i', ' class="', $tag);
// fix the title element
// not sufficient if title includes double-quotes
$tag = preg_replace('/ title=([^"].*?)(?= collapsed=| class=|\\])/i', ' title="$1"', $tag);
return $tag;
}
/**
* Implementation of hook_filter($op='process').
*
* This function uses a pseudo-"parser". Earlier versions used a
* (very complex) regular expression, but I think this verion will
* work better and have fewer memory issues.
*/
function collapse_text_process($text, $format) {
// initialize the options, then check to see if an options
// tag is provided and if so, parse it. Note that only one
// options tag is supported per invocation.
$options = array(
'form' => variable_get("collapse_text_use_form_{$format}", 1),
'default_title' => variable_get("collapse_text_default_title_{$format}", COLLAPSE_TEXT_DEFAULT_TITLE),
);
list($text, $options) = _collapse_text_check_options($text, $options);
// find all of the collapse tags and their location in the string
$tags = _collapse_text_find_tags($text, $options);
// determine the level of nesting for each element.
$levels = _collapse_text_find_levels($tags, $options);
// process the text if there are any collapse tags...
if (count($levels)) {
// turn the levels and the string into a structured tree
$tree = _collapse_text_process_recurse_levels($text, 0, strlen($text), $levels, $options);
// take the tree, and turn it into FAPI elements, then embed
// them in a form if requested
// see #634666, per deviantintegral
static $render_number = 1;
// used to generate unique ids to prevent an E_NOTICE
$holder = array();
if ($options['form']) {
$holder = array(
'#type' => 'form',
'#theme' => 'collapse_text_form',
'#id' => 'collapse-text-dynamic-form-number-' . $render_number++,
);
}
else {
$holder = array(
'#type' => 'markup',
'#prefix' => '<div id="' . 'collapse-text-dynamic-div-number-' . $render_number++ . '">',
'#suffix' => '</div>',
);
}
$holder['collapse_text_internal_text'] = _collapse_text_process_recurse_tree($tree, $options);
// render the elements back to a string
$text = drupal_render($holder);
}
return $text;
}
/**
* see if there is an options tag available.
* if so, remove it from the text and set the options.
*/
function _collapse_text_check_options($text, $options) {
$matches = array();
$regex_text = '
(?<!\\\\) # not proceeded by a backslash
\\[ # opening bracket
collapse # the word collapse
\\s+ # white space
options # the word options
[^\\]]* # everything until the closing bracket
\\] # a closing bracket
';
if (preg_match('/' . $regex_text . '/smx', $text, $matches)) {
$opt_tag = $matches[0];
// remove the "collapse" from the front of the tag, baking in an "options" tag
$opt_tag = preg_replace('/^\\[collapse /', '[', $opt_tag);
// change to angle brackets, so it can be parsed as XML
$opt_tag = preg_replace(array(
'/^\\[/',
'/\\]$/',
), array(
'<',
'/>',
), $opt_tag);
// turn HTML entities into XML entities
// Issue #1109792 by eronte
$opt_tag = _collapse_text_html_to_xml_entities($opt_tag);
$opt_tag = simplexml_load_string($opt_tag);
// form options are either 'form="form"' or 'form="noform"'
if ($opt_tag['form'] == 'form') {
$options['form'] = 1;
}
elseif ($opt_tag['form'] == 'noform') {
$options['form'] = 0;
}
if ($opt_tag['default_title']) {
// Issue #1096070 by Asgardinho: issues with UTF8 text
$options['default_title'] = htmlspecialchars(trim($opt_tag['default_title']), ENT_QUOTES, 'UTF-8');
}
// remove the options tag, including any miscellaneous <p>, </p>, or <br> tags around it.
$text = preg_replace('/(?:<\\/?p>|<br\\s*\\/?>)*' . $regex_text . '(?:<\\/?p>|<br\\s*\\/?>)*/smx', '', $text);
}
return array(
$text,
$options,
);
}
/**
* find all of the [collapse...] tags and return an array of their locations
*/
function _collapse_text_find_tags($text, $options) {
$matches = array();
$regex = '/
(?<!\\\\) # not proceeded by a backslash
\\[ # opening bracket
\\/? # a closing tag?
collapse # the word collapse
[^\\]]* # everything until the closing bracket
\\] # a closing bracket
/smx';
preg_match_all($regex, $text, $matches, PREG_OFFSET_CAPTURE);
return $matches[0];
}
/**
* using the locations of the tags, determine what the nesting structure is.
*/
function _collapse_text_find_levels($tags, $options) {
$levels = array();
$curr_level = 0;
foreach ($tags as $item) {
// determine whether this is an open or close tag
$type = 'unknown';
if (substr($item[0], 0, 9) == '[collapse') {
$type = 'start';
}
elseif (substr($item[0], 0, 10) == '[/collapse') {
$type = 'end';
}
// the level of an open tag is incremented before we save its
// information, while the level of a close tag is decremented after
if ($type == 'start') {
$curr_level++;
}
$levels[] = array(
'type' => $type,
'tag' => $item[0],
'start' => $item[1],
'end' => $item[1] + strlen($item[0]),
'level' => $curr_level,
);
if ($type == 'end') {
$curr_level--;
}
}
return $levels;
}
/**
* translate the flat levels array into a tree.
*
* this function is recursive.
*/
function _collapse_text_process_recurse_levels($string, $string_start, $string_end, $elements, $options) {
$text_start = $string_start;
$text_length = $string_end - $string_start;
$child_start = $string_start;
$child_end = $string_end;
$slice_start = -1;
$slice_end = count($elements);
// find the first start element
$elt_start_found = FALSE;
$elt_start = 0;
while (!$elt_start_found and $elt_start < count($elements)) {
if ($elements[$elt_start]['type'] == 'start') {
$elt_start_found = TRUE;
}
else {
$elt_start++;
}
}
if ($elt_start_found) {
// if there is an opening element, set the text length to everything up to it
$text_length = $elements[$elt_start]['start'] - $string_start;
$child_start = $elements[$elt_start]['end'];
$slice_start = $elt_start + 1;
}
else {
// otherwise, return everything in this segment as a string
return array(
array(
'type' => 'text',
'value' => substr($string, $text_start, $text_length),
),
);
}
// find the next end element at the same level
$elt_end_found = FALSE;
$elt_end = $elt_start;
while (!$elt_end_found and $elt_end < count($elements)) {
if ($elements[$elt_end]['type'] == 'end' and $elements[$elt_end]['level'] == $elements[$elt_start]['level']) {
$elt_end_found = TRUE;
}
else {
$elt_end++;
}
}
if ($elt_end_found) {
$child_end = $elements[$elt_end]['start'];
$slice_length = $elt_end - $slice_start;
}
else {
// there is a matching failure
// try skipping the start element...
if ($elt_start + 1 < count($elements)) {
return _collapse_text_process_recurse_levels($string, $string_start, $string_end, array_slice($elements, $elt_start + 1), $options);
}
else {
// fall back to just returning the string...
$text_length = $string_end - $text_start;
// reset the text length
return array(
array(
'type' => 'text',
'value' => substr($string, $text_start, $text_length),
),
);
}
}
$parts = array();
// add the text before the opening element
$parts[] = array(
'type' => 'text',
'value' => substr($string, $text_start, $text_length),
);
// add the child element
$parts[] = array(
'type' => 'child',
'tag' => $elements[$elt_start]['tag'],
'value' => _collapse_text_process_recurse_levels($string, $child_start, $child_end, array_slice($elements, $slice_start, $slice_length), $options),
);
// tail recurse (which ideally could be optimized away, although it won't be...) to handle
// any siblings
$parts = array_merge($parts, _collapse_text_process_recurse_levels($string, $elements[$elt_end]['end'], $string_end, array_slice($elements, $elt_end), $options));
// return the result
return $parts;
}
/**
* Take a nested tree and turn it into a string.
*
* This function is recursive.
*/
function _collapse_text_process_recurse_tree($tree, $options) {
$parts = array();
$weight = 0;
// we use $weight to make sure elements are displayed in the correct order
foreach ($tree as $item) {
// iterate over the tree
$part = NULL;
if ($item['type'] == 'text') {
$part = _collapse_text_process_text_item($item['value'], $options);
}
elseif ($item['type'] = 'child') {
$part = _collapse_text_process_child_item($item, $options);
}
if (isset($part)) {
$part['#weight'] = $weight++;
$parts[] = $part;
}
}
return $parts;
}
/**
* process a text item.
*
* @todo -- deprecate the "collapsed-text" class
*/
function _collapse_text_process_text_item($item, $options) {
// remove any leftover [collapse] or [/collapse] tags, such as might be caused by the teaser
// leaving out the closing tag. Note that a backslash before the collapse tag will act
// as an escape.
$item = preg_replace('/(?<!\\\\)\\[\\/?collapse[^\\]]*\\]/', '', $item);
// remove the first backslash before any collapse tags. This allows collapse tags to be
// escaped.
$item = str_replace(array(
'\\[collapse',
'\\[/collapse',
), array(
'[collapse',
'[/collapse',
), $item);
// clear out some miscellaneous tags that are introduced by visual editors...
$item = preg_replace('/^<\\/p>/', '', $item);
// close paragraph right at the start
$item = preg_replace('/<p(?:\\s[^>]*)?>$/', '', $item);
// open paragraph right at the end
// clear out cruft introduced by the html line ending filter
// these are probably more controversial, since they may actually be intended...
$item = preg_replace('/^<br ?\\/?>/', '', $item);
// <br> at the very start
$item = preg_replace('/<br ?\\/?>$/', '', $item);
// <br> at the very end
// only return a value if there is something besides whitespace.
if (preg_match('/\\S/', $item)) {
return array(
'#type' => 'markup',
'#value' => $item,
'#prefix' => '<div class="collapse-text-text collapsed-text">',
'#suffix' => '</div>',
);
}
else {
return NULL;
}
}
/**
* process a child item.
*
* @todo -- deprecate the "collapsed-text-fieldset" class
*/
function _collapse_text_process_child_item($item, $options) {
// translate the "tag" into a proper tag, and then parse it
// as an xml tag; this is more robust than the previous method
$tag = preg_replace(array(
'/^\\[/',
'/\\]$/',
), array(
'<',
'/>',
), $item['tag']);
// turn HTML entities into XML entities
// Issue #1109792 by eronte
$tag = _collapse_text_html_to_xml_entities($tag);
$xmltag = simplexml_load_string($tag);
$collapsed = $xmltag['collapsed'] == 'collapsed';
$class = trim($xmltag['class']);
// Issue #1096070 by Asgardinho: issues with UTF8 text
$title = htmlspecialchars(trim($xmltag['title']), ENT_QUOTES, 'UTF-8');
// set up the styles array.
$classes = array();
$classes[] = _collapse_text_id_safe('collapse-text-fieldset');
$classes[] = _collapse_text_id_safe('collapsed-text-fieldset');
// @todo - deprecate
foreach (explode(' ', $class) as $c) {
if (!empty($c)) {
$classes[] = _collapse_text_id_safe($c);
}
}
// if a title is not supplied, look in the first child for a header tag
if (empty($title)) {
if ($item['value'][0]['type'] == 'text') {
$h_matches = array();
if (preg_match('/(<h\\d[^>]*>(.+?)<\\/h\\d>)/smi', $item['value'][0]['value'], $h_matches)) {
$title = strip_tags($h_matches[2]);
}
// if we get the title from the first header tag, we should remove it from
// the text so that it isn't repeated.
if (!empty($title)) {
$occ = 1;
// this is a hack to only replace the first instance.
$item['value'][0]['value'] = str_replace($h_matches[0], '', $item['value'][0]['value'], $occ);
}
}
}
// if still no title, put in the default title
if (empty($title)) {
$title = $options['default_title'];
$classes[] = _collapse_text_id_safe('collapse-text-default-title');
}
// create a fieldset that can be themed.
// per #634666, deviantintegral, although not including the form tag, as we allow nesting.
$fieldset = array(
'#type' => 'fieldset',
'#theme' => 'collapse_text_fieldset',
'#title' => $title,
'#collapsible' => TRUE,
'#collapsed' => $collapsed,
'#attributes' => array(
'class' => join(" ", $classes),
),
'collapse_text_contents' => _collapse_text_process_recurse_tree($item['value'], $options),
);
return $fieldset;
}
/**
* add the collapse.js file
* backport of #1664952 by mstrelan; we were trying to hard to be intelligent
* let's just be stupid.
*/
function collapse_text_preprocess_page(&$variables) {
drupal_add_js('misc/collapse.js', 'core');
$variables['scripts'] = drupal_get_js();
}
/**
* Implementation of hook_theme().
*/
function collapse_text_theme($existing, $type, $theme, $path) {
return array(
'collapse_text_fieldset' => array(
'arguments' => array(
'element',
),
),
'collapse_text_form' => 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);
}
/**
* Theme the outer form. This is required for the fieldset(s) to validate.
*/
function theme_collapse_text_form($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;
}
/**
* convert html entities to xml entities
* See issue #1109792 by eronte
*
* HTML entity lists from
* - http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent
* - http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent
* - http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent
*
* @todo -- rewrite to use str_replace
*/
function _collapse_text_html_to_xml_entities($text) {
static $replace = array(
// latin 1
' ' => ' ',
'¡' => '¡',
'¢' => '¢',
'£' => '£',
'¤' => '¤',
'¥' => '¥',
'¦' => '¦',
'§' => '§',
'¨' => '¨',
'©' => '©',
'ª' => 'ª',
'«' => '«',
'¬' => '¬',
'­' => '­',
'®' => '®',
'¯' => '¯',
'°' => '°',
'±' => '±',
'²' => '²',
'³' => '³',
'´' => '´',
'µ' => 'µ',
'¶' => '¶',
'·' => '·',
'¸' => '¸',
'¹' => '¹',
'º' => 'º',
'»' => '»',
'¼' => '¼',
'½' => '½',
'¾' => '¾',
'¿' => '¿',
'À' => 'À',
'Á' => 'Á',
'Â' => 'Â',
'Ã' => 'Ã',
'Ä' => 'Ä',
'Å' => 'Å',
'Æ' => 'Æ',
'Ç' => 'Ç',
'È' => 'È',
'É' => 'É',
'Ê' => 'Ê',
'Ë' => 'Ë',
'Ì' => 'Ì',
'Í' => 'Í',
'Î' => 'Î',
'Ï' => 'Ï',
'Ð' => 'Ð',
'Ñ' => 'Ñ',
'Ò' => 'Ò',
'Ó' => 'Ó',
'Ô' => 'Ô',
'Õ' => 'Õ',
'Ö' => 'Ö',
'×' => '×',
'Ø' => 'Ø',
'Ù' => 'Ù',
'Ú' => 'Ú',
'Û' => 'Û',
'Ü' => 'Ü',
'Ý' => 'Ý',
'Þ' => 'Þ',
'ß' => 'ß',
'à' => 'à',
'á' => 'á',
'â' => 'â',
'ã' => 'ã',
'ä' => 'ä',
'å' => 'å',
'æ' => 'æ',
'ç' => 'ç',
'è' => 'è',
'é' => 'é',
'ê' => 'ê',
'ë' => 'ë',
'ì' => 'ì',
'í' => 'í',
'î' => 'î',
'ï' => 'ï',
'ð' => 'ð',
'ñ' => 'ñ',
'ò' => 'ò',
'ó' => 'ó',
'ô' => 'ô',
'õ' => 'õ',
'ö' => 'ö',
'÷' => '÷',
'ø' => 'ø',
'ù' => 'ù',
'ú' => 'ú',
'û' => 'û',
'ü' => 'ü',
'ý' => 'ý',
'þ' => 'þ',
'ÿ' => 'ÿ',
// special
''' => ''',
'Œ' => 'Œ',
'œ' => 'œ',
'Š' => 'Š',
'š' => 'š',
'Ÿ' => 'Ÿ',
'ˆ' => 'ˆ',
'˜' => '˜',
' ' => ' ',
' ' => ' ',
' ' => ' ',
'‌' => '‌',
'‍' => '‍',
'‎' => '‎',
'‏' => '‏',
'–' => '–',
'—' => '—',
'‘' => '‘',
'’' => '’',
'‚' => '‚',
'“' => '“',
'”' => '”',
'„' => '„',
'†' => '†',
'‡' => '‡',
'‰' => '‰',
'‹' => '‹',
'›' => '›',
'€' => '€',
// symbols
'ƒ' => 'ƒ',
'Α' => 'Α',
'Β' => 'Β',
'Γ' => 'Γ',
'Δ' => 'Δ',
'Ε' => 'Ε',
'Ζ' => 'Ζ',
'Η' => 'Η',
'Θ' => 'Θ',
'Ι' => 'Ι',
'Κ' => 'Κ',
'Λ' => 'Λ',
'Μ' => 'Μ',
'Ν' => 'Ν',
'Ξ' => 'Ξ',
'Ο' => 'Ο',
'Π' => 'Π',
'Ρ' => 'Ρ',
'Σ' => 'Σ',
'Τ' => 'Τ',
'Υ' => 'Υ',
'Φ' => 'Φ',
'Χ' => 'Χ',
'Ψ' => 'Ψ',
'Ω' => 'Ω',
'α' => 'α',
'β' => 'β',
'γ' => 'γ',
'δ' => 'δ',
'ε' => 'ε',
'ζ' => 'ζ',
'η' => 'η',
'θ' => 'θ',
'ι' => 'ι',
'κ' => 'κ',
'λ' => 'λ',
'μ' => 'μ',
'ν' => 'ν',
'ξ' => 'ξ',
'ο' => 'ο',
'π' => 'π',
'ρ' => 'ρ',
'ς' => 'ς',
'σ' => 'σ',
'τ' => 'τ',
'υ' => 'υ',
'φ' => 'φ',
'χ' => 'χ',
'ψ' => 'ψ',
'ω' => 'ω',
'ϒ' => 'ϒ',
'ϖ' => 'ϖ',
'•' => '•',
'…' => '…',
'′' => '′',
'″' => '″',
'‾' => '‾',
'⁄' => '⁄',
'℘' => '℘',
'ℑ' => 'ℑ',
'ℜ' => 'ℜ',
'™' => '™',
'ℵ' => 'ℵ',
'←' => '←',
'↑' => '↑',
'→' => '→',
'↓' => '↓',
'↔' => '↔',
'↵' => '↵',
'⇐' => '⇐',
'⇑' => '⇑',
'⇒' => '⇒',
'⇓' => '⇓',
'⇔' => '⇔',
'∀' => '∀',
'∂' => '∂',
'∃' => '∃',
'∅' => '∅',
'∇' => '∇',
'∈' => '∈',
'∉' => '∉',
'∋' => '∋',
'∏' => '∏',
'∑' => '∑',
'−' => '−',
'∗' => '∗',
'√' => '√',
'∝' => '∝',
'∞' => '∞',
'∠' => '∠',
'∧' => '∧',
'∨' => '∨',
'∩' => '∩',
'∪' => '∪',
'∫' => '∫',
'∴' => '∴',
'∼' => '∼',
'≅' => '≅',
'≈' => '≈',
'≠' => '≠',
'≡' => '≡',
'≤' => '≤',
'≥' => '≥',
'⊂' => '⊂',
'⊃' => '⊃',
'⊄' => '⊄',
'⊆' => '⊆',
'⊇' => '⊇',
'⊕' => '⊕',
'⊗' => '⊗',
'⊥' => '⊥',
'⋅' => '⋅',
'⌈' => '⌈',
'⌉' => '⌉',
'⌊' => '⌊',
'⌋' => '⌋',
'⟨' => '〈',
'⟩' => '〉',
'◊' => '◊',
'♠' => '♠',
'♣' => '♣',
'♥' => '♥',
'♦' => '♦',
);
// only run the substitution if there is actually an entity in the tag.
if (strpos($text, '&') !== FALSE) {
$text = strtr($text, $replace);
}
return $text;
}
Functions
Name![]() |
Description |
---|---|
collapse_text_filter | Implementation of hook_filter(). |
collapse_text_filter_tips | Implementation of hook_filter_tips(). |
collapse_text_prepare | Implementation of hook_filter($op='prepare'). |
collapse_text_preprocess_page | add the collapse.js file backport of #1664952 by mstrelan; we were trying to hard to be intelligent let's just be stupid. |
collapse_text_process | Implementation of hook_filter($op='process'). |
collapse_text_settings | Implementation of hook_filter($op='settings'). |
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. |
theme_collapse_text_form | Theme the outer form. This is required for the fieldset(s) to validate. |
_collapse_text_check_options | see if there is an options tag available. if so, remove it from the text and set the options. |
_collapse_text_filter_prepare_regex_callback | callback function for the prepare replacement. attempt to clean up poorly formatted tags |
_collapse_text_find_levels | using the locations of the tags, determine what the nesting structure is. |
_collapse_text_find_tags | find all of the [collapse...] tags and return an array of their locations |
_collapse_text_html_to_xml_entities | convert html entities to xml entities See issue #1109792 by eronte |
_collapse_text_id_safe | Converts a string to a suitable html ID attribute. Copied from zen_id_safe() in the Zen theme. |
_collapse_text_process_child_item | process a child item. |
_collapse_text_process_recurse_levels | translate the flat levels array into a tree. |
_collapse_text_process_recurse_tree | Take a nested tree and turn it into a string. |
_collapse_text_process_text_item | process a text item. |
Constants
Name![]() |
Description |
---|---|
COLLAPSE_TEXT_DEFAULT_TITLE |