- <?php
 -  * @file
 -  * Framework for handling the filtering of content.
 -  */
 - 
 -  * Implements hook_theme().
 -  */
 - function filter_theme() {
 -   $base = array(
 -     'file' => 'filter.theme.inc',
 -   );
 - 
 -   return array(
 -     'filter_admin_overview' => array(
 -       'render element' => 'form',
 -     ) + $base,
 -     'filter_admin_format_filter_order' => array(
 -       'render element' => 'element',
 -     ) + $base,
 -     'filter_tips' => array(
 -       'variables' => array('tips' => NULL, 'long' => FALSE),
 -     ) + $base,
 -     'text_format_wrapper' => array(
 -       'render element' => 'element',
 -     ) + $base,
 -     'filter_caption' => array(
 -       'variables' => array('item' => NULL, 'caption' => '', 'attributes' => array()),
 -     ) + $base,
 -     'filter_tips_more_info' => array(
 -       'variables' => array(),
 -     ) + $base,
 -     'filter_guidelines' => array(
 -       'variables' => array('format' => NULL),
 -     ) + $base,
 -     'filter_format_editor_image_form' => array(
 -         'render element' => 'form',
 -     ) + $base,
 -   );
 - }
 - 
 -  * Implements hook_element_info().
 -  *
 -  * @see filter_process_format()
 -  * @see text_format_wrapper()
 -  */
 - function filter_element_info() {
 -   $type['text_format'] = array(
 -     
 -     '#format' => NULL,
 -     
 -     
 -     '#allowed_formats' => array(),
 -     
 -     
 -     
 -     '#editor_uploads' => FALSE,
 -     '#process' => array('filter_process_format'),
 -     '#base_type' => 'textarea',
 -     '#theme_wrappers' => array('text_format_wrapper'),
 -     '#description_display' => 'before',
 -   );
 -   return $type;
 - }
 - 
 -  * Implements hook_menu().
 -  */
 - function filter_menu() {
 -   $items['filter/tips'] = array(
 -     'title' => 'Compose tips',
 -     'page callback' => 'filter_tips_long',
 -     'access callback' => TRUE,
 -     'type' => MENU_CALLBACK,
 -     'file' => 'filter.pages.inc',
 -   );
 -   $items['editor/dialog/link/%filter_format'] = array(
 -     'title' => 'Edit image',
 -     'page callback' => 'backdrop_get_form',
 -     'page arguments' => array('filter_format_editor_link_form', 3),
 -     'access callback' => 'filter_dialog_access',
 -     'access arguments' => array(3, 'link'),
 -     'theme callback' => 'ajax_base_page_theme',
 -     'type' => MENU_CALLBACK,
 -     'file' => 'filter.pages.inc',
 -   );
 -   $items['editor/dialog/image/%filter_format'] = array(
 -     'title' => 'Edit image',
 -     'page callback' => 'backdrop_get_form',
 -     'page arguments' => array('filter_format_editor_image_form', 3),
 -     'access callback' => 'filter_dialog_access',
 -     'access arguments' => array(3, 'image'),
 -     'theme callback' => 'ajax_base_page_theme',
 -     'type' => MENU_CALLBACK,
 -     'file' => 'filter.pages.inc',
 -   );
 -   $items['filter/tips/%filter_format'] = array(
 -     'title' => 'Compose tips',
 -     'page callback' => 'filter_tips_long',
 -     'page arguments' => array(2),
 -     'access callback' => 'filter_access',
 -     'access arguments' => array(2),
 -     'file' => 'filter.pages.inc',
 -   );
 -   $items['admin/config/content/formats'] = array(
 -     'title' => 'Text editors and formats',
 -     'description' => 'Configure WYSIWYG and text editors on the site. Restrict or allow certain HTML tags to be used in content.',
 -     'page callback' => 'backdrop_get_form',
 -     'page arguments' => array('filter_admin_overview'),
 -     'access arguments' => array('administer filters'),
 -     'file' => 'filter.admin.inc',
 -   );
 -   $items['admin/config/content/formats/list'] = array(
 -     'title' => 'List text formats',
 -     'type' => MENU_DEFAULT_LOCAL_TASK,
 -   );
 -   $items['admin/config/content/formats/add'] = array(
 -     'title' => 'Add text format',
 -     'page callback' => 'filter_admin_format_page',
 -     'access arguments' => array('administer filters'),
 -     'type' => MENU_LOCAL_ACTION,
 -     'weight' => 1,
 -     'file' => 'filter.admin.inc',
 -   );
 -   $items['admin/config/content/formats/%filter_format'] = array(
 -     'title callback' => 'filter_admin_format_title',
 -     'title arguments' => array(4),
 -     'load arguments' => array(TRUE),
 -     'page callback' => 'filter_admin_format_page',
 -     'page arguments' => array(4),
 -     'access arguments' => array('administer filters'),
 -     'file' => 'filter.admin.inc',
 -   );
 -   $items['admin/config/content/formats/%filter_format/enable'] = array(
 -     'title' => 'Enable text format',
 -     'load arguments' => array(TRUE),
 -     'page callback' => 'filter_admin_enable',
 -     'page arguments' => array(4),
 -     'access arguments' => array('administer filters'),
 -     'type' => MENU_CALLBACK,
 -     'file' => 'filter.admin.inc',
 -   );
 -   $items['admin/config/content/formats/%filter_format/disable'] = array(
 -     'title' => 'Disable text format',
 -     'page callback' => 'backdrop_get_form',
 -     'page arguments' => array('filter_admin_disable', 4),
 -     'access callback' => '_filter_disable_format_access',
 -     'access arguments' => array(4),
 -     'file' => 'filter.admin.inc',
 -   );
 -   $items['admin/config/content/formats/%filter_format/filter-settings/%'] = array(
 -     'title callback' => 'filter_admin_format_title',
 -     'title arguments' => array(4),
 -     'page callback' => 'backdrop_get_form',
 -     'page arguments' => array('filter_admin_format_filter_settings_form', 4, 6),
 -     'access arguments' => array('administer filters'),
 -     'file' => 'filter.admin.inc',
 -   );
 -   return $items;
 - }
 - 
 -  * Access callback: Checks a user's access to a particular text format.
 -  *
 -  * @param $format
 -  *   A text format object.
 -  *
 -  * @return
 -  *   TRUE if the text format can be used by the current user, FALSE otherwise.
 -  *
 -  * @see filter_menu()
 -  */
 - function filter_format_access($format) {
 -   $permission = filter_permission_name($format);
 -   return ($format->format === filter_fallback_format()) || user_access($permission);
 - }
 - 
 -  * Access callback: Checks access for disabling text formats.
 -  *
 -  * @param $format
 -  *   A text format object.
 -  *
 -  * @return
 -  *   TRUE if the text format can be disabled by the current user, FALSE
 -  *   otherwise.
 -  *
 -  * @see filter_menu()
 -  */
 - function _filter_disable_format_access($format) {
 -   
 -   return user_access('administer filters') && ($format->format != filter_fallback_format());
 - }
 - 
 -  * Access callback: Generic dialog access check callback.
 -  *
 -  * Ensures that a dialog is only opened from a safe calling page and via an
 -  * AJAX request from the same site.
 -  */
 - function filter_dialog_access($format, $dialog_name, $account = NULL, $calling_path = NULL) {
 -   if (!isset($account)) {
 -     $account = $GLOBALS['user'];
 -   }
 -   if (!isset($calling_path) && isset($_GET['token'])) {
 -     $calling_path = $_GET['calling_path'];
 -   }
 - 
 -   $format_access = filter_access($format, $account);
 -   $tokens_match = FALSE;
 -   if (isset($_GET['token'])) {
 -     $tokens_match = ($_GET['token'] === filter_editor_dialog_token($format, $dialog_name, $account, $calling_path));
 -   }
 - 
 -   return $format_access && $tokens_match;
 - }
 - 
 -  * Implements hook_config_info().
 -  */
 - function filter_config_info() {
 -   $prefixes['filter.format'] = array(
 -     'name_key' => 'format',
 -     'label_key' => 'name',
 -     'group' => t('Text formats'),
 -   );
 -   return $prefixes;
 - }
 - 
 -  * Implements hook_config_data_validate().
 -  */
 - function filter_config_data_validate(Config $config, ?array $config_info) {
 -   if (strpos($config->getName(), 'filter.format.') === 0) {
 -     $filters = $config->get('filters');
 -     $all_filter_info = filter_get_filters();
 -     foreach ($filters as $filter_name => $filter) {
 -       if (isset($filter['module']) && !module_exists($filter['module'])) {
 -         throw new ConfigValidateException(t('The configuration "@file" could not be imported because the module "@module" that provides the filter "@filter" is not enabled.', array('@file' => $config->getName(), '@module' => $filter['module'], '@filter' => $filter_name)));
 -       }
 -       if (!isset($all_filter_info[$filter_name])) {
 -         throw new ConfigValidateException(t('The configuration "@file" could not be imported because the filter "@filter" is not recognized. Be sure that the module providing this filter is enabled.', array('@file' => $config->getName(), '@filter' => $filter_name)));
 -       }
 -     }
 -   }
 - }
 - 
 -  * Loads a text format object from the database.
 -  *
 -  * @param $format_id
 -  *   The format ID.
 -  * @param boolean $load_if_disabled
 -  *   If TRUE the format will be loaded when disabled.
 -  *
 -  * @return stdClass|FALSE
 -  *   A fully-populated text format object or FALSE if the format is not found.
 -  *
 -  * @see filter_format_exists()
 -  */
 - function filter_format_load($format_id, $load_if_disabled = FALSE) {
 -   $formats = filter_formats(NULL, $load_if_disabled);
 -   return isset($formats[$format_id]) ? $formats[$format_id] : FALSE;
 - }
 - 
 -  * Builds a text format object from initial values.
 -  *
 -  * @param $format
 -  *   A format object having the properties:
 -  *   - format: A machine-readable name representing the ID of the text format
 -  *     to save. If this corresponds to an existing text format, that format
 -  *     will be updated; otherwise, a new format will be created.
 -  *   - name: The title of the text format.
 -  *   - status: (optional) An integer indicating whether the text format is
 -  *     enabled (1) or not (0). Defaults to 1.
 -  *   - weight: (optional) The weight of the text format, which controls its
 -  *     placement in text format lists. If omitted, the weight is set to 0.
 -  *   - editor: (optional) The machine-readable name of the editor to use for
 -  *     this text format (e.g. 'ckeditor'). Defaults to NULL.
 -  *   - editor_settings: (optional) An associative array of settings for the
 -  *     specified editor. See standard_install() for an example.
 -  *   - filters: (optional) An associative, multi-dimensional array of filters
 -  *     assigned to the text format, keyed by the name of each filter and using
 -  *     the properties:
 -  *     - weight: (optional) The weight of the filter in the text format. If
 -  *       omitted, either the currently stored weight is retained (if there is
 -  *       one), or the filter is assigned a weight of 10, which will usually
 -  *       put it at the bottom of the list.
 -  *     - status: (optional) A boolean indicating whether the filter is
 -  *       enabled in the text format. If omitted, the filter will be disabled.
 -  *     - settings: (optional) An array of configured settings for the filter.
 -  *       See hook_filter_info() for details.
 -  *
 -  * @return $format
 -  *   A text format object suitable for saving to configuration.
 -  */
 - function filter_format_build_format($format) {
 -   $format->name = trim($format->name);
 -   $format->cache = _filter_format_is_cacheable($format);
 -   if (!isset($format->status)) {
 -     $format->status = 1;
 -   }
 -   if (!isset($format->weight)) {
 -     $format->weight = 0;
 -   }
 -   if (!isset($format->editor)) {
 -     $format->editor = NULL;
 -   }
 - 
 -   
 -   if (!isset($format->filters)) {
 -     $format->filters = array();
 -   }
 -   $all_filter_info = filter_get_filters();
 -   foreach ($format->filters as $name => $filter) {
 -     if (!array_key_exists($name, $all_filter_info)) {
 -       
 -       
 -       unset($format->filters[$name]);
 -       continue;
 -     }
 - 
 -     $filter_info = $all_filter_info[$name];
 - 
 -     
 -     
 -     
 -     $filter = (object) $filter;
 - 
 -     
 -     
 -     $filter->weight = (int) (isset($filter->weight) ? $filter->weight : $filter_info['weight']);
 -     $filter->status = (int) (isset($filter->status) ? $filter->status : 0);
 -     $filter->module = $filter_info['module'];
 -     $filter->settings = isset($filter->settings) ? $filter->settings : array();
 -     $format->filters[$name] = $filter;
 -   }
 - 
 -   return $format;
 - }
 - 
 -  * Saves a text format object to configuration.
 -  *
 -  * @param $format
 -  *   A format object having the properties:
 -  *   - format: A machine-readable name representing the ID of the text format
 -  *     to save. If this corresponds to an existing text format, that format
 -  *     will be updated; otherwise, a new format will be created.
 -  *   - name: The title of the text format.
 -  *   - status: (optional) An integer indicating whether the text format is
 -  *     enabled (1) or not (0). Defaults to 1.
 -  *   - weight: (optional) The weight of the text format, which controls its
 -  *     placement in text format lists. If omitted, the weight is set to 0.
 -  *   - editor: (optional) The machine-readable name of the editor to use for
 -  *     this text format (e.g. 'ckeditor'). Defaults to NULL.
 -  *   - editor_settings: (optional) An associative array of settings for the
 -  *     specified editor. See standard_install() for an example.
 -  *   - filters: (optional) An associative, multi-dimensional array of filters
 -  *     assigned to the text format, keyed by the name of each filter and using
 -  *     the properties:
 -  *     - weight: (optional) The weight of the filter in the text format. If
 -  *       omitted, either the currently stored weight is retained (if there is
 -  *       one), or the filter is assigned a weight of 10, which will usually
 -  *       put it at the bottom of the list.
 -  *     - status: (optional) A boolean indicating whether the filter is
 -  *       enabled in the text format. If omitted, the filter will be disabled.
 -  *     - settings: (optional) An array of configured settings for the filter.
 -  *       See hook_filter_info() for details.
 -  *
 -  * @return
 -  *   SAVED_NEW or SAVED_UPDATED.
 -  */
 - function filter_format_save($format) {
 -   $format = filter_format_build_format($format);
 - 
 -   
 -   $config = config('filter.format.' . $format->format);
 -   $is_new = $config->isNew();
 - 
 -   
 -   $format_data = (array) $format;
 -   foreach ($format_data['filters'] as $filter_name => $filter) {
 -     $filter_data = (array) $filter;
 -     if (isset($filter_data['name'])) {
 -       unset($filter_data['name']);
 -     }
 -     $format_data['filters'][$filter_name] = $filter_data;
 -   }
 -   if (isset($format_data['is_new'])) {
 -     unset($format_data['is_new']);
 -   }
 - 
 -   $config->setData($format_data);
 -   $config->save();
 - 
 -   if ($is_new) {
 -     module_invoke_all('filter_format_insert', $format);
 -     $return = SAVED_NEW;
 -   }
 -   else {
 -     module_invoke_all('filter_format_update', $format);
 -     
 -     
 -     
 -     
 -     $return = SAVED_UPDATED;
 - 
 -     
 -     cache('filter')->deletePrefix($format->format . ':');
 -   }
 - 
 -   filter_formats_reset();
 - 
 -   return $return;
 - }
 - 
 -  * Enables a text format.
 -  *
 -  * There is no core facility to re-enable a disabled format. It is not deleted
 -  * to keep information for contrib and to make sure the format ID is never
 -  * reused. As there might be content using the disabled format, this would lead
 -  * to data corruption.
 -  *
 -  * @param $format
 -  *   The text format object to be enabled.
 -  */
 - function filter_format_enable($format) {
 -   config_set('filter.format.' . $format->format, 'status', 1);
 - 
 -   
 -   module_invoke_all('filter_format_enable', $format);
 - 
 -   
 -   filter_formats_reset();
 -   cache('filter')->deletePrefix($format->format . ':');
 - }
 - 
 -  * Disables a text format.
 -  *
 -  * There is no core facility to re-enable a disabled format. It is not deleted
 -  * to keep information for contrib and to make sure the format ID is never
 -  * reused. As there might be content using the disabled format, this would lead
 -  * to data corruption.
 -  *
 -  * @param $format
 -  *   The text format object to be disabled.
 -  */
 - function filter_format_disable($format) {
 -   config_set('filter.format.' . $format->format, 'status', 0);
 - 
 -   
 -   module_invoke_all('filter_format_disable', $format);
 - 
 -   
 -   filter_formats_reset();
 -   cache('filter')->deletePrefix($format->format . ':');
 - }
 - 
 -  * Determines if a text format exists.
 -  *
 -  * @param $format_id
 -  *   The ID of the text format to check.
 -  *
 -  * @return
 -  *   TRUE if the text format exists, FALSE otherwise. Note that for disabled
 -  *   formats filter_format_exists() will return TRUE while filter_format_load()
 -  *   will return FALSE.
 -  *
 -  * @see filter_format_load()
 -  */
 - function filter_format_exists($format_id) {
 -   $filter_format_names = config_get_names_with_prefix('filter.format');
 -   return in_array('filter.format.' . $format_id, $filter_format_names, TRUE);
 - }
 - 
 -  * Get a complete list of allowed and forbidden tags for a text format.
 -  *
 -  * @param $format
 -  *   The text format object for which the list will be generated.
 -  *
 -  * @return array|TRUE
 -  *   An array of allowed HTML with the following keys:
 -  *   - allowed: A list of allowed tags keyed by tag name. The value is an array
 -  *     of attributes.
 -  *   - forbidden: An unindexed array of tags that are not allowed.
 -  *   For the full documentation on the return values of these two properties,
 -  *   see callback_filter_allowed_html().
 -  *   If TRUE is returned, then there are no restrictions on this format's HTML
 -  *   content.
 -  *
 -  * @see callback_filter_allowed_html()
 -  */
 - function filter_format_allowed_html($format) {
 -   $all_filter_info = filter_get_filters();
 -   $all_html_allowed = TRUE;
 -   $restrictions = array(
 -     'allowed' => array(),
 -     'forbidden' => array(),
 -   );
 - 
 -   foreach ($format->filters as $filter_name => $filter) {
 -     
 -     if (!$filter->status) {
 -       continue;
 -     }
 -     
 -     if (!isset($all_filter_info[$filter_name])) {
 -       continue;
 -     }
 -     
 -     $filter_info = $all_filter_info[$filter_name];
 -     if (!isset($filter_info['allowed html callback'])) {
 -       continue;
 -     }
 - 
 -     $allowed_html_callback = $filter_info['allowed html callback'];
 -     $filter_restrictions = $allowed_html_callback($filter, $format);
 - 
 -     if ($filter_restrictions) {
 -       $all_html_allowed = FALSE;
 -     }
 -     else {
 -       continue;
 -     }
 - 
 -     
 -     
 -     if (isset($filter_restrictions['forbidden'])) {
 -       $restrictions['forbidden'] = array_unique(array_merge($restrictions['forbidden'], $filter_restrictions['forbidden']));
 -     }
 - 
 -     
 -     
 -     $intersected_tags = array();
 -     foreach ($filter_restrictions['allowed'] as $tag => $attributes) {
 -       if (!isset($restrictions['allowed'][$tag])) {
 -         $restrictions['allowed'][$tag] = $attributes;
 -       }
 -       else {
 -         $intersected_tags[$tag] = $attributes;
 -       }
 -     }
 - 
 -     
 -     
 -     
 -     foreach ($intersected_tags as $tag => $attributes) {
 -       $intersection = NULL;
 -       $current_attributes = isset($restrictions['allowed'][$tag]) ? $restrictions['allowed'][$tag] : array();
 -       $new_attributes = $filter_restrictions['allowed'][$tag];
 -       
 -       if (!is_array($current_attributes) && $current_attributes == FALSE) {
 -         continue;
 -       }
 -       
 -       elseif (!is_array($current_attributes) && $current_attributes == TRUE && ($new_attributes == FALSE || is_array($new_attributes))) {
 -         $intersection = $new_attributes;
 -       }
 -       
 -       elseif (is_array($current_attributes) && $new_attributes == FALSE) {
 -         $intersection = $new_attributes;
 -       }
 -       
 -       elseif (is_array($current_attributes) && $new_attributes == TRUE) {
 -         continue;
 -       }
 -       
 -       elseif ($current_attributes == $new_attributes) {
 -         continue;
 -       }
 -       
 -       
 -       
 -       
 -       
 -       else {
 -         $intersection = array_intersect_key($current_attributes, $new_attributes);
 -         foreach (array_keys($intersection) as $attribute_value) {
 -           $intersection[$attribute_value] = $intersection[$attribute_value] && $new_attributes[$attribute_value];
 -         }
 -       }
 -       if (isset($intersection)) {
 -         $restrictions['allowed'][$tag] = $intersection;
 -       }
 -     }
 -   }
 - 
 -   
 -   
 -   
 -   
 -   if (isset($restrictions['allowed']) && isset($restrictions['forbidden'])) {
 -     foreach ($restrictions['forbidden'] as $tag) {
 -       if (isset($restrictions['allowed'][$tag])) {
 -         unset($restrictions['allowed'][$tag]);
 -       }
 -     }
 -     $restrictions['forbidden'] = array();
 -   }
 - 
 -   
 -   
 -   
 -   if (isset($restrictions['allowed'])) {
 -     if (count($restrictions['allowed']) === 1 && array_key_exists('*', $restrictions['allowed']) && !isset($restrictions['forbidden'])) {
 -       $restrictions['allowed'] = array();
 -     }
 -   }
 - 
 -   
 -   
 -   if ($all_html_allowed) {
 -     $restrictions = TRUE;
 -   }
 - 
 -   return $restrictions;
 - }
 - 
 -  * Displays a text format form title.
 -  *
 -  * @param object $format
 -  *   A format object.
 -  *
 -  * @return string
 -  *   The name of the format.
 -  *
 -  * @see filter_menu()
 -  */
 - function filter_admin_format_title($format) {
 -   return $format->name;
 - }
 - 
 -  * Implements hook_permission().
 -  */
 - function filter_permission() {
 -   $perms['administer filters'] = array(
 -     'title' => t('Administer text formats and filters'),
 -     'description' => t('Define how text is handled by combining filters into <a href="@url">text formats</a>.', array('@url' => url('admin/config/content/formats'))),
 -     'restrict access' => TRUE,
 -     'warning' => t('Permit any HTML tag in content (e.g.: <code><script></code>) and/or allow file uploads of any filetype via CKEditor.'),
 -   );
 - 
 -   $perms['upload editor images'] = array(
 -     'title' => t('Upload images through editor dialogs'),
 -     'description' => t('Allow users with access to image dialogs to upload files.'),
 -   );
 - 
 -   $perms['upload editor files'] = array(
 -     'title' => t('Upload files through editor dialogs'),
 -     'description' => t('Allow users with access to editor dialogs to upload files.'),
 -   );
 - 
 -   
 -   
 -   foreach (filter_formats() as $format) {
 -     $permission = filter_permission_name($format);
 -     if (!empty($permission)) {
 -       
 -       
 -       $format_name_replacement = l($format->name, 'admin/config/content/formats/' . $format->format);
 -       $perms[$permission] = array(
 -         'title' => t("Use the !text_format text format", array('!text_format' => $format_name_replacement,)),
 -         'description' => backdrop_placeholder(t('Warning: This permission may have security implications depending on how the text format is configured.')),
 -       );
 -     }
 -   }
 -   return $perms;
 - }
 - 
 -  * Returns the machine-readable permission name for a provided text format.
 -  *
 -  * @param $format
 -  *   An object representing a text format.
 -  *
 -  * @return
 -  *   The machine-readable permission name, or FALSE if the provided text format
 -  *   is malformed or is the fallback format (which is available to all users).
 -  */
 - function filter_permission_name($format) {
 -   if (isset($format->format) && $format->format != filter_fallback_format()) {
 -     return 'use text format ' . $format->format;
 -   }
 -   return FALSE;
 - }
 - 
 -  * Implements hook_library_info().
 -  */
 - function filter_library_info() {
 -   $module_path = backdrop_get_path('module', 'filter');
 -   $libraries['filter'] = array(
 -     'version' => BACKDROP_VERSION,
 -     'js' => array(
 -       $module_path . '/js/filter.js' => array(),
 -     ),
 -     'css' => array(
 -       $module_path . '/css/filter.css' => array(),
 -     ),
 -     'dependencies' => array(
 -       array('system', 'backdrop.ajax'),
 -     ),
 -   );
 -   
 -   $libraries['filter.admin'] = array(
 -     'version' => BACKDROP_VERSION,
 -     'js' => array(
 -       $module_path . '/js/filter.admin.js' => array('group' => JS_THEME, 'aggregate' => FALSE,),
 -     ),
 -     'css' => array(
 -       $module_path . '/css/filter.admin.css' => array('group' => CSS_THEME, 'aggregate' => FALSE),
 -     ),
 -   );
 -   $libraries['filter.filtered_html.admin'] = array(
 -     'version' => BACKDROP_VERSION,
 -     'js' => array(
 -       $module_path . '/js/filter.filtered_html.admin.js' => array('group' => JS_THEME, 'aggregate' => FALSE,),
 -     ),
 -     'dependencies' => array(
 -       array('filter', 'filter.admin'),
 -     ),
 -   );
 -   return $libraries;
 - }
 - 
 -  * Implements hook_modules_enabled().
 -  */
 - function filter_modules_enabled($modules) {
 -   
 -   
 -   backdrop_static_reset('filter_get_filters');
 - }
 - 
 -  * Implements hook_modules_disabled().
 -  */
 - function filter_modules_disabled($modules) {
 -   
 -   
 -   backdrop_static_reset('filter_get_filters');
 - }
 - 
 -  * Retrieves a list of text formats, ordered by weight.
 -  *
 -  * @param User $account
 -  *   (optional) If provided, only those formats that are allowed for this user
 -  *   account will be returned. All formats will be returned otherwise. Defaults
 -  *   to NULL.
 -  * @param Boolean $include_disabled
 -  *   (optional) If TRUE, will return all formats, including disabled formats.
 -  *
 -  * @return stdClass[]
 -  *   An array of text format objects, keyed by the format ID and ordered by
 -  *   weight.
 -  *
 -  * @see filter_formats_reset()
 -  */
 - function filter_formats($account = NULL, $include_disabled = FALSE) {
 -   global $language;
 -   $formats = &backdrop_static(__FUNCTION__, array());
 - 
 -   
 -   if (!isset($formats['all'])) {
 -     if ($cache = cache()->get("filter_formats:{$language->langcode}")) {
 -       $formats['all'] = $cache->data;
 -     }
 -     else {
 -       $formats['all'] = array();
 - 
 -       $all_filter_info = filter_get_filters();
 -       $all_editor_info = filter_get_editors();
 -       $config_names = config_get_names_with_prefix('filter.format.');
 -       $filter_formats = config_load_multiple($config_names);
 -       backdrop_sort($filter_formats, array('weight' => SORT_NUMERIC, 'name' => SORT_STRING));
 -       foreach ($filter_formats as $format_name => $filter_format) {
 -         $filter_format += array(
 -           'status' => 1,
 -           'weight' => 0,
 -           'editor' => NULL,
 -           'editor_settings' => array(),
 -         );
 -         $filter_format = (object) $filter_format;
 - 
 -         
 -         backdrop_sort($filter_format->filters);
 - 
 -         
 -         foreach ($filter_format->filters as $filter_name => $filter) {
 -           $filter += array(
 -             'name' => $filter_name,
 -             'status' => 1,
 -             'weight' => 0,
 -             'settings' => array(),
 -           );
 -           if (isset($all_filter_info[$filter_name]['default settings'])) {
 -             $filter['settings'] += $all_filter_info[$filter_name]['default settings'];
 -           }
 -           $filter_format->filters[$filter_name] = (object) $filter;
 -         }
 -         
 -         if (isset($all_editor_info[$filter_format->editor]['default settings'])) {
 -           $filter_format->editor_settings += $all_editor_info[$filter_format->editor]['default settings'];
 -         }
 - 
 -         $formats['all'][$filter_format->format] = $filter_format;
 -       }
 - 
 -       cache()->set("filter_formats:{$language->langcode}", $formats['all']);
 -     }
 -   }
 - 
 -   
 -   if (!isset($formats['enabled'])) {
 -     foreach ($formats['all'] as $format) {
 -       if ($format->status) {
 -         $formats['enabled'][$format->format] = $format;
 -       }
 -     }
 -   }
 - 
 -   
 -   if (isset($account) && !isset($formats['user'][$account->uid])) {
 -     $formats['user'][$account->uid] = array();
 -     foreach ($formats['all'] as $format) {
 -       if (filter_access($format, $account)) {
 -         
 -         $formats['user'][$account->uid]['all'][$format->format] = $format;
 -         
 -         if ($format->status) {
 -           $formats['user'][$account->uid]['enabled'][$format->format] = $format;
 -         }
 -       }
 -     }
 -   }
 - 
 -   $enabled_or_all = $include_disabled ? 'all' : 'enabled';
 -   if (isset($account)) {
 -     return $formats['user'][$account->uid][$enabled_or_all];
 -   }
 -   else {
 -     return $formats[$enabled_or_all];
 -   }
 - }
 - 
 -  * Resets the text format caches.
 -  *
 -  * @see filter_formats()
 -  */
 - function filter_formats_reset() {
 -   cache()->deletePrefix('filter_formats');
 -   backdrop_static_reset('filter_formats');
 - }
 - 
 -  * Returns a list of text editors that are used with 'text_format' elements.
 -  */
 - function filter_get_editors() {
 -   $editors = &backdrop_static(__FUNCTION__, NULL);
 - 
 -   if (!isset($editors)) {
 -     $editors = array();
 -     $modules = module_implements('editor_info');
 -     foreach ($modules as $module) {
 -       $module_editors = module_invoke($module, 'editor_info');
 -       foreach ($module_editors as $editor_name => $editor) {
 -         $editor['module'] = $module;
 -         
 -         $editor += array(
 -           'settings callback' => NULL,
 -           'default settings' => array(),
 -           'file' => NULL,
 -         );
 -         $editors[$editor_name] = $editor;
 -       }
 -     }
 -     backdrop_alter('editor_info', $editors);
 -   }
 - 
 -   return $editors;
 - }
 - 
 -  * Loads an individual editor's information.
 -  */
 - function filter_editor_load($editor_name) {
 -   $editors = filter_get_editors();
 -   return isset($editors[$editor_name]) ? $editors[$editor_name] : FALSE;
 - }
 - 
 -  * Generate a URL token for checking access to an editor dialog.
 -  *
 -  * Note that we do not use backdrop_get_token() because it requires an active
 -  * session.
 -  *
 -  * @param stdClass $format
 -  *   An object representing the text format.
 -  * @param string $dialog_name
 -  *   The type of dialog that will be opened.
 -  * @param User $account
 -  *   The user account that will be opening the dialog.
 -  * @param string $path
 -  *   The path from which the dialog will be opened. Defaults to the current
 -  *   path.
 -  *
 -  * @return string
 -  *   A token intended to be used in a query string.
 -  */
 - function filter_editor_dialog_token($format, $dialog_name, $account = NULL, $path = NULL) {
 -   if (!isset($account)) {
 -     $account = $GLOBALS['user'];
 -   }
 -   if (!isset($path)) {
 -     $path = $_GET['q'];
 -   }
 -   $values = array(
 -     $path,
 -     $format->format,
 -     $dialog_name,
 -     $account->uid,
 -   );
 -   return backdrop_hmac_base64(implode('-', $values), backdrop_get_private_key() . backdrop_get_hash_salt());
 - }
 - 
 -  * Retrieves a list of roles that are allowed to use a given text format.
 -  *
 -  * @param $format
 -  *   An object representing the text format.
 -  *
 -  * @return
 -  *   An unindexed array of role names.
 -  */
 - function filter_get_roles_by_format($format) {
 -   
 -   if ($format->format == filter_fallback_format()) {
 -     return array_keys(user_roles());
 -   }
 -   
 -   $permission = filter_permission_name($format);
 -   return !empty($permission) ? array_keys(user_roles(FALSE, $permission)) : array();
 - }
 - 
 -  * Retrieves a list of text formats that are allowed for a given role.
 -  *
 -  * @param string $role_name
 -  *   The user role name to retrieve text formats for.
 -  *
 -  * @return
 -  *   An array of text format objects that are allowed for the role, keyed by
 -  *   the text format ID and ordered by weight.
 -  */
 - function filter_get_formats_by_role($role_name) {
 -   $formats = array();
 -   foreach (filter_formats() as $format) {
 -     $roles = filter_get_roles_by_format($format);
 -     if (in_array($role_name, $roles)) {
 -       $formats[$format->format] = $format;
 -     }
 -   }
 -   return $formats;
 - }
 - 
 -  * Returns the ID of the default text format for a particular user.
 -  *
 -  * The default text format is the first available format that the user is
 -  * allowed to access, when the formats are ordered by weight. It should
 -  * generally be used as a default choice when presenting the user with a list
 -  * of possible text formats (for example, in a node creation form).
 -  *
 -  * Conversely, when existing content that does not have an assigned text format
 -  * needs to be filtered for display, the default text format is the wrong
 -  * choice, because it is not guaranteed to be consistent from user to user, and
 -  * some trusted users may have an unsafe text format set by default, which
 -  * should not be used on text of unknown origin. Instead, the fallback format
 -  * returned by filter_fallback_format() should be used, since that is intended
 -  * to be a safe, consistent format that is always available to all users.
 -  *
 -  * @param $account
 -  *   (optional) The user account to check. Defaults to the currently logged-in
 -  *   user. Defaults to NULL.
 -  *
 -  * @return
 -  *   The ID of the user's default text format.
 -  *
 -  * @see filter_fallback_format()
 -  */
 - function filter_default_format($account = NULL) {
 -   global $user;
 -   if (!isset($account)) {
 -     $account = $user;
 -   }
 -   
 -   
 -   $formats = filter_formats($account);
 -   $format = reset($formats);
 -   return $format->format;
 - }
 - 
 -  * Returns the ID of the fallback text format that all users have access to.
 -  *
 -  * The fallback text format is a regular text format in every respect, except
 -  * it does not participate in the filter permission system and cannot be
 -  * disabled. It needs to exist because any user who has permission to create
 -  * formatted content must always have at least one text format they can use.
 -  *
 -  * Because the fallback format is available to all users, it should always be
 -  * configured securely. For example, when the Filter module is installed, this
 -  * format is initialized to output plain text. Installation profiles and site
 -  * administrators have the freedom to configure it further.
 -  *
 -  * Note that the fallback format is completely distinct from the default format,
 -  * which differs per user and is the first format which that user has access to.
 -  * The default and fallback formats are only guaranteed to be the same for users
 -  * who do not have access to any other format; otherwise, the fallback format's
 -  * weight determines its placement with respect to the user's other formats.
 -  *
 -  * Any modules implementing a format deletion functionality must not delete this
 -  * format.
 -  *
 -  * @return
 -  *   The ID of the fallback text format.
 -  *
 -  * @see hook_filter_format_disable()
 -  * @see filter_default_format()
 -  */
 - function filter_fallback_format() {
 -   return config_get('system.core', 'filter_fallback_format');
 - }
 - 
 -  * Returns the title of the fallback text format.
 -  *
 -  * @return string
 -  *   The title of the fallback text format.
 -  */
 - function filter_fallback_format_title() {
 -   $fallback_format = filter_format_load(filter_fallback_format());
 -   return filter_admin_format_title($fallback_format);
 - }
 - 
 -  * Returns a list of all filters provided by modules.
 -  *
 -  * @return array
 -  *   An array of filter formats.
 -  */
 - function filter_get_filters() {
 -   $filters = &backdrop_static(__FUNCTION__, array());
 - 
 -   if (empty($filters)) {
 -     foreach (module_implements('filter_info') as $module) {
 -       $info = module_invoke($module, 'filter_info');
 -       if (isset($info) && is_array($info)) {
 -         
 -         
 -         foreach (array_keys($info) as $name) {
 -           $info[$name]['module'] = $module;
 -           $info[$name] += array(
 -             'description' => '',
 -             'weight' => 0,
 -             'default settings' => array(),
 -           );
 -         }
 -         $filters = array_merge($filters, $info);
 -       }
 -     }
 -     
 -     backdrop_alter('filter_info', $filters);
 -     backdrop_sort($filters, array('title' => SORT_STRING));
 -   }
 - 
 -   return $filters;
 - }
 - 
 -  * Checks if the text in a certain text format is allowed to be cached.
 -  *
 -  * This function can be used to check whether the result of the filtering
 -  * process can be cached. A text format may allow caching depending on the
 -  * filters enabled.
 -  *
 -  * @param $format_id
 -  *   The text format ID to check.
 -  *
 -  * @return
 -  *   TRUE if the given text format allows caching, FALSE otherwise.
 -  */
 - function filter_format_allowcache($format_id) {
 -   $format = filter_format_load($format_id);
 -   return !empty($format->cache);
 - }
 - 
 -  * Helper function to determine whether the output of a given text format can be cached.
 -  *
 -  * The output of a given text format can be cached when all enabled filters in
 -  * the text format allow caching.
 -  *
 -  * @param $format
 -  *   The text format object to check.
 -  *
 -  * @return
 -  *   TRUE if all the filters enabled in the given text format allow caching,
 -  *   FALSE otherwise.
 -  *
 -  * @see filter_format_save()
 -  */
 - function _filter_format_is_cacheable($format) {
 -   if (empty($format->filters)) {
 -     return TRUE;
 -   }
 -   $filter_info = filter_get_filters();
 -   foreach ($format->filters as $name => $filter) {
 -     
 -     if (!empty($filter->status) && isset($filter_info[$name]['cache']) && !$filter_info[$name]['cache']) {
 -       return FALSE;
 -     }
 -   }
 -   return TRUE;
 - }
 - 
 -  * Retrieves a list of filters for a given text format.
 -  *
 -  * This function is deprecated in Backdrop. All text format object already have
 -  * all filters loaded in the "filters" property of the object.
 -  *
 -  * @param $format_id
 -  *   The format ID to retrieve filters for.
 -  *
 -  * @return
 -  *   An array of filter objects associated to the given text format, keyed by
 -  *   filter name.
 -  *
 -  * @deprecated since 1.0
 -  */
 - function filter_list_format($format_id) {
 -   watchdog_deprecated_function('Filter', __FUNCTION__);
 -   if ($format = filter_format_load($format_id)) {
 -     return $format->filters;
 -   }
 -   else {
 -     return array();
 -   }
 - }
 - 
 -  * Runs all the enabled filters on a piece of text.
 -  *
 -  * Note: Because filters can inject JavaScript or execute PHP code, security is
 -  * vital here. When a user supplies a text format, you should validate it using
 -  * filter_access() before accepting/using it. This is normally done in the
 -  * validation stage of the Form API. You should for example never make a preview
 -  * of content in a disallowed format.
 -  *
 -  * @param $text
 -  *   The text to be filtered.
 -  * @param $format_id
 -  *   (optional) The machine name of the filter format to be used to filter the
 -  *   text. Defaults to the fallback format. See filter_fallback_format().
 -  * @param $langcode
 -  *   (optional) The language code of the text to be filtered, e.g. 'en' for
 -  *   English. This allows filters to be language aware so language specific
 -  *   text replacement can be implemented. Defaults to an empty string.
 -  * @param $cache
 -  *   (optional) A Boolean indicating whether to cache the filtered output in the
 -  *   {cache_filter} table. The caller may set this to FALSE when the output is
 -  *   already cached elsewhere to avoid duplicate cache lookups and storage.
 -  *   Defaults to FALSE.
 -  *
 -  * @return
 -  *   The filtered text.
 -  *
 -  * @ingroup sanitization
 -  */
 - function check_markup($text, $format_id = NULL, $langcode = '', $cache = FALSE) {
 -   if (!isset($format_id)) {
 -     $format_id = filter_fallback_format();
 -   }
 -   
 -   if (!$format = filter_format_load($format_id)) {
 -     watchdog('filter', 'Missing text format: %format.', array('%format' => $format_id), WATCHDOG_ALERT);
 -     return '';
 -   }
 - 
 -   
 -   $cache = $cache && !empty($format->cache);
 -   $cache_id = '';
 -   if ($cache) {
 -     $cache_id = $format->format . ':' . $langcode . ':' . hash('sha256', $text);
 -     if ($cached = cache('filter')->get($cache_id)) {
 -       return $cached->data;
 -     }
 -   }
 - 
 -   
 -   
 -   $text = str_replace(array("\r\n", "\r"), "\n", (string) $text);
 - 
 -   
 -   $filters = $format->filters;
 -   $filter_info = filter_get_filters();
 - 
 -   
 -   foreach ($filters as $name => $filter) {
 -     if ($filter->status && isset($filter_info[$name]['prepare callback'])) {
 -       $function = $filter_info[$name]['prepare callback'];
 -       $text = $function($text, $filter, $format, $langcode, $cache, $cache_id);
 -     }
 -   }
 - 
 -   
 -   foreach ($filters as $name => $filter) {
 -     if ($filter->status && isset($filter_info[$name]['process callback'])) {
 -       $function = $filter_info[$name]['process callback'];
 -       $text = $function($text, $filter, $format, $langcode, $cache, $cache_id);
 -     }
 -   }
 - 
 -   
 -   
 -   
 -   
 -   if ($cache) {
 -     cache('filter')->set($cache_id, $text);
 -   }
 - 
 -   return $text;
 - }
 - 
 -  * Expands an element into a base element with text format selector attached.
 -  *
 -  * The form element will be expanded into two separate form elements, one
 -  * holding the original element, and the other holding the text format selector:
 -  * - value: Holds the original element, having its #type changed to the value of
 -  *   #base_type or 'textarea' by default.
 -  * - format: Holds the text format fieldset and the text format selection, using
 -  *   the text format id specified in #format or the user's default format by
 -  *   default, if NULL.
 -  *
 -  * The resulting value for the element will be an array holding the value and
 -  * the format. For example, the value for the body element will be:
 -  * @code
 -  *   $form_state['values']['body']['value'] = 'foo';
 -  *   $form_state['values']['body']['format'] = 'foo';
 -  * @endcode
 -  *
 -  * @param $element
 -  *   The form element to process. Properties used:
 -  *   - #base_type: The form element #type to use for the 'value' element.
 -  *     'textarea' by default.
 -  *   - #format: (optional) The text format name to preselect. If NULL or not
 -  *     set, the default format for the current user will be used.
 -  *   - #allowed_formats: (optional) An array of format names that should be
 -  *     available options. If none are specified, all available formats for the
 -  *     current user are displayed.
 -  *
 -  * @return array
 -  *   The expanded element.
 -  */
 - function filter_process_format($element) {
 -   global $user;
 - 
 -   
 -   $element['#tree'] = TRUE;
 -   $denylist = array(
 -     
 -     '#parents',
 -     '#id',
 -     '#name',
 -     
 -     
 -     '#process',
 -     
 -     '#weight',
 -     
 -     '#prefix',
 -     '#suffix',
 -     '#attached',
 -     '#processed',
 -     '#theme_wrappers',
 -   );
 - 
 -   
 -   
 -   
 -   
 -   
 -   if ($element['#description_display'] == 'after') {
 -     $denylist[] = '#description';
 -   }
 - 
 -   
 -   unset($element['value']);
 -   foreach (element_properties($element) as $key) {
 -     if (!in_array($key, $denylist)) {
 -       $element['value'][$key] = $element[$key];
 -     }
 -   }
 - 
 -   $element['value']['#type'] = $element['#base_type'];
 -   $element['value'] += element_info($element['#base_type']);
 - 
 -   
 -   $formats = filter_formats($user);
 -   $fallback_format = filter_fallback_format();
 - 
 -   
 -   
 -   if (isset($element['#attached'])) {
 -     $element['#attached'] = array_merge_recursive($element['#attached'], filter_get_attached($formats));
 -   }
 -   else {
 -     $element['#attached'] = filter_get_attached($formats);
 -   }
 - 
 -   
 -   if (!empty($element['#allowed_formats'])) {
 -     foreach ($formats as $format) {
 -       if ($format->format !== $fallback_format &&
 -         !in_array($format->format, $element['#allowed_formats'])) {
 -         unset($formats[$format->format]);
 -       }
 -     }
 -   }
 - 
 -   
 -   $element['value']['#attributes']['data-editor-uploads'] = empty($element['#editor_uploads']) ? 'false' : 'true';
 - 
 -   
 -   if (!isset($element['#format'])) {
 -     $element['#format'] = filter_default_format($user);
 -   }
 - 
 -   
 -   if (($element['#format'] != $fallback_format) && (count($formats) > 1) && array_key_exists($fallback_format, $formats)) {
 -     unset($formats[$fallback_format]);
 -   }
 - 
 -   
 -   $element['format'] = array(
 -     '#type' => 'fieldset',
 -     '#attributes' => array('class' => array('filter-wrapper')),
 -     '#title' => t('Formatting options'),
 -     '#collapsible' => TRUE,
 -     '#collapsed' => TRUE,
 -   );
 - 
 -   
 -   $element['format']['guidelines'] = array(
 -     '#type' => 'container',
 -     '#attributes' => array('class' => array('filter-guidelines')),
 -     '#weight' => 20,
 -   );
 -   $options = array();
 -   foreach ($formats as $format) {
 -     $options[$format->format] = check_plain($format->name);
 -     $element['format']['guidelines'][$format->format] = array(
 -       '#theme' => 'filter_guidelines',
 -       '#format' => $format,
 -     );
 -   }
 - 
 -   
 -   
 -   if (count($options) > 1 || !array_key_exists($element['#format'], $options)) {
 -     $element['format']['format'] = array(
 -       '#type' => 'select',
 -       '#title' => t('Editor'),
 -       '#options' => $options,
 -       '#default_value' => $element['#format'],
 -       '#weight' => 10,
 -       '#attributes' => array('class' => array('filter-list')),
 -       '#parents' => array_merge($element['#parents'], array('format')),
 -     );
 -   }
 -   
 -   
 -   else {
 -     $element['format']['format'] = array(
 -       '#type' => 'hidden',
 -       '#value' => $element['#format'],
 -       '#default_value' => $element['#format'],
 -       '#attributes' => array(
 -         'class' => array('filter-list'),
 -         
 -         
 -         'data-text-format-name' => check_plain($options[$element['#format']]),
 -       ),
 -       '#parents' => array_merge($element['#parents'], array('format')),
 -     );
 -   }
 - 
 -   $all_formats = filter_formats();
 -   $format_exists = isset($all_formats[$element['#format']]);
 -   $user_has_access = isset($formats[$element['#format']]);
 -   $user_is_admin = user_access('administer filters');
 - 
 -   
 -   
 -   if (!$format_exists && $user_is_admin) {
 -     $element['format']['format']['#required'] = TRUE;
 -     $element['format']['format']['#default_value'] = NULL;
 -     
 -     
 -     $element['format']['format']['#access'] = TRUE;
 -   }
 -   
 -   
 -   
 -   
 -   elseif (!$user_has_access || !$format_exists) {
 -     if (!empty($element['#default_value'])) {
 -       
 -       $element['value']['#value'] = $element['value']['#default_value'];
 -       $element['format']['format']['#value'] = $element['format']['format']['#default_value'];
 - 
 -       
 -       
 -       $element['value'] += array('#pre_render' => array());
 -       array_unshift($element['value']['#pre_render'], 'filter_form_access_denied');
 - 
 -       
 -       if (isset($element['value']['#rows'])) {
 -         $element['value']['#rows'] = 3;
 -       }
 -       $element['value']['#disabled'] = TRUE;
 -       $element['value']['#resizable'] = 'none';
 - 
 -       
 -       
 -       foreach (element_children($element) as $key) {
 -         if ($key != 'value') {
 -           $element[$key]['#access'] = FALSE;
 -         }
 -       }
 -     }
 -   }
 - 
 -   return $element;
 - }
 - 
 -  * Adds filter configuration information to the page for access by JavaScript.
 -  *
 -  * @param array $formats
 -  *   An array of formats as returned by filter_formats(), whose settings should
 -  *   be added to the page.
 -  * @return array
 -  *   An array of attached libraries, CSS, and JS that can be set to an element's
 -  *   #attached property.
 -  */
 - function filter_get_attached($formats) {
 -   $attached = array();
 -   $attached['library'][] = array('filter', 'filter');
 - 
 -   foreach ($formats as $format) {
 -     
 -     if ($format->editor && ($editor = filter_editor_load($format->editor)) && isset($editor['library'])) {
 -       $attached['library'][] = $editor['library'];
 -     }
 -   }
 - 
 -   if (!empty($formats)) {
 -     $settings = filter_get_js_settings($formats);
 -     $attached['js'][] = array(
 -       'type' => 'setting',
 -       'key' => 'filter_formats',
 -       'data' => array('filter' => array('formats' => $settings)),
 -     );
 -   }
 - 
 -   return $attached;
 - }
 - 
 -  * Retrieve JavaScript settings that should be added by each filter.
 -  *
 -  * @param array $formats
 -  *   An array of formats as returned by filter_formats().
 -  *
 -  * @return array
 -  *   An array of JavaScript settings representing the configuration of the
 -  *   filters.
 -  */
 - function filter_get_js_settings($formats) {
 -   $settings = array();
 -   $filter_info = filter_get_filters();
 -   $editor_info = filter_get_editors();
 - 
 -   foreach ($formats as $format_name => $format) {
 -     
 -     if (!$format->editor) {
 -       continue;
 -     }
 - 
 -     $filter_settings = array();
 -     foreach ($format->filters as $filter_name => $filter) {
 -       if ($filter->status && isset($filter_info[$filter_name]['js settings callback'])) {
 -         $function = $filter_info[$filter_name]['js settings callback'];
 -         $filter_settings += $function($filter, $format);
 -       }
 -     }
 -     $settings[$format_name] = array(
 -       'filterSettings' => $filter_settings,
 -       'editor' => $format->editor,
 -       'editorSettings' => array(),
 -     );
 - 
 -     if ($format->editor && isset($editor_info[$format->editor]['js settings callback'])) {
 -       $function = $editor_info[$format->editor]['js settings callback'];
 -       $settings[$format_name]['editorSettings'] = $function($format, $settings);
 -     }
 -   }
 - 
 -   backdrop_alter('filter_js_settings', $settings, $formats);
 - 
 -   return $settings;
 - }
 - 
 -  * Render API callback: Hides the field value of 'text_format' elements.
 -  *
 -  * To not break form processing and previews if a user does not have access to a
 -  * stored text format, the expanded form elements in filter_process_format() are
 -  * forced to take over the stored #default_values for 'value' and 'format'.
 -  * However, to prevent the unfiltered, original #value from being displayed to
 -  * the user, we replace it with a friendly notice here.
 -  *
 -  * @see filter_process_format()
 -  */
 - function filter_form_access_denied($element) {
 -   $element['#value'] = t('This field has been disabled because you do not have sufficient permissions to edit it.');
 -   return $element;
 - }
 - 
 -  * Checks if a user has access to a particular text format.
 -  *
 -  * @param $format
 -  *   An object representing the text format.
 -  * @param $account
 -  *   (optional) The user account to check access for; if omitted, the currently
 -  *   logged-in user is used. Defaults to NULL.
 -  *
 -  * @return
 -  *   Boolean TRUE if the user is allowed to access the given format.
 -  */
 - function filter_access($format, $account = NULL) {
 -   global $user;
 -   if (!isset($account)) {
 -     $account = $user;
 -   }
 -   
 -   
 -   if ($format->format == filter_fallback_format()) {
 -     return TRUE;
 -   }
 -   
 -   
 -   $permission = filter_permission_name($format);
 -   return !empty($permission) && user_access($permission, $account);
 - }
 - 
 -  * Retrieves the filter tips.
 -  *
 -  * @param $format_id
 -  *   The ID of the text format for which to retrieve tips, or -1 to return tips
 -  *   for all formats accessible to the current user.
 -  * @param $long
 -  *   (optional) Boolean indicating whether the long form of tips should be
 -  *   returned. Defaults to FALSE.
 -  *
 -  * @return
 -  *   An associative array of filtering tips, keyed by filter name. Each
 -  *   filtering tip is an associative array with elements:
 -  *   - tip: Tip text.
 -  *   - id: Filter ID.
 -  */
 - function _filter_tips($format_id, $long = FALSE) {
 -   global $user;
 - 
 -   $formats = filter_formats($user);
 -   $filter_info = filter_get_filters();
 - 
 -   $tips = array();
 - 
 -   
 -   if ($format_id != -1) {
 -     $formats = array($formats[$format_id]);
 -   }
 - 
 -   foreach ($formats as $format) {
 -     $tips[$format->name] = array();
 -     foreach ($format->filters as $name => $filter) {
 -       if ($filter->status && isset($filter_info[$name]['tips callback'])) {
 -         $tip = $filter_info[$name]['tips callback']($filter, $format, $long);
 -         if (isset($tip)) {
 -           $tips[$format->name][$name] = array('tip' => $tip, 'id' => $name);
 -         }
 -       }
 -     }
 -   }
 - 
 -   return $tips;
 - }
 - 
 -  * Parses an HTML snippet and returns it as a DOM object.
 -  *
 -  * This function loads the body part of a partial (X)HTML document and returns
 -  * a full DOMDocument object that represents this document. You can use
 -  * filter_dom_serialize() to serialize this DOMDocument back to a XHTML
 -  * snippet.
 -  *
 -  * @param $text
 -  *   The partial (X)HTML snippet to load. Invalid mark-up will be corrected on
 -  *   import.
 -  * @return DOMDocument
 -  *   A DOMDocument that represents the loaded (X)HTML snippet.
 -  */
 - function filter_dom_load($text) {
 -   $dom_document = new DOMDocument();
 -   
 -   @$dom_document->loadHTML('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head><body>' . $text . '</body></html>');
 - 
 -   return $dom_document;
 - }
 - 
 -  * Converts a DOM object back to an HTML snippet.
 -  *
 -  * The function serializes the body part of a DOMDocument back to an XHTML
 -  * snippet. The resulting XHTML snippet will be properly formatted to be
 -  * compatible with HTML user agents.
 -  *
 -  * @param $dom_document
 -  *   A DOMDocument object to serialize, only the tags below
 -  *   the first <body> node will be converted.
 -  *
 -  * @return
 -  *   A valid (X)HTML snippet, as a string.
 -  */
 - function filter_dom_serialize($dom_document) {
 -   $body_node = $dom_document->getElementsByTagName('body')->item(0);
 -   $body_content = '';
 - 
 -   if ($body_node !== NULL) {
 -     foreach ($body_node->getElementsByTagName('script') as $node) {
 -       filter_dom_serialize_escape_cdata_element($dom_document, $node);
 -     }
 - 
 -     foreach ($body_node->getElementsByTagName('style') as $node) {
 -       filter_dom_serialize_escape_cdata_element($dom_document, $node, '/*', '*/');
 -     }
 - 
 -     foreach ($body_node->childNodes as $child_node) {
 -       $body_content .= $dom_document->saveXML($child_node);
 -     }
 -     return preg_replace('|<([^> ]*)/>|i', '<$1 />', $body_content);
 -   }
 -   else {
 -     return $body_content;
 -   }
 - }
 - 
 -  * Adds comments around the <!CDATA section in a dom element.
 -  *
 -  * DOMDocument::loadHTML in filter_dom_load() makes CDATA sections from the
 -  * contents of inline script and style tags.  This can cause HTML 4 browsers to
 -  * throw exceptions.
 -  *
 -  * This function attempts to solve the problem by creating a DocumentFragment
 -  * and imitating the behavior in backdrop_get_js(), commenting the CDATA tag.
 -  *
 -  * @param $dom_document
 -  *   The DOMDocument containing the $dom_element.
 -  * @param $dom_element
 -  *   The element potentially containing a CDATA node.
 -  * @param $comment_start
 -  *   (optional) A string to use as a comment start marker to escape the CDATA
 -  *   declaration. Defaults to '//'.
 -  * @param $comment_end
 -  *   (optional) A string to use as a comment end marker to escape the CDATA
 -  *   declaration. Defaults to an empty string.
 -  */
 - function filter_dom_serialize_escape_cdata_element($dom_document, $dom_element, $comment_start = '//', $comment_end = '') {
 -   foreach ($dom_element->childNodes as $node) {
 -     if (get_class($node) == 'DOMCdataSection') {
 -       
 -       $embed_prefix = "\n<!--{$comment_start}--><![CDATA[{$comment_start} ><!--{$comment_end}\n";
 -       $embed_suffix = "\n{$comment_start}--><!]]>{$comment_end}\n";
 - 
 -       
 -       
 -       
 -       
 -       $data = str_replace(']]>', ']]]]><![CDATA[>', $node->data);
 - 
 -       $fragment = $dom_document->createDocumentFragment();
 -       $fragment->appendXML($embed_prefix . $data . $embed_suffix);
 -       $dom_element->appendChild($fragment);
 -       $dom_element->removeChild($node);
 -     }
 -   }
 - }
 - 
 -  * Implements hook_entity_insert().
 -  */
 - function filter_entity_insert(EntityInterface $entity) {
 -   $referenced_files_by_field = _filter_get_file_ids_by_field($entity);
 -   foreach ($referenced_files_by_field as $field => $fids) {
 -     _filter_record_file_usage($fids, $entity);
 -   }
 - }
 - 
 -  * Implements hook_entity_update().
 -  */
 - function filter_entity_update(EntityInterface $entity) {
 -   
 -   
 -   $entity_info = entity_get_info($entity->entityType());
 -   $vid_key = isset($entity_info['entity keys']['revision']) ? $entity_info['entity keys']['revision'] : NULL;
 -   if (!empty($entity->original)) {
 -     if ($vid_key && $entity->$vid_key != $entity->original->$vid_key) {
 -       $referenced_files_by_field = _filter_get_file_ids_by_field($entity);
 -       foreach ($referenced_files_by_field as $field => $fids) {
 -         _filter_record_file_usage($fids, $entity);
 -       }
 -     }
 - 
 -     
 -     
 -     
 -     
 -     else {
 -       $original_fids_by_field = _filter_get_file_ids_by_field($entity->original);
 -       $fids_by_field = _filter_get_file_ids_by_field($entity);
 - 
 -       
 -       foreach ($fids_by_field as $field => $fids) {
 -         $added_files = array_diff($fids_by_field[$field], $original_fids_by_field[$field]);
 -         _filter_record_file_usage($added_files, $entity);
 -       }
 - 
 -       
 -       foreach ($original_fids_by_field as $field => $fids) {
 -         $removed_files = array_diff($original_fids_by_field[$field], $fids_by_field[$field]);
 -         _filter_delete_file_usage($removed_files, $entity, 1);
 -       }
 -     }
 -   }
 - }
 - 
 -  * Implements hook_entity_delete().
 -  */
 - function filter_entity_delete(EntityInterface $entity) {
 -   $referenced_files_by_field = _filter_get_file_ids_by_field($entity);
 -   foreach ($referenced_files_by_field as $field => $fids) {
 -     _filter_delete_file_usage($fids, $entity, 0);
 -   }
 - }
 - 
 -  * Implements hook_node_revision_delete().
 -  *
 -  * @todo Ideally this would be hook_entity_revision_delete(), but entities do
 -  * not have full revision support at this point.
 -  */
 - function filter_node_revision_delete(Node $node) {
 -   $referenced_files_by_field = _filter_get_file_ids_by_field($node);
 -   foreach ($referenced_files_by_field as $field => $fids) {
 -     _filter_delete_file_usage($fids, $node, 1);
 -   }
 - }
 - 
 -  * Records file usage of files referenced by processed text fields.
 -  *
 -  * Every referenced file that does not yet have the FILE_STATUS_PERMANENT state,
 -  * will be given that state.
 -  *
 -  * @param array $fids
 -  *   An array of file entity IDs.
 -  * @param EntityInterface $entity
 -  *   An entity whose fields to inspect for file references.
 -  */
 - function _filter_record_file_usage(array $fids, EntityInterface $entity) {
 -   foreach ($fids as $fid) {
 -     $file = file_load($fid);
 -     if ($file) {
 -       if ($file->status != FILE_STATUS_PERMANENT) {
 -         $file->status = FILE_STATUS_PERMANENT;
 -         $file->save();
 -       }
 -       file_usage_add($file, 'filter', $entity->entityType(), $entity->id());
 -     }
 -   }
 - }
 - 
 -  * Deletes file usage of files referenced by processed text fields.
 -  *
 -  * @param array $fids
 -  *   An array of file entity IDs.
 -  * @param EntityInterface $entity
 -  *   An entity whose fields to inspect for file references.
 -  * @param $count
 -  *   The number of references to delete. Should be 1 when deleting a single
 -  *   revision and 0 when deleting an entity entirely.
 -  */
 - function _filter_delete_file_usage(array $fids, EntityInterface $entity, $count) {
 -   foreach ($fids as $fid) {
 -     if ($file = file_load($fid)) {
 -       file_usage_delete($file, 'filter', $entity->entityType(), $entity->id(), $count);
 -     }
 -   }
 - }
 - 
 -  * Finds all files referenced (data-file-id) by processed text fields.
 -  *
 -  * @param EntityInterface $entity
 -  *   An entity whose fields to analyze.
 -  *
 -  * @return array
 -  *   An array of file entity FIDs.
 -  */
 - function _filter_get_file_ids_by_field(EntityInterface $entity) {
 -   $fids = array();
 - 
 -   $processed_text_fields = _filter_get_processed_text_fields($entity);
 -   foreach ($processed_text_fields as $processed_text_field) {
 -     $fids[$processed_text_field] = array();
 -     if (isset($entity->$processed_text_field)) {
 -       foreach ($entity->$processed_text_field as $langcode => $values) {
 -         foreach ($values as $delta => $text) {
 -           if (isset($text['value'])) {
 -             $fids[$processed_text_field] = array_merge($fids[$processed_text_field], filter_parse_file_fids($text['value']));
 -           }
 -           if (isset($text['summary'])) {
 -             $fids[$processed_text_field] = array_merge($fids[$processed_text_field], filter_parse_file_fids($text['summary']));
 -           }
 -         }
 -       }
 -     }
 -     $fids[$processed_text_field] = array_unique($fids[$processed_text_field]);
 -   }
 -   return $fids;
 - }
 - 
 -  * Determines the text fields on an entity that have text processing enabled.
 -  *
 -  * @param EntityInterface $entity
 -  *   An entity whose fields to analyze.
 -  *
 -  * @return array
 -  *   The names of the fields on this entity that have text processing enabled.
 -  */
 - function _filter_get_processed_text_fields(EntityInterface $entity) {
 -   $fields = field_info_instances($entity->entityType(), $entity->bundle());
 -   $field_list = array();
 -   foreach ($fields as $field) {
 -     if (!empty($field['settings']['text_processing'])) {
 -       $field_list[] = $field['field_name'];
 -     }
 -   }
 -   return $field_list;
 - }
 - 
 -  * Parse an HTML snippet for any data-file-id attributes.
 -  *
 -  * @param string $text
 -  *   The partial (X)HTML snippet to load. Invalid markup will be corrected on
 -  *   import.
 -  *
 -  * @return array
 -  *   An array of all found FIDs.
 -  */
 - function filter_parse_file_fids($text) {
 -   $dom = filter_dom_load($text);
 -   $xpath = new DOMXPath($dom);
 -   $fids = array();
 -   foreach ($xpath->query('//*[@data-file-id]') as $node) {
 -     $fids[] = $node->getAttribute('data-file-id');
 -   }
 -   return $fids;
 - }
 - 
 - 
 -  * Get a format currently being edited from the tempstore.
 -  *
 -  * @param string $name
 -  *   The machine name of the format item.
 -  */
 - function filter_get_format_tempstore($name) {
 -   $caches = &backdrop_static(__FUNCTION__, array());
 -   if (!isset($caches[$name])) {
 -     
 -     $item = tempstore_get('filter_store', $name);
 -     $caches[$name] = $item;
 -   }
 - 
 -   return $caches[$name];
 - }
 - 
 -  * Store changes to a format in the temporary store.
 -  *
 -  * @param $item
 -  *   The format item to save into tempstore.
 -  */
 - function filter_set_format_tempstore($item) {
 -   if (empty($item->format)) {
 -     return;
 -   }
 - 
 -   tempstore_set('filter_store', $item->format, $item, 604800);
 - }
 - 
 -  * Remove an item from the object cache.
 -  */
 - function filter_clear_format_tempstore($name) {
 -   tempstore_clear('filter_store', $name);
 - }
 - 
 -  * @defgroup standard_filters Standard filters
 -  * @{
 -  * Filters implemented by the Filter module.
 -  */
 - 
 -  * Implements hook_filter_info().
 -  */
 - function filter_filter_info() {
 -   $filters['filter_html'] = array(
 -     'title' => t('Limit allowed HTML tags'),
 -     'process callback' => '_filter_html',
 -     'settings callback' => '_filter_html_settings',
 -     'allowed html callback' => '_filter_html_allowed_html',
 -     'default settings' => array(
 -       'allowed_html' => '<a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd> <h3> <h4> <h5> <p> <br> <img> <figure> <figcaption>',
 -       'filter_html_help' => 1,
 -       'filter_html_nofollow' => 0,
 -     ),
 -     'tips callback' => '_filter_html_tips',
 -     'weight' => -10,
 -   );
 -   $filters['filter_autop'] = array(
 -     'title' => t('Convert line breaks into HTML (i.e. <code><br></code> and <code><p></code>)'),
 -     'process callback' => '_filter_autop',
 -     'tips callback' => '_filter_autop_tips',
 -   );
 -   $filters['filter_url'] = array(
 -     'title' => t('Convert URLs into links'),
 -     'process callback' => '_filter_url',
 -     'settings callback' => '_filter_url_settings',
 -     'js settings callback' => '_filter_url_js_settings',
 -     'default settings' => array(
 -       'filter_url_length' => 72,
 -     ),
 -     'tips callback' => '_filter_url_tips',
 -   );
 -   $filters['filter_htmlcorrector'] = array(
 -     'title' =>  t('Correct faulty and chopped off HTML'),
 -     'process callback' => '_filter_htmlcorrector',
 -     'weight' => 10,
 -   );
 -   $filters['filter_html_escape'] = array(
 -     'title' => t('Display any HTML as plain text'),
 -     'process callback' => '_filter_html_escape',
 -     'tips callback' => '_filter_html_escape_tips',
 -     'weight' => -10,
 -   );
 -   $filters['filter_image_caption'] = array(
 -     'title' => t('Convert image captions to figure and figcaption elements'),
 -     'process callback' => '_filter_image_caption',
 -     'weight' => 4,
 -   );
 -   $filters['filter_image_align'] = array(
 -     'title' => t('Float images left and right using the data-align attribute'),
 -     'process callback' => '_filter_image_align',
 -     'weight' => 5,
 -   );
 -   return $filters;
 - }
 - 
 -  * Implements callback_filter_settings().
 -  *
 -  * Filter settings callback for the HTML content filter.
 -  */
 - function _filter_html_settings($form, &$form_state, $filter, $format) {
 -   $settings['allowed_html'] = array(
 -     '#type' => 'textfield',
 -     '#title' => t('Allowed HTML tags'),
 -     '#default_value' => $filter->settings['allowed_html'],
 -     '#maxlength' => 1024,
 -     '#description' => t('A list of HTML tags that can be used. JavaScript event attributes, JavaScript URLs, and CSS are always stripped.'),
 -   );
 -   $settings['filter_html_help'] = array(
 -     '#type' => 'checkbox',
 -     '#title' => t('Display basic HTML help in long filter tips'),
 -     '#default_value' => $filter->settings['filter_html_help'],
 -   );
 -   $settings['filter_html_nofollow'] = array(
 -     '#type' => 'checkbox',
 -     '#title' => t('Add rel="nofollow" to all links'),
 -     '#default_value' => $filter->settings['filter_html_nofollow'],
 -   );
 -   return $settings;
 - }
 - 
 -  * Implements callback_filter_allowed_html().
 -  */
 - function _filter_html_allowed_html($filter, $format) {
 -   
 -   $restrictions = array('allowed' => array());
 -   $tags = preg_split('/\s+|<|>/', $filter->settings['allowed_html'], -1, PREG_SPLIT_NO_EMPTY);
 -   
 -   foreach ($tags as $tag) {
 -     $restrictions['allowed'][$tag] = TRUE;
 -   }
 -   
 -   $restrictions['allowed']['*'] = array('style' => FALSE, 'on*' => FALSE);
 -   return $restrictions;
 - }
 - 
 -  * Implements callback_filter_process().
 -  *
 -  * Provides filtering of input into accepted HTML.
 -  */
 - function _filter_html($text, $filter) {
 -   $allowed_tags = preg_split('/\s+|<|>/', $filter->settings['allowed_html'], -1, PREG_SPLIT_NO_EMPTY);
 -   $text = filter_xss($text, $allowed_tags);
 - 
 -   if ($filter->settings['filter_html_nofollow']) {
 -     $html_dom = filter_dom_load($text);
 -     $links = $html_dom->getElementsByTagName('a');
 -     foreach ($links as $link) {
 -       $link->setAttribute('rel', 'nofollow');
 -     }
 -     $text = filter_dom_serialize($html_dom);
 -   }
 - 
 -   return trim($text);
 - }
 - 
 -  * Implements callback_filter_tips().
 -  *
 -  * Provides help for the HTML filter.
 -  *
 -  * @see filter_filter_info()
 -  */
 - function _filter_html_tips($filter, $format, $long = FALSE) {
 -   global $base_url;
 - 
 -   if (!($allowed_html = $filter->settings['allowed_html'])) {
 -     return;
 -   }
 - 
 -   
 -   if ($format->editor == 'ckeditor') {
 -     return;
 -   }
 - 
 -   $output = t('Allowed HTML tags: @tags', array('@tags' => $allowed_html));
 -   if (!$long) {
 -     return $output;
 -   }
 - 
 -   $output = '<p>' . $output . '</p>';
 -   if (!$filter->settings['filter_html_help']) {
 -     return $output;
 -   }
 - 
 -   $output .= '<p>' . t('This site allows HTML content. While learning all of HTML may feel intimidating, learning how to use a very small number of the most basic HTML "tags" is very easy. This table provides examples for each tag that is enabled on this site.') . '</p>';
 -   $output .= '<p>' . t('For more information see W3C\'s <a href="@html-specifications">HTML Specifications</a> or use your favorite search engine to find other sites that explain HTML.', array('@html-specifications' => 'http://www.w3.org/TR/html/')) . '</p>';
 -   $tips = array(
 -     'a' => array(t('Anchors are used to make links to other pages.'), '<a href="' . $base_url . '">' . check_plain(config_get('system.core', 'site_name')) . '</a>'),
 -     'br' => array(t('By default line break tags are automatically added, so use this tag to add additional ones. Use of this tag is different because it is not used with an open/close pair like all the others. Use the extra " /" inside the tag to maintain XHTML 1.0 compatibility'), t('Text with <br />line break')),
 -     'p' => array(t('By default paragraph tags are automatically added, so use this tag to add additional ones.'), '<p>' . t('Paragraph one.') . '</p> <p>' . t('Paragraph two.') . '</p>'),
 -     'strong' => array(t('Strong', array(), array('context' => 'Font weight')), '<strong>' . t('Strong', array(), array('context' => 'Font weight')) . '</strong>'),
 -     'em' => array(t('Emphasized'), '<em>' . t('Emphasized') . '</em>'),
 -     'cite' => array(t('Cited'), '<cite>' . t('Cited') . '</cite>'),
 -     'code' => array(t('Coded text used to show programming source code'), '<code>' . t('Coded') . '</code>'),
 -     'b' => array(t('Bolded'), '<b>' . t('Bolded') . '</b>'),
 -     'u' => array(t('Underlined'), '<u>' . t('Underlined') . '</u>'),
 -     'i' => array(t('Italicized'), '<i>' . t('Italicized') . '</i>'),
 -     'sup' => array(t('Superscripted'), t('<sup>Super</sup>scripted')),
 -     'sub' => array(t('Subscripted'), t('<sub>Sub</sub>scripted')),
 -     'pre' => array(t('Preformatted'), '<pre>' . t('Preformatted') . '</pre>'),
 -     'abbr' => array(t('Abbreviation'), t('<abbr title="Abbreviation">Abbrev.</abbr>')),
 -     'acronym' => array(t('Acronym'), t('<acronym title="Three-Letter Acronym">TLA</acronym>')),
 -     'blockquote' => array(t('Block quoted'), '<blockquote>' . t('Block quoted') . '</blockquote>'),
 -     'q' => array(t('Quoted inline'), '<q>' . t('Quoted inline') . '</q>'),
 -     
 -     'table' => array(t('Table'), '<table> <tr><th>' . t('Table header') . '</th></tr> <tr><td>' . t('Table cell') . '</td></tr> </table>'),
 -     'tr' => NULL, 'td' => NULL, 'th' => NULL,
 -     'del' => array(t('Deleted'), '<del>' . t('Deleted') . '</del>'),
 -     'ins' => array(t('Inserted'), '<ins>' . t('Inserted') . '</ins>'),
 -      
 -     'ol' => array(t('Ordered list - use the <li> to begin each list item'), '<ol> <li>' . t('First item') . '</li> <li>' . t('Second item') . '</li> </ol>'),
 -     'ul' => array(t('Unordered list - use the <li> to begin each list item'), '<ul> <li>' . t('First item') . '</li> <li>' . t('Second item') . '</li> </ul>'),
 -     'li' => NULL,
 -     
 -     'dl' => array(t('Definition lists are similar to other HTML lists. <dl> begins the definition list, <dt> begins the definition term and <dd> begins the definition description.'), '<dl> <dt>' . t('First term') . '</dt> <dd>' . t('First definition') . '</dd> <dt>' . t('Second term') . '</dt> <dd>' . t('Second definition') . '</dd> </dl>'),
 -     'dt' => NULL, 'dd' => NULL,
 -     'h1' => array(t('Heading'), '<h1>' . t('Title') . '</h1>'),
 -     'h2' => array(t('Heading'), '<h2>' . t('Subtitle') . '</h2>'),
 -     'h3' => array(t('Heading'), '<h3>' . t('Subtitle three') . '</h3>'),
 -     'h4' => array(t('Heading'), '<h4>' . t('Subtitle four') . '</h4>'),
 -     'h5' => array(t('Heading'), '<h5>' . t('Subtitle five') . '</h5>'),
 -     'h6' => array(t('Heading'), '<h6>' . t('Subtitle six') . '</h6>')
 -   );
 -   $header = array(t('Tag Description'), t('You Type'), t('You Get'));
 -   preg_match_all('/<([a-z0-9]+)[^a-z0-9]/i', $allowed_html, $out);
 -   foreach ($out[1] as $tag) {
 -     if (!empty($tips[$tag])) {
 -       $rows[] = array(
 -         array('data' => $tips[$tag][0], 'class' => array('description')),
 -         array('data' => '<code>' . check_plain($tips[$tag][1]) . '</code>', 'class' => array('type')),
 -         array('data' => $tips[$tag][1], 'class' => array('get'))
 -       );
 -     }
 -     else {
 -       $rows[] = array(
 -         array('data' => t('No help provided for tag %tag.', array('%tag' => $tag)), 'class' => array('description'), 'colspan' => 3),
 -       );
 -     }
 -   }
 -   $output .= theme('table', array('header' => $header, 'rows' => $rows));
 - 
 -   $output .= '<p>' . t('Most unusual characters can be directly entered without any problems.') . '</p>';
 -   $output .= '<p>' . t('If you do encounter problems, try using HTML character entities. A common example looks like &amp; for an ampersand & character. For a full list of entities see HTML\'s <a href="@html-entities">entities</a> page. Some of the available characters include:', array('@html-entities' => 'http://www.w3.org/TR/html4/sgml/entities.html')) . '</p>';
 - 
 -   $entities = array(
 -     array(t('Ampersand'), '&'),
 -     array(t('Greater than'), '>'),
 -     array(t('Less than'), '<'),
 -     array(t('Quotation mark'), '"'),
 -   );
 -   $header = array(t('Character Description'), t('You Type'), t('You Get'));
 -   unset($rows);
 -   foreach ($entities as $entity) {
 -     $rows[] = array(
 -       array('data' => $entity[0], 'class' => array('description')),
 -       array('data' => '<code>' . check_plain($entity[1]) . '</code>', 'class' => array('type')),
 -       array('data' => $entity[1], 'class' => array('get'))
 -     );
 -   }
 -   $output .= theme('table', array('header' => $header, 'rows' => $rows));
 -   return $output;
 - }
 - 
 -  * Implements callback_filter_settings().
 -  *
 -  * Provides settings for the URL filter.
 -  *
 -  * @see filter_filter_info()
 -  */
 - function _filter_url_settings($form, &$form_state, $filter, $format) {
 -   $settings['filter_url_length'] = array(
 -     '#type' => 'number',
 -     '#title' => t('Maximum link text length'),
 -     '#default_value' => $filter->settings['filter_url_length'],
 -     '#max' => 100000,
 -     '#min' => 1,
 -     '#field_suffix' => t('characters'),
 -     '#description' => t('URLs longer than this number of characters will be truncated to prevent long strings that break formatting. The link itself will be retained; just the text portion of the link will be truncated.'),
 -   );
 -   return $settings;
 - }
 - 
 -  * Implements callback_filter_js_settings().
 -  *
 -  * Filter URL JS settings callback: return settings for JavaScript.
 -  */
 - function _filter_url_js_settings($filter, $format) {
 -   return array(
 -     'filterUrlLength' => $filter->settings['filter_url_length'],
 -   );
 - }
 - 
 -  * Implements callback_filter_process().
 -  *
 -  * Converts text into hyperlinks automatically.
 -  *
 -  * This filter identifies and makes clickable three types of "links".
 -  * - URLs like http://example.com.
 -  * - email addresses like name@example.com.
 -  * - Web addresses without the "http://" protocol defined, like www.example.com.
 -  * Each type must be processed separately, as there is no one regular
 -  * expression that could possibly match all of the cases in one pass.
 -  */
 - function _filter_url($text, $filter) {
 -   
 -   $ignore_tags = 'a|script|style|code|pre';
 -   
 -   $ignore_classes = array('nolink');
 - 
 -   
 -   _filter_url_trim(NULL, $filter->settings['filter_url_length']);
 - 
 -   
 -   
 -   
 -   
 -   
 -   $tasks = array();
 - 
 -   
 -   
 -   
 -   
 -   
 -   
 -   $protocols = settings_get('filter_allowed_protocols', array('ftp', 'http', 'https', 'irc', 'mailto', 'news', 'nntp', 'rtsp', 'sftp', 'ssh', 'tel', 'telnet', 'webcal'));
 -   $protocols = implode(':(?://)?|', $protocols) . ':(?://)?';
 - 
 -   
 -   
 -   
 -   
 -   $domain = '(?:[A-Za-z0-9._+-]+\.)?[A-Za-z]{2,64}\b';
 - 
 -   
 -   
 -   $email_domain = '(?:[\p{L}\p{M}\p{N}._+-]+\.)+[\p{L}\p{M}]{2,64}\b';
 - 
 -   $ip = '(?:[0-9]{1,3}\.){3}[0-9]{1,3}';
 -   $auth = '[a-zA-Z0-9:%_+*~#?&=.,/;-]+@';
 -   $trail = '[a-zA-Z0-9:%_+*~#&\[\]=/;?!\.,-]*[a-zA-Z0-9:%_+*~#&\[\]=/;-]';
 - 
 -   
 -   
 -   
 -   
 -   $punctuation = '[\.,?!]*?';
 - 
 -   
 -   $url_pattern = "(?:$auth)?(?:$domain|$ip)/?(?:$trail)?";
 -   $pattern = "`((?:$protocols)(?:$url_pattern))($punctuation)`";
 -   $tasks['_filter_url_parse_full_links'] = $pattern;
 - 
 -   
 -   $url_pattern = "[\p{L}\p{M}\p{N}._+-]{1,254}@(?:$email_domain)";
 -   $pattern = "`($url_pattern)`";
 -   $tasks['_filter_url_parse_email_links'] = $pattern;
 - 
 -   
 -   $url_pattern = "www\.(?:$domain)/?(?:$trail)?";
 -   $pattern = "`($url_pattern)($punctuation)`";
 -   $tasks['_filter_url_parse_partial_links'] = $pattern;
 - 
 -   
 -   
 -   
 -   foreach ($tasks as $task => $pattern) {
 -     
 -     
 -     
 -     _filter_url_escape_comments('', TRUE);
 -     $text = preg_replace_callback('`<!--(.*?)-->`s', '_filter_url_escape_comments', $text);
 - 
 -     
 -     $chunks = preg_split('/(<.+?>)/is', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
 -     
 -     
 -     
 -     $chunk_type = 'text';
 -     
 -     
 -     
 -     $open_tag = '';
 - 
 -     for ($i = 0; $i < count($chunks); $i++) {
 -       if ($chunk_type == 'text') {
 -         
 -         if ($open_tag == '') {
 -           
 -           
 -           $chunks[$i] = preg_replace_callback($pattern, $task, $chunks[$i]);
 -         }
 -         
 -         $chunk_type = 'tag';
 -       }
 -       else {
 -         
 -         if ($open_tag == '') {
 -           
 -           if (preg_match("`<($ignore_tags)(?:\s|>)`i", $chunks[$i], $matches)) {
 -             $open_tag = $matches[1];
 -           }
 -           
 -           elseif (preg_match('`<([a-z0-9-]+)\s+[\s\S]*?class=(["\'])([\s\S]*?)\2[\s\S]*?>`i', $chunks[$i], $matches)) {
 -             if (!empty($matches[3])) {
 -               $classes = array_map('trim', explode(' ', $matches[3]));
 -               if (array_intersect($ignore_classes, $classes)) {
 -                 $open_tag = $matches[1];
 -               }
 -             }
 -           }
 -         }
 -         
 -         else {
 -           if (preg_match("`<\/$open_tag>`i", $chunks[$i], $matches)) {
 -             $open_tag = '';
 -           }
 -         }
 -         
 -         $chunk_type = 'text';
 -       }
 -     }
 - 
 -     $text = implode($chunks);
 -     
 -     _filter_url_escape_comments('', FALSE);
 -     $text = preg_replace_callback('`<!--(.*?)-->`', '_filter_url_escape_comments', $text);
 -   }
 - 
 -   return $text;
 - }
 - 
 -  * Makes links out of absolute URLs.
 -  *
 -  * Callback for preg_replace_callback() within _filter_url().
 -  */
 - function _filter_url_parse_full_links($match) {
 -   
 -   $i = 1;
 - 
 -   $match[$i] = decode_entities($match[$i]);
 -   $caption = check_plain(_filter_url_trim($match[$i]));
 -   $match[$i] = check_plain($match[$i]);
 -   return '<a href="' . $match[$i] . '">' . $caption . '</a>' . $match[$i + 1];
 - }
 - 
 -  * Makes links out of email addresses.
 -  *
 -  * Callback for preg_replace_callback() within _filter_url().
 -  */
 - function _filter_url_parse_email_links($match) {
 -   
 -   $i = 0;
 - 
 -   $match[$i] = decode_entities($match[$i]);
 -   $caption = check_plain(_filter_url_trim($match[$i]));
 -   $match[$i] = check_plain($match[$i]);
 -   return '<a href="mailto:' . $match[$i] . '">' . $caption . '</a>';
 - }
 - 
 -  * Makes links out of domain names starting with "www."
 -  *
 -  * Callback for preg_replace_callback() within _filter_url().
 -  */
 - function _filter_url_parse_partial_links($match) {
 -   
 -   $i = 1;
 - 
 -   $match[$i] = decode_entities($match[$i]);
 -   $caption = check_plain(_filter_url_trim($match[$i]));
 -   $match[$i] = check_plain($match[$i]);
 -   return '<a href="http://' . $match[$i] . '">' . $caption . '</a>' . $match[$i + 1];
 - }
 - 
 -  * Escapes the contents of HTML comments.
 -  *
 -  * Callback for preg_replace_callback() within _filter_url().
 -  *
 -  * @param $match
 -  *   An array containing matches to replace from preg_replace_callback(),
 -  *   whereas $match[1] is expected to contain the content to be filtered.
 -  * @param $escape
 -  *   (optional) A Boolean indicating whether to escape (TRUE) or unescape
 -  *   comments (FALSE). Defaults to NULL, indicating neither. If TRUE, statically
 -  *   cached $comments are reset.
 -  */
 - function _filter_url_escape_comments($match, $escape = NULL) {
 -   static $mode, $comments = array();
 - 
 -   if (isset($escape)) {
 -     $mode = $escape;
 -     if ($escape){
 -       $comments = array();
 -     }
 -     return;
 -   }
 - 
 -   
 -   if ($mode) {
 -     $content = $match[1];
 -     $hash = hash('sha256', $content);
 -     $comments[$hash] = $content;
 -     return "<!-- $hash -->";
 -   }
 -   
 -   else {
 -     $hash = $match[1];
 -     $hash = trim($hash);
 -     $content = $comments[$hash];
 -     return "<!--$content-->";
 -   }
 - }
 - 
 -  * Shortens long URLs to http://www.example.com/long/url...
 -  */
 - function _filter_url_trim($text, $length = NULL) {
 -   static $_length;
 -   if ($length !== NULL) {
 -     $_length = $length;
 -   }
 - 
 -   
 -   if ($_length && strlen((string) $text) > $_length + 3) {
 -     $text = substr($text, 0, $_length) . '...';
 -   }
 - 
 -   return $text;
 - }
 - 
 -  * Implements callback_filter_tips().
 -  *
 -  * Provides help for the URL filter.
 -  *
 -  * @see filter_filter_info()
 -  */
 - function _filter_url_tips($filter, $format, $long = FALSE) {
 -   return t("Web page addresses and email addresses turn into links automatically, unless the parent tag has the 'nolink' class.");
 - }
 - 
 -  * Implements callback_filter_process().
 -  *
 -  * Scans the input and makes sure that HTML tags are properly closed.
 -  */
 - function _filter_htmlcorrector($text) {
 -   return filter_dom_serialize(filter_dom_load($text));
 - }
 - 
 -  * Implements callback_filter_process().
 -  *
 -  * Converts line breaks into <p> and <br> in an intelligent fashion.
 -  *
 -  * Based on: http://photomatt.net/scripts/autop
 -  */
 - function _filter_autop($text) {
 -   
 -   $block = '(?:table|thead|tfoot|caption|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|select|form|blockquote|address|p|h[1-6]|hr|article|aside|details|figcaption|figure|footer|header|hgroup|menu|nav|section|summary)';
 - 
 -   
 -   
 -   
 -   
 -   
 -   $chunks = preg_split('@(<!--.*?-->|</?(?:pre|script|style|object|iframe|!--)[^>]*>)@i', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
 -   
 -   
 -   $ignore = FALSE;
 -   $ignore_tag = '';
 -   $output = '';
 -   foreach ($chunks as $i => $chunk) {
 -     if ($i % 2) {
 -       $comment = (substr($chunk, 0, 4) == '<!--');
 -       if ($comment) {
 -         
 -         $output .= $chunk;
 -         continue;
 -       }
 -       
 -       $open = ($chunk[1] != '/');
 -       list($tag) = preg_split('/[ >]/', substr($chunk, 2 - $open), 2);
 -       if (!$ignore) {
 -         if ($open) {
 -           $ignore = TRUE;
 -           $ignore_tag = $tag;
 -         }
 -       }
 -       
 -       elseif (!$open && $ignore_tag == $tag) {
 -         $ignore = FALSE;
 -         $ignore_tag = '';
 -       }
 -     }
 -     elseif (!$ignore) {
 -       $chunk = preg_replace('|<br>|', '<br />', $chunk); 
 -       $chunk = preg_replace('|\n*$|', '', $chunk) . "\n\n"; 
 -       $chunk = preg_replace('|<br />\s*<br />|', "\n\n", $chunk);
 -       $chunk = preg_replace('!(<' . $block . '[^>]*>)!', "\n$1", $chunk); 
 -       $chunk = preg_replace('!(</' . $block . '>)!', "$1\n\n", $chunk); 
 -       $chunk = preg_replace("/\n\n+/", "\n\n", $chunk); 
 -       $chunk = preg_replace('/^\n|\n\s*\n$/', '', $chunk);
 -       $chunk = '<p>' . preg_replace('/\n\s*\n\n?(.)/', "</p>\n<p>$1", $chunk) . "</p>\n"; 
 -       $chunk = preg_replace("|<p>(<li.+?)</p>|", "$1", $chunk); 
 -       $chunk = preg_replace('|<p><blockquote([^>]*)>|i', "<blockquote$1><p>", $chunk);
 -       $chunk = str_replace('</blockquote></p>', '</p></blockquote>', $chunk);
 -       $chunk = preg_replace('|<p>\s*</p>\n?|', '', $chunk); 
 -       $chunk = preg_replace('!<p>\s*(</?' . $block . '[^>]*>)!', "$1", $chunk);
 -       $chunk = preg_replace('!(</?' . $block . '[^>]*>)\s*</p>!', "$1", $chunk);
 -       $chunk = preg_replace('|(?<!<br />)\s*\n|', "<br />\n", $chunk); 
 -       $chunk = preg_replace('!(</?' . $block . '[^>]*>)\s*<br />!', "$1", $chunk);
 -       $chunk = preg_replace('!<br />(\s*</?(?:p|li|div|th|pre|td|ul|ol|h[1-6])>)!', '$1', $chunk); 
 -       $chunk = preg_replace('/&([^#])(?![A-Za-z0-9]{1,8};)/', '&$1', $chunk);
 -     }
 -     $output .= $chunk;
 -   }
 -   return $output;
 - }
 - 
 -  * Implements callback_filter_tips().
 -  *
 -  * Provides help for the auto-paragraph filter.
 -  *
 -  * @see filter_filter_info()
 -  */
 - function _filter_autop_tips($filter, $format, $long = FALSE) {
 -   
 -   if ($format->editor == 'ckeditor') {
 -     return;
 -   }
 - 
 -   if ($long) {
 -     return t('Lines and paragraphs are automatically recognized. The <br /> line break, <p> paragraph and </p> close paragraph tags are inserted automatically. If paragraphs are not recognized, add a couple blank lines.');
 -   }
 -   else {
 -     return t('Lines and paragraphs break automatically.');
 -   }
 - }
 - 
 -  * Implements callback_filter_process().
 -  *
 -  * Escapes all HTML tags, so they will be visible instead of being effective.
 -  */
 - function _filter_html_escape($text) {
 -   return trim(check_plain($text));
 - }
 - 
 -  * Implements callback_filter_tips().
 -  *
 -  * Provides help for the HTML escaping filter.
 -  *
 -  * @see filter_filter_info()
 -  */
 - function _filter_html_escape_tips($filter, $format, $long = FALSE) {
 -   return t('No HTML tags allowed.');
 - }
 - 
 -  * Implements callback_filter_process().
 -  *
 -  * Replace img data-caption attributes with figure and figcaption elements.
 -  */
 - function _filter_image_caption($text) {
 -   
 -   if (stristr($text, 'data-caption') === FALSE) {
 -     return $text;
 -   }
 - 
 -   
 -   $dom = filter_dom_load($text);
 -   $xpath = new DOMXPath($dom);
 - 
 -   foreach ($xpath->query('//*[@data-caption]') as $node) {
 -     
 -     $caption = $node->getAttribute('data-caption');
 -     $node->removeAttribute('data-caption');
 - 
 -     
 -     
 -     $caption = filter_xss($caption, array('a', 'em', 'strong', 'cite', 'code', 'br'));
 - 
 -     
 -     if (backdrop_strlen($caption) === 0) {
 -       continue;
 -     }
 - 
 -     
 -     
 -     
 -     $attributes = array();
 -     $tag = $node->tagName;
 -     $classes = $node->getAttribute('class');
 -     $node->removeAttribute('class');
 -     $node = ($node->parentNode->tagName === 'a') ? $node->parentNode : $node;
 -     if ($classes) {
 -       $attributes['class'] = explode(' ', $classes);
 -     }
 -     $attributes['class'][] = 'caption';
 -     $attributes['class'][] = 'caption-' . $node->tagName;
 - 
 -     $theme_parameters = array(
 -       'item' => $node->ownerDocument->saveXML($node),
 -       'tag' => $tag,
 -       'caption' => $caption,
 -       'attributes' => $attributes,
 -     );
 - 
 -     
 -     
 -     if (Database::isActiveConnection()) {
 -       $filter_caption = theme('filter_caption', $theme_parameters);
 -     }
 -     else {
 -       module_load_include('inc', 'filter', 'filter.theme');
 -       $filter_caption = theme_filter_caption($theme_parameters);
 -     }
 - 
 -     
 -     $updated_node = filter_dom_load($filter_caption)
 -       ->getElementsByTagName('body')
 -       ->item(0)
 -       ->firstChild;
 - 
 -     
 -     
 -     $updated_node = $dom->importNode($updated_node, TRUE);
 - 
 -     
 -     $node->parentNode->replaceChild($updated_node, $node);
 -   }
 - 
 -   return filter_dom_serialize($dom);
 - }
 - 
 -  * Implements callback_filter_process().
 -  *
 -  * Replaces img data-align attributes with figure and figcaption elements.
 -  */
 - function _filter_image_align($text) {
 -   if (stristr($text, 'data-align') === FALSE) {
 -     return $text;
 -   }
 - 
 -   
 -   $dom = filter_dom_load($text);
 -   $xpath = new DOMXPath($dom);
 - 
 -   foreach ($xpath->query('//*[@data-align]') as $node) {
 -     
 -     $align = $node->getAttribute('data-align');
 -     $node->removeAttribute('data-align');
 - 
 -     
 -     if (in_array($align, array('left', 'center', 'right'))) {
 -       
 -       $parent = $node->parentNode;
 -       if ($parent && $parent->nodeName === 'figure') {
 -         $target = $parent;
 -       }
 -       
 -       
 -       elseif ($parent && $parent->nodeName === 'a' && $parent->parentNode && $parent->parentNode->nodeName === 'figure') {
 -         $target = $parent->parentNode;
 -       }
 -       else {
 -         $target = $node;
 -       }
 -       $classes = $target->getAttribute('class');
 -       $classes = (strlen($classes) > 0) ? explode(' ', $classes) : array();
 -       $classes[] = 'align-' . $align;
 -       $target->setAttribute('class', implode(' ', $classes));
 - 
 -       
 -       
 -       if ($align === 'center') {
 -         $wrapper_div = $dom->createElement('div');
 -         $wrapper_attribute = $dom->createAttribute('class');
 -         $wrapper_attribute->value = 'centered-wrapper';
 -         $wrapper_div->appendChild($wrapper_attribute);
 -         $wrapper_div->appendChild($target->cloneNode(TRUE));
 -         $target->parentNode->replaceChild($wrapper_div, $target);
 -       }
 -     }
 -   }
 - 
 -   return filter_dom_serialize($dom);
 - }
 - 
 -  * @} End of "Standard filters".
 -  */