You are here

xbbcode_list.module in Extensible BBCode 8

File

xbbcode_list/xbbcode_list.module
View source
<?php

function xbbcode_list_xbbcode_info() {
  $sample = t("[*] fruit\n[**] apples\n[**] bananas\n[*] animals\n[**] cat\n[**] dog");
  $tags['list'] = array(
    'callback' => '_xbbcode_list_render',
    'description' => t('Formats a list of items in the default style.'),
    'sample' => '[list]' . $sample . '[/list]',
  );
  $tags['ol'] = array(
    'callback' => '_xbbcode_list_render',
    'description' => t('Formats a numbered list of items.'),
    'sample' => '[ol]' . $sample . '[/ol]',
  );
  $tags['ul'] = array(
    'callback' => '_xbbcode_list_render',
    'description' => t('Formats a non-numbered list of items.'),
    'sample' => '[ul]' . $sample . '[/ul]',
  );
  return $tags;
}
function xbbcode_list_init() {
  drupal_add_css(drupal_get_path('module', 'xbbcode_list') . '/xbbcode_list.css');
}
function _xbbcode_list_render($tag) {
  $settings = variable_get('xbbcode_list', array(
    'type' => 'ol',
    'ol' => array(
      'style' => 'hierarchy',
      'classes' => array(
        'numeric',
        'lower-alpha',
        'lower-roman',
      ),
    ),
  ));
  $type = $tag->name == 'list' ? $settings['type'] : $tag->name;
  $classes = $type == 'ol' ? $settings['ol']['style'] == 'hierarchy' ? $settings['ol']['classes'] : array(
    'sectioned',
  ) : array(
    'default',
  );
  $tree = _xbbcode_list_tree($tag->content);
  return _xbbcode_list_render_tree($type, $classes, $tree);
}
function _xbbcode_list_tree($text) {
  $text = str_replace("\n", '', $text);
  $tokens = preg_split('/\\s*\\[(\\*+)\\]\\s*/', trim($text), -1, PREG_SPLIT_DELIM_CAPTURE);
  array_shift($tokens);
  $root = (object) array(
    'sub' => array(),
    'data' => NULL,
  );
  $stack = array(
    $root,
  );
  if ($tokens[0] != '*') {
    return NULL;
  }
  for ($i = 0; $i < count($tokens); $i += 2) {
    if (strlen($tokens[$i] > count($stack) + 1)) {
      return NULL;
    }
    elseif (strlen($tokens[$i]) == count($stack) + 1) {
      array_push($stack, end(end($stack)->sub));
    }
    else {
      while (strlen($tokens[$i]) < count($stack)) {
        array_pop($stack);
      }
    }
    end($stack)->sub[] = (object) array(
      'sub' => array(),
      'data' => $tokens[$i + 1],
    );
  }
  return $root;
}
function _xbbcode_list_render_tree($type, $classes, $tree) {
  $class = count($classes) > 1 ? array_shift($classes) : end($classes);
  $out = $tree->data;
  if ($tree->sub) {
    $out .= "<{$type} class='{$class}'>";
    foreach ($tree->sub as $item) {
      $out .= '<li>' . _xbbcode_list_render_tree($type, $classes, $item) . '</li>';
    }
    $out .= "</{$type}>";
  }
  return $out;
}
function xbbcode_list_menu() {
  $menu['admin/config/content/lists/autocomplete'] = array(
    'title' => 'list-style-type autocomplete',
    'page callback' => 'xbbcode_list_autocomplete',
    'access arguments' => array(
      'administer site configuration',
    ),
    'type' => MENU_CALLBACK,
  );
  $menu['admin/config/content/lists'] = array(
    'title' => 'BBCode list styles',
    'description' => 'Configure the way that BBCode-formatted lists are displayed.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'xbbcode_list_admin_settings',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'type' => MENU_NORMAL_ITEM,
  );
  return $menu;
}
function xbbcode_list_admin_settings($form_state) {
  drupal_add_js(drupal_get_path('module', 'xbbcode_list') . '/xbbcode_list.js');
  $settings = variable_get('xbbcode_list', array(
    'type' => 'ol',
    'ol' => array(
      'style' => 'hierarchy',
      'classes' => array(
        'numeric',
        'lower-alpha',
        'lower-roman',
      ),
    ),
  ));
  $form['#tree'] = TRUE;
  $form['type'] = array(
    '#type' => 'radios',
    '#title' => t('Default list type'),
    '#description' => t('Choose whether [list] is numbered or non-numbered by default. Users can override the default by using [ul] or [ol].'),
    '#options' => array(
      'ol' => t('Numbered'),
      'ul' => t('Non-numbered'),
    ),
    '#default_value' => $settings['type'],
  );
  $form['ol'] = array(
    '#type' => 'fieldset',
    '#title' => t('Ordered list settings'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
    '#description' => t('Configure how to display lists with numbered items.'),
  );
  $form['ul'] = array(
    '#type' => 'fieldset',
    '#title' => t('Unordered list settings'),
    '#collapsible' => FALSE,
    '#collapsed' => FALSE,
    '#description' => t('Configure how to display lists with non-numbered items.'),
  );
  $form['ol']['style'] = array(
    '#type' => 'radios',
    '#title' => t('Ordered list default'),
    '#description' => t('Ordered lists will look like this by default. The style can be overridden with [ol=sections] and [ol=levels]'),
    '#options' => array(
      'sections' => t('Sectioned (this works only in a browser with CSS 2.0 support!)'),
      'hierarchy' => t('Hierarchical levels'),
    ),
    '#default_value' => $settings['ol']['style'],
  );
  $form['ol']['example'] = array(
    '#markup' => '
<ol id="xbbocde_list_sample_1" class="numeric">
  <li>' . t('Fruit') . '
    <ol id="xbbocde_list_sample_2" class="lower-alpha">
      <li>' . t('Citrus') . '
        <ol id="xbbocde_list_sample_3" class="lower-roman">
          <li>' . t('Lemon') . '</li>
          <li>' . t('Orange') . '</li>
        </ol>
      </li>
    </ol>
  </li>
</ol>',
  );
  $form['ol']['classes'] = array(
    '#type' => 'textfield',
    '#title' => t('Style levels'),
    '#description' => t('Enter a comma-separated list of styles that will be used by nested lists. Valid styles are: %list. Deeper levels will repeat the lowest level that was defined.', array(
      '%list' => 'upper-roman, lower-roman, numeric, upper-alpha, lower-alpha, none',
    )),
    '#default_value' => implode(', ', $settings['ol']['classes']),
    '#autocomplete_path' => 'admin/config/content/lists/autocomplete',
    '#element_validate' => array(
      'xbbcode_list_style_validate',
    ),
  );
  return $form;
}
function xbbcode_list_style_validate($element) {
  $valid_styles = array(
    'upper-alpha',
    'lower-alpha',
    'upper-roman',
    'lower-roman',
    'numeric',
  );
  $types = preg_split('/ *, */', $element['#value']);
  foreach ($types as $type) {
    if (!in_array($type, $valid_styles)) {
      form_error($element, t('%type is not a valid style.', array(
        '%type' => $type,
      )));
    }
  }
}
function xbbcode_list_admin_settings_submit($form, &$form_state) {
  $data = $form_state['values'];
  $data['ol']['classes'] = preg_split('/ *, */', $element['#value']);
  variable_set('xbbcode_list', $data);
}
function xbbcode_list_autocomplete($string) {
  $styles = array(
    'upper-alpha',
    'lower-alpha',
    'upper-roman',
    'lower-roman',
    'numeric',
  );
  $results = array();
  if (preg_match('/^(.*,\\s*)?(.*)$/', check_plain($string), $match)) {
    list($first, $last) = array(
      $match[1],
      $match[2],
    );
    $length = drupal_strlen($last);
    foreach ($styles as $style) {
      if (drupal_substr($style, 0, $length) == $last) {
        $results[$first . $style] = $style;
      }
    }
  }
  drupal_json_output($results);
}