1<?php 2if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); 3if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 4require_once(DOKU_PLUGIN.'syntax.php'); 5 6class syntax_plugin_complex_lists extends DokuWiki_Syntax_Plugin { 7 8 function getInfo(){ 9 return array( 10 'author' => 'Troy Rollo', 11 'email' => 'dokuwiki@troy.rollo.name', 12 'date' => '2008-06-10', 13 'name' => 'Complex Lists Plugin', 14 'desc' => 'Add complex multi-level lists', 15 'url' => 'http://wiki.splitbrain.org/plugin:complex_lists' 16 ); 17 } 18 19 function syntax_plugin_complex_lists(){ 20 } 21 22 function getAllowedTypes() 23 { 24 return array('container', 'paragraphs', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs'); 25 } 26 27 function getType(){ 28 return 'container'; 29 } 30 31 function getPType(){ 32 return 'block'; 33 } 34 35 function getSort(){ 36 return 15; 37 } 38 39 function connectTo($mode) { 40 $this->Lexer->addEntryPattern('\n#[^\n#]+\|[^\n#]+#\n', $mode, 'plugin_complex_lists'); 41 } 42 function postConnect() { 43 $this->Lexer->addPattern('\n#\n', 'plugin_complex_lists'); 44 $this->Lexer->addPattern('\n#\+#\n', 'plugin_complex_lists'); 45 $this->Lexer->addPattern('\n#-#\n', 'plugin_complex_lists'); 46 $this->Lexer->addPattern('#:[^#]+#', 'plugin_complex_lists'); 47 $this->Lexer->addExitPattern('\n##\n','plugin_complex_lists'); 48 } 49 50 function handle($match, $state, $pos, &$handler){ 51 switch ($state) 52 { 53 case DOKU_LEXER_ENTER: 54 return array($state, $this->list_styles = explode('|', trim($match, "#\r\n"))); 55 56 case DOKU_LEXER_EDIT: 57 return array($state); 58 59 case DOKU_LEXER_UNMATCHED: 60 $handler->base($match, $state, $pos); 61 return array($state); 62 63 case DOKU_LEXER_MATCHED: 64 $match = trim($match, "\n"); 65 switch ($match) 66 { 67 case '#': 68 return array($state, 0); 69 70 case '#+#': 71 return array($state, 1); 72 73 case '#-#': 74 return array($state, 2); 75 76 default: /* Bookmark */ 77 return array($state, 3, substr(trim($match, '#'), 1)); 78 79 } 80 } 81 return array($state); 82 } 83 84 function romanNum($n) 85 { 86 $r = ''; 87 88 while ($n >= 1000) 89 { 90 $n -= 1000; 91 $r = $r . 'm'; 92 } 93 if ($n >= 900) 94 { 95 $r = $r . 'cm'; 96 $n -= 900; 97 } 98 if ($n >= 500) 99 { 100 $r = $r . 'd'; 101 $n -= 500; 102 } 103 if ($n > 400) 104 { 105 $r = $r . 'cd'; 106 $n -= 400; 107 } 108 while ($n >= 100) 109 { 110 $r = $r . 'c'; 111 $n -= 100; 112 } 113 if ($n >= 90) 114 { 115 $r = $r . 'xc'; 116 $n -= 90; 117 } 118 if ($n >= '50') 119 { 120 $r = $r . 'l'; 121 $n -= 50; 122 } 123 if ($n >= '40') 124 { 125 $r = $r . 'xl'; 126 $n -= 40; 127 } 128 while ($n >= 10) 129 { 130 $r = $r . 'x'; 131 $n -= 10; 132 } 133 if ($n == 9) 134 { 135 $r = $r . 'ix'; 136 $n = 0; 137 } 138 if ($n >= 5) 139 { 140 $r = $r .'v'; 141 $n -= 5; 142 } 143 if ($n == 4) 144 { 145 $r = $r . 'iv'; 146 $n = 0; 147 } 148 while ($n > 0) 149 { 150 $r = $r . 'i'; 151 --$n; 152 } 153 return $r; 154 } 155 156 function latinNum($n) 157 { 158 $r = ''; 159 $j=0; 160 while ($n > 0 && $j < 20) 161 { 162 $d = $n % 27; 163 $r = chr(0x60 + $d); 164 $n = ($n - $d) / 27; 165 if ($n > 0) 166 ++$n; 167 ++$j; 168 } 169 return $r; 170 } 171 172 function getLevelText($counters, $level) 173 { 174 $str = $this->list_styles[$level - 1]; 175 $len = strlen($str); 176 $type = 0; 177 $n = $counters[$level]; 178 179 for ($loc = 0; $loc < $len; ++$loc) 180 { 181 $type = strpos("01aAiI", substr($str, $loc, 1)); 182 if ($type) 183 break; 184 } 185 switch ($type) 186 { 187 default: 188 return '???'; 189 190 case 1: 191 $numtext = $n; 192 break; 193 194 case 2: 195 $numtext = $this->latinNum($n); 196 break; 197 198 case 3: 199 $numtext = strtoupper($this->latinNum($n)); 200 break; 201 202 case 4: 203 $numtext = $this->romanNum($n); 204 break; 205 206 case 5: 207 $numtext = strtoupper($this->romanNum($n)); 208 break; 209 } 210 211 return htmlspecialchars(substr($str, 0, $loc) . $numtext . substr($str, $loc + 1)); 212 } 213 214 function getListText() 215 { 216 return $this->getLevelText($this->list_counters, $this->list_level); 217 } 218 219 function startList() { 220 $this->list_level += 1; 221 $this->list_counters[$this->list_level] = 0; 222 return '<table class="complexlist' . $this->list_level . '">' . "\n"; 223 } 224 225 function startListItem() { 226 $this->list_counters[$this->list_level] += 1; 227 return '<!--CPLX-LIST-ITEM#' . $this->list_level . '#' . $this->list_counters[$this->list_level] . 228 '--><tr><td class="complexlistnum">' . 229 $this->getListText() . 230 '</td><td class="complexlistcont">' . "\n"; 231 } 232 233 function endListItem() { 234 if ($this->list_level > 0) 235 return "</td></tr>\n"; 236 } 237 238 function endList() 239 { 240 if ($this->list_level > 0) 241 { 242 $this->list_level -= 1; 243 return "<!--CPLX-LIST-ENDSUB--></table>\n"; 244 } 245 } 246 247 function getListLevelNow() 248 { 249 return $this->list_level; 250 } 251 /** 252 * Create output 253 */ 254 function render($mode, &$renderer, $data) { 255 if($mode == 'xhtml'){ 256 $first = 1; 257 switch ($data[0]) 258 { 259 case DOKU_LEXER_UNMATCHED: 260 //$renderer->doc .= "<pre>[$data[1]]</pre>"; 261 return false; 262 263 case DOKU_LEXER_ENTER: 264 $this->bookmarks = array(); 265 $this->list_styles = $data[1]; 266 267 $this->list_level = 0; 268 $renderer->doc .= '<!--CPLX-LIST-START-->'; 269 $renderer->doc .= $this->startList() . 270 $this->startListItem(); 271 return true; 272 273 case DOKU_LEXER_MATCHED: 274 switch ($data[1]) 275 { 276 case 0: 277 $renderer->doc .= $this->endListItem() . 278 $this->startListItem(); 279 break; 280 281 case 1: 282 $renderer->doc .= $this->startList() . 283 $this->startListItem(); 284 break; 285 286 case 2: 287 $renderer->doc .= $this->endListItem() . 288 $this->endList(); 289 break; 290 291 case 3: 292 $this->bookmarks[$data[2]][0] = $this->list_level; 293 $this->bookmarks[$data[2]][1] = $this->list_counters; 294 break; 295 } 296 return true; 297 298 case DOKU_LEXER_EXIT: 299 while ($this->list_level > 0) 300 { 301 $renderer->doc .= $this->endListItem() . 302 $this->endList(); 303 } 304 $pos = strpos($renderer->doc, '<!--CPLX-LIST-START-->'); 305 $listtext = substr($renderer->doc, $pos + 22); 306 $renderer->doc = substr($renderer->doc, 0, $pos); 307 308 $level = 1; 309 $lcounters[1] = 0; 310 311 while ($listtext) 312 { 313 $pos = strpos($listtext, '<!--CPLX-LIST-'); 314 if (!$pos) 315 { 316 $renderer->doc .= $listtext; 317 break; 318 } 319 $renderer->doc .= substr($listtext, 0, $pos); 320 $listtext = substr($listtext, $pos+14); 321 $pos = strpos($listtext, '-->'); 322 if (!$pos) 323 { 324 $renderer->doc .= 'Catastrophe'; 325 break; 326 } 327 $command = explode('#', substr($listtext, 0, $pos)); 328 $listtext = substr($listtext, $pos + 3); 329 330 switch ($command[0]) 331 { 332 case 'XREF': 333 $bookmark = $this->bookmarks[$command[2]]; 334 if ($bookmark) 335 { 336 if ($command[1] == 2) 337 { 338 for ($i = 1; $i <= $bookmark[0]; ++$i) 339 $renderer->doc .= $this->getLevelText($bookmark[1], $i); 340 } 341 else 342 { 343 $last = $bookmark[0]; 344 $first = $last; 345 if ($first > $level) 346 $first = $level + 1; 347 for ($i = 1; $i < $first; ++$i) 348 { 349 if ($lcounters[$i] != $bookmark[1][$i]) 350 { 351 $first = $i; 352 break; 353 } 354 } 355 for ($i = $first; $i <= $last; ++$i) 356 $renderer->doc .= $this->getLevelText($bookmark[1], $i); 357 } 358 } 359 else 360 { 361 $renderer->doc .= '<span class="cplxlisterror">Bookmark not found!</span>'; 362 } 363 break; 364 365 case 'ENDSUB': 366 --$level; 367 break; 368 369 case 'ITEM': 370 if ($command[1] > $level) 371 $level = $command[1]; 372 $lcounters[$level] = $command[2]; 373 break; 374 375 default: 376 $renderer->doc .= 'Bad command: ' . $command[0] . '!'; 377 } 378 } 379 380 return true; 381 } 382 } 383 return false; 384 } 385 386} 387 388//Setup VIM: ex: et ts=4 enc=utf-8 : 389?> 390