You are here

xbbcode_basic.module in Extensible BBCode 8.2

File

xbbcode_basic/xbbcode_basic.module
View source
<?php

use Drupal\Core\Url;
function xbbcode_basic_init() {
  drupal_add_css(drupal_get_path('module', 'xbbcode_basic') . '/xbbcode_basic.css');
}
function xbbcode_basic_xbbcode_info() {

  // Basic emphasis.
  $tags['i'] = [
    'markup' => '<em>{content}</em>',
    'description' => t('Italic text'),
    'sample' => t('[i]italic[/i]'),
  ];
  $tags['b'] = [
    'markup' => '<strong>{content}</strong>',
    'description' => t('Bold text'),
    'sample' => t('[b]bold[/b]'),
  ];
  $tags['u'] = [
    'markup' => '<span style="text-decoration:underline">{content}</span>',
    'description' => t('Underlined text'),
    'sample' => t('[u]underlined[/u]'),
  ];
  $tags['s'] = [
    'markup' => '<del>{content}</del>',
    'description' => t('Stricken-through text'),
    'sample' => t('[s]this sentence is false[/s]'),
  ];

  // Font style.
  $tags['font'] = [
    'markup' => '<span style="font-family:{option}">{content}</span>',
    'description' => t('Changes the font of the text.'),
    'sample' => t('[font=arial]Text[/font]'),
  ];
  $tags['size'] = [
    'markup' => '<span style="font-size:{option}">{content}</span>',
    'description' => t('Changes the text size. This requires the <em>unit</em> (pt, px, em) of the size.'),
    'sample' => t('[size=16pt]Text[/size]'),
  ];
  $tags['color'] = [
    'markup' => '<span style="color:{option}">{content}</span>',
    'description' => t('Changes the color. You may enter a color word (red) or a hex code <em>with hash sign</em> (#ff0)'),
    'sample' => t('[color=#f80]Orange text[/color]'),
  ];
  $tags['sup'] = [
    'markup' => '<sup>{content}</sup>',
    'sample' => 'x[sup]2[/sup]',
    'description' => t('Sets text to be set smaller and above the line.'),
  ];
  $tags['sub'] = [
    'markup' => '<sub>{content}</sub>',
    'sample' => 'a[sub]i,j[/sub]',
    'description' => t('Sets text to be smaller and below the line.'),
  ];

  // Alignment.
  $tags['left'] = [
    'markup' => '<object><p style="text-align:left">{content}</p></object>',
    'description' => t('Aligns text on the left side.'),
    'sample' => t('[left]Left-aligned text[/left]'),
  ];
  $tags['right'] = [
    'markup' => '<object><p style="text-align:right">{content}</p></object>',
    'description' => t('Aligns text on the right side.'),
    'sample' => t('[right]Right-aligned text[/right]'),
  ];
  $tags['center'] = [
    'markup' => '<object><p style="text-align:center">{content}</p></object>',
    'description' => t('Aligns text in the center.'),
    'sample' => t('[center]Centered text[/center]'),
  ];
  $tags['justify'] = [
    'markup' => '<object><p style="text-align:justify">{content}</p></object>',
    'description' => t('Aligns text as a justified block.'),
    'sample' => '[justify]Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.[/justify]',
  ];

  // Hyperlinks and resources.
  $tags['url'] = [
    'markup' => '<a href="{option}" title="{option}">{content}</a>',
    'description' => t('Formats a Hyperlink.'),
    'sample' => t('[url=http://drupal.org]Drupal.org[/url]'),
  ];
  $tags['img'] = [
    'callback' => 'xbbcode_basic_render_img',
    'sample' => t('[img=65x73]
@url
[/img]', [
      '@url' => Url::fromUri('base:core/themes/bartik/logo.png')
        ->toString(),
    ]),
    'description' => t('Displays a picture. The size may be set by entering widthxheight as the only option.'),
  ];
  $tags['node'] = [
    'callback' => 'xbbcode_basic_render_nodelink',
    'sample' => t('[node=1]Node #1[/node]'),
    'description' => t('Links to a certain node. Unlike a [url] tag, this is passed through the linking function and returns the alias of the node.'),
  ];
  $tags['wikipedia'] = [
    'markup' => '<a title="{content} on Wikipedia" href="http://www.wikipedia.org/wiki/{content}">{content}</a>',
    'sample' => t('[wikipedia]Drupal[/wikipedia]'),
    'description' => t('Formats a link to Wikipedia, the free encyclopedia.'),
  ];
  $tags['youtube'] = [
    'callback' => 'xbbcode_basic_render_youtube',
    'sample' => '[youtube=224x126]rF1X12PE6PY[/youtube]',
    'description' => t('Embed a Youtube video.'),
  ];

  // Section headings, dividers.
  $tags['h1'] = [
    'markup' => '<h1>{content}</h1>',
    'description' => t('Level 1 heading. <em>You may want to restrict access to the level 1 and level 2 headings for ordinary users.</em>'),
    'sample' => t('[h1]Title[/h1]'),
  ];
  $tags['h2'] = [
    'markup' => '<h2>{content}</h2>',
    'description' => t('Level 2 heading. Use this as the top level on non-node content, as level 1 is used by the site header.'),
    'sample' => t('[h2]Volume[/h2]'),
  ];
  $tags['h3'] = [
    'markup' => '<h3>{content}</h3>',
    'description' => t('Level 3 heading. Use this as the top level within nodes as levels 1 and 2 are used by the site and node title.'),
    'sample' => t('[h3]Chapter[/h3]'),
  ];
  $tags['h4'] = [
    'markup' => '<h4>{content}</h4>',
    'description' => t('Level 4 heading.'),
    'sample' => t('[h4]Section[/h4]'),
  ];
  $tags['h5'] = [
    'markup' => '<h5>{content}</h5>',
    'description' => t('Level 5 heading.'),
    'sample' => t('[h5]Sub-section[/h5]'),
  ];
  $tags['h6'] = [
    'markup' => '<h6>{content}</h6>',
    'description' => t('Level 6 heading.'),
    'sample' => t('[h6]Sub-sub-section[/h6]'),
  ];
  $tags['hr'] = [
    'markup' => '<hr />',
    'description' => t('Horizontal divider.'),
    'sample' => '[hr]',
    'options' => [
      'selfclosing' => TRUE,
    ],
  ];

  // Text objects.
  $tags['code'] = [
    'callback' => 'xbbcode_basic_render_code',
    'description' => t('Formats the content text as code, in Monospace and with a grey box around it. BBCode tags within this tag will <em>not</em> be parsed.'),
    'sample' => t("[code]def fib (n):\n  a, b = 0, 1\n  for i in xrange(n):\n    a, b = b, a + 1\n  return a[/code]"),
  ];
  $tags['php'] = [
    'callback' => 'xbbcode_basic_render_code',
    'description' => t('This colors the syntax of PHP code using the in-built PHP highlighting library.'),
    'sample' => t("[php]<?php\n echo 'Hello World' . 5 . \$variable;\n?>[/php]"),
  ];
  $tags['quote'] = [
    'markup' => '<blockquote class="xbbcode">{content}</blockquote>',
    'description' => t('Formats a quote.'),
    'sample' => t('[quote]This text is quoted.[/quote]'),
  ];
  $tags['list'] = [
    'callback' => 'xbbcode_basic_render_list',
    'description' => t('Formats a list. The CSS list style type or "n" may be set as an option.'),
    'sample' => t("[list=lower-roman]\n  [*]Apples\n  [*]Oranges\n  [*]Bananas\n  [/list]"),
  ];
  $tags['define'] = [
    'callback' => 'xbbcode_basic_render_define',
    'description' => t("Formats a definition list."),
    'sample' => t("[define]\n  --verbose:This will start the program with full debug messages.\n  --in=IN:This will set the input file to IN.\n  --out=OUT:This will write output to OUT.\n[/define]"),
  ];
  $tags['table'] = [
    'callback' => 'xbbcode_basic_render_table',
    'description' => t('Create a table from comma-separated data. The tag option contains an optional caption and column headers; a prefix of ! or # aligns the column to the center or the right, respectively.'),
    'sample' => t("[table=Caption;!Item,Color,#Amount]\nFish,Red,1\nFish,Blue,2\n[/table]"),
  ];

  // Semantic meaning.
  $tags['acronym'] = [
    'markup' => '<acronym title="{option}">{content}</acronym>',
    'sample' => t('[acronym=PHP: Hypertext Preprocessor]PHP[/acronym]'),
    'description' => t('Puts a tooltip over the contained text, which displays the full meaning of the acronym.'),
  ];
  $tags['abbr'] = [
    'markup' => '<abbr title="{option}">{content}</abbr>',
    'sample' => t('[abbr=et cetera]etc.[/abbr]'),
    'description' => t('Identifies the content as an abbreviation and displays the full meaning in a tooltip.'),
  ];
  return $tags;
}
function xbbcode_basic_render_img($tag) {
  if ($tag->option) {
    list($width, $height) = explode('x', $tag->option);
  }
  elseif (isset($tag->args['width'], $tag->args['height'])) {
    list($width, $height) = [
      $tag->args['width'],
      $tag->args['height'],
    ];
  }
  else {
    list($width, $height) = [
      0,
      0,
    ];
  }
  $scale = $width && $height ? "style='width:{$width}px;height:{$height}px;'" : '';
  $alt = isset($tag->args['alt']) ? $tag->args['alt'] : "IMAGE({$tag->content})";
  return "<img {$scale} src='{$tag->content}' alt='{$alt}' />";
}
function xbbcode_basic_render_youtube($tag) {
  list($width, $height) = [
    560,
    315,
  ];
  if (preg_match('/^(\\d+)x(\\d+)$/', $tag->option, $match)) {
    list($width, $height) = [
      $match[1],
      $match[2],
    ];
  }
  else {
    $width = $tag
      ->attr('width') ? $tag
      ->attr('width') : $width;
    $height = $tag
      ->attr('height') ? $tag
      ->attr('height') : $height;
  }
  if (preg_match('/(\\/v\\/|((\\?|&amp;|&)v=))(?<id>.*?)(&|$)/', $tag->content, $match)) {
    $id = $match['id'];
  }
  else {
    $id = $tag->content;
  }
  return "<iframe width=\"{$width}\" height=\"{$height}\" src=\"http://www.youtube.com/embed/{$id}\" frameborder=\"0\" allowfullscreen></iframe>";
}
function xbbcode_basic_render_nodelink($tag) {
  return Drupal::l($tag->content, new Url('entity.node.canonical', [
    'node' => $tag->option,
  ]), [
    'html' => TRUE,
  ]);
}
function xbbcode_basic_render_list($tag) {
  $items = preg_split('/\\s*\\[\\*\\]\\s*/', trim($tag->content));

  // The first [*] is not a delimiter.
  array_shift($items);
  $text = '<li>' . implode('</li><li>', $items) . '</li>';
  $style = '';

  // "n" renders an <ol> element and is usually equivalent to "numeric".
  if ($tag->option == 'n') {
    $element = 'ol';
  }
  else {

    // The list style type overrides the element, so <ul> may safely be used.
    $element = 'ul';
    if (preg_match('/^[a-z-]*$/', $tag->option, $match)) {
      $style = ' style="list-style-type:' . $match[0] . '"';
    }
  }
  return "<{$element}{$style}>{$text}</{$element}>";
}
function xbbcode_basic_render_code($tag) {
  $text = $tag->source;

  // Code tags without linebreaks are rendered inline.
  if ($tag->name == 'code' && !strpos("-{$text}-", "\n") && !strpos("-{$text}-", "<br")) {
    return "<code>{$text}</code>";
  }
  $text = trim($text);
  if ($tag->name == 'php') {
    $text = preg_replace('/<\\/?code>/', '', highlight_string(html_entity_decode($text), TRUE));
  }
  $text = '<object><div class="codeblock xbbcode">' . trim($text) . '</div></object>';
  return $text;
}
function xbbcode_basic_render_define($tag) {
  $items = explode("\n", $tag->content);
  $out = '<dl>';
  foreach ($items as $item) {
    preg_match('/^((.*?):)?(.*)$/', $item, $match);
    if ($match[1]) {
      $out .= "<dt>{$match[2]}</dt>";
    }
    $out .= "<dd>{$match[3]}</dd>";
  }
  $out .= '</dl>';
  return $out;
}

/**
 * Renders a table tag.
 */
function xbbcode_basic_render_table($tag) {
  $alignment = [
    '' => 'left',
    '#' => 'right',
    '!' => 'center',
  ];
  $table = [
    '#type' => 'table',
    '#rows' => [],
  ];
  if ($tag->option) {

    // Look for an optional caption;header syntax.
    preg_match('/^(?:(.*);)?(.*)$/', $tag->option, $match);
    list($_, $table['#caption'], $headers) = $match;
  }
  else {
    $table['#caption'] = $tag
      ->attr('caption');
    $headers = $tag
      ->attr('headers');
  }
  if ($headers) {
    $headers = explode(',', $headers);
    $table['#header'] = [];
    foreach ($headers as $i => $header) {

      // !<name> is aligned right, #<name> is centered, otherwise aligned left.
      preg_match('/^([#!]?)(.+)$/', $header, $match);
      $table['#header'][$i] = $match[2];
      $align[$i] = $alignment[$match[1]];
    }
  }
  $rows = explode("\n", trim($tag->content));
  foreach ($rows as $i => $row) {
    $row = preg_split('/(?<!\\\\),/', $row);
    if ($headers) {
      foreach ($row as $j => $cell) {
        $row[$j] = [
          '#markup' => trim(str_replace('\\,', ',', $cell)),
          '#wrapper_attributes' => [
            'style' => 'text-align:' . $align[$j],
          ],
        ];
      }
      $table["row-{$i}"] = $row;
    }
    else {
      $table['#rows'][] = $row;
    }
  }

  // Strip trailing intertag whitespace, in case this text passes through a linebreak converter.
  return preg_replace('/(>)\\s+|\\s+(<)/', '\\1\\2', drupal_render($table));
}