[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/inc/Parsing/Handler/ -> Lists.php (source)

   1  <?php
   2  
   3  namespace dokuwiki\Parsing\Handler;
   4  
   5  class Lists implements ReWriterInterface
   6  {
   7  
   8      /** @var CallWriterInterface original call writer */
   9      protected $callWriter;
  10  
  11      protected $calls = array();
  12      protected $listCalls = array();
  13      protected $listStack = array();
  14  
  15      protected $initialDepth = 0;
  16  
  17      const NODE = 1;
  18  
  19  
  20      /** @inheritdoc */
  21      public function __construct(CallWriterInterface $CallWriter)
  22      {
  23          $this->callWriter = $CallWriter;
  24      }
  25  
  26      /** @inheritdoc */
  27      public function writeCall($call)
  28      {
  29          $this->calls[] = $call;
  30      }
  31  
  32      /**
  33       * @inheritdoc
  34       * Probably not needed but just in case...
  35       */
  36      public function writeCalls($calls)
  37      {
  38          $this->calls = array_merge($this->calls, $calls);
  39      }
  40  
  41      /** @inheritdoc */
  42      public function finalise()
  43      {
  44          $last_call = end($this->calls);
  45          $this->writeCall(array('list_close',array(), $last_call[2]));
  46  
  47          $this->process();
  48          $this->callWriter->finalise();
  49          unset($this->callWriter);
  50      }
  51  
  52      /** @inheritdoc */
  53      public function process()
  54      {
  55  
  56          foreach ($this->calls as $call) {
  57              switch ($call[0]) {
  58                  case 'list_item':
  59                      $this->listOpen($call);
  60                      break;
  61                  case 'list_open':
  62                      $this->listStart($call);
  63                      break;
  64                  case 'list_close':
  65                      $this->listEnd($call);
  66                      break;
  67                  default:
  68                      $this->listContent($call);
  69                      break;
  70              }
  71          }
  72  
  73          $this->callWriter->writeCalls($this->listCalls);
  74          return $this->callWriter;
  75      }
  76  
  77      protected function listStart($call)
  78      {
  79          $depth = $this->interpretSyntax($call[1][0], $listType);
  80  
  81          $this->initialDepth = $depth;
  82          //                   array(list type, current depth, index of current listitem_open)
  83          $this->listStack[] = array($listType, $depth, 1);
  84  
  85          $this->listCalls[] = array('list'.$listType.'_open',array(),$call[2]);
  86          $this->listCalls[] = array('listitem_open',array(1),$call[2]);
  87          $this->listCalls[] = array('listcontent_open',array(),$call[2]);
  88      }
  89  
  90  
  91      protected function listEnd($call)
  92      {
  93          $closeContent = true;
  94  
  95          while ($list = array_pop($this->listStack)) {
  96              if ($closeContent) {
  97                  $this->listCalls[] = array('listcontent_close',array(),$call[2]);
  98                  $closeContent = false;
  99              }
 100              $this->listCalls[] = array('listitem_close',array(),$call[2]);
 101              $this->listCalls[] = array('list'.$list[0].'_close', array(), $call[2]);
 102          }
 103      }
 104  
 105      protected function listOpen($call)
 106      {
 107          $depth = $this->interpretSyntax($call[1][0], $listType);
 108          $end = end($this->listStack);
 109          $key = key($this->listStack);
 110  
 111          // Not allowed to be shallower than initialDepth
 112          if ($depth < $this->initialDepth) {
 113              $depth = $this->initialDepth;
 114          }
 115  
 116          if ($depth == $end[1]) {
 117              // Just another item in the list...
 118              if ($listType == $end[0]) {
 119                  $this->listCalls[] = array('listcontent_close',array(),$call[2]);
 120                  $this->listCalls[] = array('listitem_close',array(),$call[2]);
 121                  $this->listCalls[] = array('listitem_open',array($depth-1),$call[2]);
 122                  $this->listCalls[] = array('listcontent_open',array(),$call[2]);
 123  
 124                  // new list item, update list stack's index into current listitem_open
 125                  $this->listStack[$key][2] = count($this->listCalls) - 2;
 126  
 127                  // Switched list type...
 128              } else {
 129                  $this->listCalls[] = array('listcontent_close',array(),$call[2]);
 130                  $this->listCalls[] = array('listitem_close',array(),$call[2]);
 131                  $this->listCalls[] = array('list'.$end[0].'_close', array(), $call[2]);
 132                  $this->listCalls[] = array('list'.$listType.'_open', array(), $call[2]);
 133                  $this->listCalls[] = array('listitem_open', array($depth-1), $call[2]);
 134                  $this->listCalls[] = array('listcontent_open',array(),$call[2]);
 135  
 136                  array_pop($this->listStack);
 137                  $this->listStack[] = array($listType, $depth, count($this->listCalls) - 2);
 138              }
 139          } elseif ($depth > $end[1]) { // Getting deeper...
 140              $this->listCalls[] = array('listcontent_close',array(),$call[2]);
 141              $this->listCalls[] = array('list'.$listType.'_open', array(), $call[2]);
 142              $this->listCalls[] = array('listitem_open', array($depth-1), $call[2]);
 143              $this->listCalls[] = array('listcontent_open',array(),$call[2]);
 144  
 145              // set the node/leaf state of this item's parent listitem_open to NODE
 146              $this->listCalls[$this->listStack[$key][2]][1][1] = self::NODE;
 147  
 148              $this->listStack[] = array($listType, $depth, count($this->listCalls) - 2);
 149          } else { // Getting shallower ( $depth < $end[1] )
 150              $this->listCalls[] = array('listcontent_close',array(),$call[2]);
 151              $this->listCalls[] = array('listitem_close',array(),$call[2]);
 152              $this->listCalls[] = array('list'.$end[0].'_close',array(),$call[2]);
 153  
 154              // Throw away the end - done
 155              array_pop($this->listStack);
 156  
 157              while (1) {
 158                  $end = end($this->listStack);
 159                  $key = key($this->listStack);
 160  
 161                  if ($end[1] <= $depth) {
 162                      // Normalize depths
 163                      $depth = $end[1];
 164  
 165                      $this->listCalls[] = array('listitem_close',array(),$call[2]);
 166  
 167                      if ($end[0] == $listType) {
 168                          $this->listCalls[] = array('listitem_open',array($depth-1),$call[2]);
 169                          $this->listCalls[] = array('listcontent_open',array(),$call[2]);
 170  
 171                          // new list item, update list stack's index into current listitem_open
 172                          $this->listStack[$key][2] = count($this->listCalls) - 2;
 173                      } else {
 174                          // Switching list type...
 175                          $this->listCalls[] = array('list'.$end[0].'_close', array(), $call[2]);
 176                          $this->listCalls[] = array('list'.$listType.'_open', array(), $call[2]);
 177                          $this->listCalls[] = array('listitem_open', array($depth-1), $call[2]);
 178                          $this->listCalls[] = array('listcontent_open',array(),$call[2]);
 179  
 180                          array_pop($this->listStack);
 181                          $this->listStack[] = array($listType, $depth, count($this->listCalls) - 2);
 182                      }
 183  
 184                      break;
 185  
 186                      // Haven't dropped down far enough yet.... ( $end[1] > $depth )
 187                  } else {
 188                      $this->listCalls[] = array('listitem_close',array(),$call[2]);
 189                      $this->listCalls[] = array('list'.$end[0].'_close',array(),$call[2]);
 190  
 191                      array_pop($this->listStack);
 192                  }
 193              }
 194          }
 195      }
 196  
 197      protected function listContent($call)
 198      {
 199          $this->listCalls[] = $call;
 200      }
 201  
 202      protected function interpretSyntax($match, & $type)
 203      {
 204          if (substr($match, -1) == '*') {
 205              $type = 'u';
 206          } else {
 207              $type = 'o';
 208          }
 209          // Is the +1 needed? It used to be count(explode(...))
 210          // but I don't think the number is seen outside this handler
 211          return substr_count(str_replace("\t", '  ', $match), '  ') + 1;
 212      }
 213  }