info343/lib/writeups/php/structure.php

<?php
   define('HEAD', 'h3');
   define('SUBHEAD', 'h4');
   
   function t_head($text, $time, $opts = array()) {
      $opts['time'] = $time;
      return head_with_id(HEAD, $text, $opts);
   }
   
   function head($text, $opts = array()) {
      return head_with_id(HEAD, $text, $opts);
   }
   
   function subhead($text, $opts = array()) {
      return head_with_id(SUBHEAD, $text, $opts);
   }
   
   function head_with_id($tag, $text, $opts) {
      global $headings;
      
      $attrs = array();
      $extras = array(
         'modified' => false,
         'essential' => false,
         'optional' => false,
         'extra' => false,
         'time' => null
      );
      
      if (isset($opts['class'])) {
         if (is_array($opts['class'])) {
            $attrs['class'] = $opts['class'];
         } else {
            $attrs['class'] = array($opts['class']);
         }
      } else {
         $attrs['class'] = array();
      }
      
      if (isset($opts['id'])) {
         $id = $opts['id'];
      } else {
         $id = text2id($text);
      }
      $attrs['id'] = $id;
      
      foreach ($extras as $key => $val) {
         if (isset($opts[$key])) {
            $extras[$key] = $opts[$key];
         }
      }
      
      if ($extras['modified']) {
         array_push($attrs['class'], 'ins');
      }
      
      if ($tag == HEAD) {
         $header_classes = array();
         foreach (array('essential', 'optional', 'extra') as $header_extra) {
            if ($extras[$header_extra]) {
               array_push($header_classes, $header_extra);
            }
         }
         $header_attrs = count($header_classes) ? array('class' => $header_classes) : array();
      }
      
      switch ($tag) {
         case HEAD:
            $headings[$id] = array($text, $extras, null);
            break;
         case SUBHEAD:
            $last = end(array_keys($headings));
            list($h_text, $h_extras, $subs) = $headings[$last];
            if (!is_array($subs)) {
               $subs = array();
            }
            $subs[$id] = array($text, $extras);
            $headings[$last] = array($h_text, $h_extras, $subs);
            break;
      }
      
      $output = tag($tag, $text, $attrs) . ($extras['time'] ? tag('h4', "({$extras['time']} min)", array('class' => array('time'))) : '');
      if ($tag == HEAD) {
         $output = tag('header', $output, $header_attrs);
      }
      return $output;
   }
   
   function attributemap($attr, $value) {
      return htmlspecialchars($attr) . '="' . htmlspecialchars($value) . '"';
   }
   
   function phplink($func, $label = '') {
      if (!$label) $label = "$func()";
      return tag('code', tag('a', $label, array('href' => "http://php.net/$func"), ''));
   }
   
   function tag($name, $content, $attributes = array()) {
      $name = htmlspecialchars($name);
      // $content = htmlspecialchars($content);
      
      if (preg_match('/^(a|em|strong|code|var|samp|kbd|dfn|span|cite|ins|del|q|su[bp]|img|label|legend|textarea)$/', $name)) {
         $ending = '';
      } else {
         $ending = "\n";
      }
      
      if (isset($attributes['class'])) {
         $attributes['class'] = implode(' ', $attributes['class']);
      }
      
      $attributestr = implode(" ", array_map("attributemap", array_keys($attributes), array_values($attributes)));
      
      if ($attributestr) {
         return "<$name $attributestr>$content</$name>$ending";
      } else {
         return "<$name>$content</$name>$ending";
      }
   }
   
   function text2id($text) {
      $id = str_replace('&amp;', 'and', $text);
      $id = preg_replace('|</?[^>]+>|', '', $id);
      $id = preg_replace("/['\"‘’“”]/", '', $id);
      $id = preg_replace("/[\s   ]+/", '-', $id);
      $id = preg_replace("/[\-–—]+/", '-', $id);
      $id = preg_replace("/\b\.\b/", '-', $id);
      $id = preg_replace("/[^\w\-]/", '', $id);
      return strtolower($id);
   }
   
   function toc() {
      global $headings;
      ob_start();
      print_toc_list($headings);
      return ob_get_clean();
   }
   
   function print_toc_list($headings) {
      ?>
      <ol>
      <?php
      foreach ($headings as $headid => $parts) {
         list($text, $extras, $subs) = @$parts;
         $ins = $extras['modified'] ? ' class="ins"' : '';
         $cls = array();
         foreach (array('essential', 'optional', 'extra') as $c) {
            if ($extras[$c]) {
               $cls[] = $c;
            }
         }
         $classes = count($cls) ? ' class="' . implode(' ', $cls) . '"' : '';
         if (is_array($subs)) {
            ?>
            <li<?= $classes ?>>
               <p<?= $ins ?>>
                  <a href="#<?= $headid ?>"><?= $text ?></a>
                  <?php if ($extras['time']) { ?>
                     <span class="time">(<?= $extras['time'] ?> min)</span>
                  <?php } ?>
               </p>
               <?php print_toc_list($subs); ?>
            </li>
            <?php
         } else {
            ?>
            <li<?= $classes ?>>
               <a <?= $ins ?> href="#<?= $headid ?>"><?= $text ?></a>
               <?php if ($extras['time']) { ?>
                  <span class="time">(<?= $extras['time'] ?> min)</span>
               <?php } ?>
            </li>
            <?php
         }
      }
      ?>
      </ol>
      <?php
   }
   
   function begin_structured_page() {
      global $headings;
      $headings = array();
      ob_start();
   }
   
   function generate_structured_page() {
      print str_replace('%%TOC%%', toc(), ob_get_clean());
   }
?>