├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── assets └── css │ └── backend.css ├── boot.php ├── install.php ├── lang └── de_de.lang ├── lib ├── rex_api_docs.php └── rex_api_docs_parsedown.php ├── links.php ├── package.yml ├── pages ├── docs.php ├── help.changelog.php ├── help.license.php ├── help.readme.php └── index.php ├── uninstall.php └── update.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | API Docs - Changelog 2 | ==================== 3 | 4 | ### Version 1.1.0 - 09. Juni 2018 5 | 6 | * Fixed Links 7 | * Fixed Parsedown for R5.6 8 | 9 | ### Version 1.0.0 - 15. März 2017 10 | 11 | * Erste Version 12 | 13 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Friends Of REDAXO 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | API Docs AddOn für REDAXO 5 2 | =========================== 3 | 4 | Umfangreiche Linksammlung zu REDAXO 5 Entwickler Resourcen 5 | 6 | Hinzufügen eigener Links 7 | ------------------------ 8 | 9 | In der `boot.php` des eigenen AddOns den `API_DOCS` Extension Point registrieren. 10 | 11 | ```php 12 | if (rex::isBackend()) { 13 | rex_extension::register('API_DOCS', function(rex_extension_point $ep) { 14 | $subject = $ep->getSubject(); 15 | 16 | if (isset($subject['api']['links'])) { 17 | $subject['api']['links'][] = [ 18 | 'title' => rex_i18n::msg('xcore_api_docs_title'), 19 | 'description' => rex_i18n::msg('xcore_api_docs_description'), 20 | 'href' => rex_url::backendPage('xcore/rexx_api'), 21 | 'open_in_new_window' => false 22 | ]; 23 | } 24 | 25 | $ep->setSubject($subject); 26 | }); 27 | } 28 | ``` 29 | 30 | Hinweise 31 | -------- 32 | 33 | * Getestet mit REDAXO 5.3 34 | * AddOn-Ordner lautet: `api_docs` 35 | 36 | Changelog 37 | --------- 38 | 39 | siehe `CHANGELOG.md` des AddOns 40 | 41 | Lizenz 42 | ------ 43 | 44 | MIT-Lizenz, siehe `LICENSE.md` des AddOns 45 | 46 | -------------------------------------------------------------------------------- /assets/css/backend.css: -------------------------------------------------------------------------------- 1 | .api-docs-addon .panel h2 { 2 | font-size: 18px; 3 | font-weight: bold; 4 | margin-bottom: 3px; 5 | } 6 | 7 | .api-docs-addon .panel { 8 | padding-bottom: 15px; 9 | } 10 | 11 | .api-docs-addon .panel a i { 12 | margin-left: 2px; 13 | } 14 | 15 | .api-docs-addon .panel p { 16 | line-height: 22px; 17 | } 18 | -------------------------------------------------------------------------------- /boot.php: -------------------------------------------------------------------------------- 1 | getAssetsUrl('css/backend.css')); 12 | 13 | rex_extension::register('PAGE_BODY_ATTR', function (rex_extension_point $ep) { 14 | $subject = $ep->getSubject(); 15 | $subject['class'][] = 'api-docs-addon'; 16 | 17 | $ep->setSubject($subject); 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /install.php: -------------------------------------------------------------------------------- 1 | '; 10 | } 11 | 12 | $targetAttr = 'target="_blank"'; 13 | $icon = ''; 14 | 15 | if (isset($link['open_in_new_window'])) { 16 | if ($link['open_in_new_window']) { 17 | $targetAttr = 'target="_blank"'; 18 | $icon = ''; 19 | } else { 20 | $targetAttr = ''; 21 | $icon = ''; 22 | } 23 | } 24 | 25 | if (strpos($link['href'], 'http') === false) { 26 | $shownLink = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'] . dirname($_SERVER['PHP_SELF']) . '/' . $link['href']; 27 | } else { 28 | $shownLink = $link['href']; 29 | } 30 | 31 | $content .= ' 32 |

' . $link['title'] . '

33 |

34 | ' . $description . ' 35 | ' . $shownLink . ' ' . $icon . ' 36 |

37 | '; 38 | } 39 | 40 | return $content; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/rex_api_docs_parsedown.php: -------------------------------------------------------------------------------- 1 | breaksEnabled = $breaksEnabled; 6 | } 7 | 8 | protected function blockFencedCodeComplete($Block) { 9 | $text = $Block['element']['text']['text']; 10 | 11 | $text = self::highlight($text, $Block['element']['text']['attributes']['class']); 12 | 13 | $Block['element']['text']['text'] = $text; 14 | 15 | return $Block; 16 | } 17 | 18 | // credits: http://php.net/manual/de/function.highlight-string.php#118550 19 | public static function highlight($text, $fileExt = '') { 20 | if (strpos($fileExt, 'lanuage-') === 0) { 21 | $fileExt = substr($fileExt, strlen('lanuage-')); 22 | } 23 | 24 | if ($fileExt == "php") { 25 | ini_set("highlight.comment", "#008000"); 26 | ini_set("highlight.default", "#000000"); 27 | ini_set("highlight.html", "#808080"); 28 | ini_set("highlight.keyword", "#0000BB; font-weight: bold"); 29 | ini_set("highlight.string", "#DD0000"); 30 | } else if ($fileExt == "html" || $fileExt == "javascript") { 31 | ini_set("highlight.comment", "green"); 32 | ini_set("highlight.default", "#CC0000"); 33 | ini_set("highlight.html", "#000000"); 34 | ini_set("highlight.keyword", "black; font-weight: bold"); 35 | ini_set("highlight.string", "#0000FF"); 36 | } 37 | 38 | $text = trim($text); 39 | $text = highlight_string("\\|", "", $text, 1); // remove prefix 42 | $text = preg_replace("|\\\$|", "", $text, 1); // remove suffix 1 43 | $text = trim($text); // remove line breaks 44 | $text = preg_replace("|\\\$|", "", $text, 1); // remove suffix 2 45 | $text = trim($text); // remove line breaks 46 | $text = preg_replace("|^(\\)(<\\?php )(.*?)(\\)|", "\$1\$3\$4", $text); // remove custom added "DefinitionData = array(); 64 | 65 | # standardize line breaks 66 | $text = str_replace(array("\r\n", "\r"), "\n", $text); 67 | 68 | # remove surrounding line breaks 69 | $text = trim($text, "\n"); 70 | 71 | # split text into lines 72 | $lines = explode("\n", $text); 73 | 74 | # iterate through lines to identify blocks 75 | $markup = $this->lines($lines); 76 | 77 | # trim line breaks 78 | $markup = trim($markup, "\n"); 79 | 80 | return $markup; 81 | } 82 | 83 | # 84 | # Setters 85 | # 86 | 87 | function setBreaksEnabled($breaksEnabled) 88 | { 89 | $this->breaksEnabled = $breaksEnabled; 90 | 91 | return $this; 92 | } 93 | 94 | protected $breaksEnabled; 95 | 96 | function setMarkupEscaped($markupEscaped) 97 | { 98 | $this->markupEscaped = $markupEscaped; 99 | 100 | return $this; 101 | } 102 | 103 | protected $markupEscaped; 104 | 105 | function setUrlsLinked($urlsLinked) 106 | { 107 | $this->urlsLinked = $urlsLinked; 108 | 109 | return $this; 110 | } 111 | 112 | protected $urlsLinked = true; 113 | 114 | # 115 | # Lines 116 | # 117 | 118 | protected $BlockTypes = array( 119 | '#' => array('Header'), 120 | '*' => array('Rule', 'List'), 121 | '+' => array('List'), 122 | '-' => array('SetextHeader', 'Table', 'Rule', 'List'), 123 | '0' => array('List'), 124 | '1' => array('List'), 125 | '2' => array('List'), 126 | '3' => array('List'), 127 | '4' => array('List'), 128 | '5' => array('List'), 129 | '6' => array('List'), 130 | '7' => array('List'), 131 | '8' => array('List'), 132 | '9' => array('List'), 133 | ':' => array('Table'), 134 | '<' => array('Comment', 'Markup'), 135 | '=' => array('SetextHeader'), 136 | '>' => array('Quote'), 137 | '[' => array('Reference'), 138 | '_' => array('Rule'), 139 | '`' => array('FencedCode'), 140 | '|' => array('Table'), 141 | '~' => array('FencedCode'), 142 | ); 143 | 144 | # ~ 145 | 146 | protected $unmarkedBlockTypes = array( 147 | 'Code', 148 | ); 149 | 150 | # 151 | # Blocks 152 | # 153 | 154 | protected function lines(array $lines) 155 | { 156 | $CurrentBlock = null; 157 | 158 | foreach ($lines as $line) 159 | { 160 | if (chop($line) === '') 161 | { 162 | if (isset($CurrentBlock)) 163 | { 164 | $CurrentBlock['interrupted'] = true; 165 | } 166 | 167 | continue; 168 | } 169 | 170 | if (strpos($line, "\t") !== false) 171 | { 172 | $parts = explode("\t", $line); 173 | 174 | $line = $parts[0]; 175 | 176 | unset($parts[0]); 177 | 178 | foreach ($parts as $part) 179 | { 180 | $shortage = 4 - mb_strlen($line, 'utf-8') % 4; 181 | 182 | $line .= str_repeat(' ', $shortage); 183 | $line .= $part; 184 | } 185 | } 186 | 187 | $indent = 0; 188 | 189 | while (isset($line[$indent]) and $line[$indent] === ' ') 190 | { 191 | $indent ++; 192 | } 193 | 194 | $text = $indent > 0 ? substr($line, $indent) : $line; 195 | 196 | # ~ 197 | 198 | $Line = array('body' => $line, 'indent' => $indent, 'text' => $text); 199 | 200 | # ~ 201 | 202 | if (isset($CurrentBlock['continuable'])) 203 | { 204 | $Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock); 205 | 206 | if (isset($Block)) 207 | { 208 | $CurrentBlock = $Block; 209 | 210 | continue; 211 | } 212 | else 213 | { 214 | if ($this->isBlockCompletable($CurrentBlock['type'])) 215 | { 216 | $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); 217 | } 218 | } 219 | } 220 | 221 | # ~ 222 | 223 | $marker = $text[0]; 224 | 225 | # ~ 226 | 227 | $blockTypes = $this->unmarkedBlockTypes; 228 | 229 | if (isset($this->BlockTypes[$marker])) 230 | { 231 | foreach ($this->BlockTypes[$marker] as $blockType) 232 | { 233 | $blockTypes []= $blockType; 234 | } 235 | } 236 | 237 | # 238 | # ~ 239 | 240 | foreach ($blockTypes as $blockType) 241 | { 242 | $Block = $this->{'block'.$blockType}($Line, $CurrentBlock); 243 | 244 | if (isset($Block)) 245 | { 246 | $Block['type'] = $blockType; 247 | 248 | if ( ! isset($Block['identified'])) 249 | { 250 | $Blocks []= $CurrentBlock; 251 | 252 | $Block['identified'] = true; 253 | } 254 | 255 | if ($this->isBlockContinuable($blockType)) 256 | { 257 | $Block['continuable'] = true; 258 | } 259 | 260 | $CurrentBlock = $Block; 261 | 262 | continue 2; 263 | } 264 | } 265 | 266 | # ~ 267 | 268 | if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted'])) 269 | { 270 | $CurrentBlock['element']['text'] .= "\n".$text; 271 | } 272 | else 273 | { 274 | $Blocks []= $CurrentBlock; 275 | 276 | $CurrentBlock = $this->paragraph($Line); 277 | 278 | $CurrentBlock['identified'] = true; 279 | } 280 | } 281 | 282 | # ~ 283 | 284 | if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type'])) 285 | { 286 | $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); 287 | } 288 | 289 | # ~ 290 | 291 | $Blocks []= $CurrentBlock; 292 | 293 | unset($Blocks[0]); 294 | 295 | # ~ 296 | 297 | $markup = ''; 298 | 299 | foreach ($Blocks as $Block) 300 | { 301 | if (isset($Block['hidden'])) 302 | { 303 | continue; 304 | } 305 | 306 | $markup .= "\n"; 307 | $markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']); 308 | } 309 | 310 | $markup .= "\n"; 311 | 312 | # ~ 313 | 314 | return $markup; 315 | } 316 | 317 | protected function isBlockContinuable($Type) 318 | { 319 | return method_exists($this, 'block'.$Type.'Continue'); 320 | } 321 | 322 | protected function isBlockCompletable($Type) 323 | { 324 | return method_exists($this, 'block'.$Type.'Complete'); 325 | } 326 | 327 | # 328 | # Code 329 | 330 | protected function blockCode($Line, $Block = null) 331 | { 332 | if (isset($Block) and ! isset($Block['type']) and ! isset($Block['interrupted'])) 333 | { 334 | return; 335 | } 336 | 337 | if ($Line['indent'] >= 4) 338 | { 339 | $text = substr($Line['body'], 4); 340 | 341 | $Block = array( 342 | 'element' => array( 343 | 'name' => 'pre', 344 | 'handler' => 'element', 345 | 'text' => array( 346 | 'name' => 'code', 347 | 'text' => $text, 348 | ), 349 | ), 350 | ); 351 | 352 | return $Block; 353 | } 354 | } 355 | 356 | protected function blockCodeContinue($Line, $Block) 357 | { 358 | if ($Line['indent'] >= 4) 359 | { 360 | if (isset($Block['interrupted'])) 361 | { 362 | $Block['element']['text']['text'] .= "\n"; 363 | 364 | unset($Block['interrupted']); 365 | } 366 | 367 | $Block['element']['text']['text'] .= "\n"; 368 | 369 | $text = substr($Line['body'], 4); 370 | 371 | $Block['element']['text']['text'] .= $text; 372 | 373 | return $Block; 374 | } 375 | } 376 | 377 | protected function blockCodeComplete($Block) 378 | { 379 | $text = $Block['element']['text']['text']; 380 | 381 | $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8'); 382 | 383 | $Block['element']['text']['text'] = $text; 384 | 385 | return $Block; 386 | } 387 | 388 | # 389 | # Comment 390 | 391 | protected function blockComment($Line) 392 | { 393 | if ($this->markupEscaped) 394 | { 395 | return; 396 | } 397 | 398 | if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!') 399 | { 400 | $Block = array( 401 | 'markup' => $Line['body'], 402 | ); 403 | 404 | if (preg_match('/-->$/', $Line['text'])) 405 | { 406 | $Block['closed'] = true; 407 | } 408 | 409 | return $Block; 410 | } 411 | } 412 | 413 | protected function blockCommentContinue($Line, array $Block) 414 | { 415 | if (isset($Block['closed'])) 416 | { 417 | return; 418 | } 419 | 420 | $Block['markup'] .= "\n" . $Line['body']; 421 | 422 | if (preg_match('/-->$/', $Line['text'])) 423 | { 424 | $Block['closed'] = true; 425 | } 426 | 427 | return $Block; 428 | } 429 | 430 | # 431 | # Fenced Code 432 | 433 | protected function blockFencedCode($Line) 434 | { 435 | if (preg_match('/^['.$Line['text'][0].']{3,}[ ]*([\w-]+)?[ ]*$/', $Line['text'], $matches)) 436 | { 437 | $Element = array( 438 | 'name' => 'code', 439 | 'text' => '', 440 | ); 441 | 442 | if (isset($matches[1])) 443 | { 444 | $class = 'language-'.$matches[1]; 445 | 446 | $Element['attributes'] = array( 447 | 'class' => $class, 448 | ); 449 | } 450 | 451 | $Block = array( 452 | 'char' => $Line['text'][0], 453 | 'element' => array( 454 | 'name' => 'pre', 455 | 'handler' => 'element', 456 | 'text' => $Element, 457 | ), 458 | ); 459 | 460 | return $Block; 461 | } 462 | } 463 | 464 | protected function blockFencedCodeContinue($Line, $Block) 465 | { 466 | if (isset($Block['complete'])) 467 | { 468 | return; 469 | } 470 | 471 | if (isset($Block['interrupted'])) 472 | { 473 | $Block['element']['text']['text'] .= "\n"; 474 | 475 | unset($Block['interrupted']); 476 | } 477 | 478 | if (preg_match('/^'.$Block['char'].'{3,}[ ]*$/', $Line['text'])) 479 | { 480 | $Block['element']['text']['text'] = substr($Block['element']['text']['text'], 1); 481 | 482 | $Block['complete'] = true; 483 | 484 | return $Block; 485 | } 486 | 487 | $Block['element']['text']['text'] .= "\n".$Line['body']; 488 | 489 | return $Block; 490 | } 491 | 492 | # 493 | # Header 494 | 495 | protected function blockHeader($Line) 496 | { 497 | if (isset($Line['text'][1])) 498 | { 499 | $level = 1; 500 | 501 | while (isset($Line['text'][$level]) and $Line['text'][$level] === '#') 502 | { 503 | $level ++; 504 | } 505 | 506 | if ($level > 6) 507 | { 508 | return; 509 | } 510 | 511 | $text = trim($Line['text'], '# '); 512 | 513 | $Block = array( 514 | 'element' => array( 515 | 'name' => 'h' . min(6, $level), 516 | 'text' => $text, 517 | 'handler' => 'line', 518 | ), 519 | ); 520 | 521 | return $Block; 522 | } 523 | } 524 | 525 | # 526 | # List 527 | 528 | protected function blockList($Line) 529 | { 530 | list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]+[.]'); 531 | 532 | if (preg_match('/^('.$pattern.'[ ]+)(.*)/', $Line['text'], $matches)) 533 | { 534 | $Block = array( 535 | 'indent' => $Line['indent'], 536 | 'pattern' => $pattern, 537 | 'element' => array( 538 | 'name' => $name, 539 | 'handler' => 'elements', 540 | ), 541 | ); 542 | 543 | if($name === 'ol') 544 | { 545 | $listStart = stristr($matches[0], '.', true); 546 | 547 | if($listStart !== '1') 548 | { 549 | $Block['element']['attributes'] = array('start' => $listStart); 550 | } 551 | } 552 | 553 | $Block['li'] = array( 554 | 'name' => 'li', 555 | 'handler' => 'li', 556 | 'text' => array( 557 | $matches[2], 558 | ), 559 | ); 560 | 561 | $Block['element']['text'] []= & $Block['li']; 562 | 563 | return $Block; 564 | } 565 | } 566 | 567 | protected function blockListContinue($Line, array $Block) 568 | { 569 | if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'(?:[ ]+(.*)|$)/', $Line['text'], $matches)) 570 | { 571 | if (isset($Block['interrupted'])) 572 | { 573 | $Block['li']['text'] []= ''; 574 | 575 | unset($Block['interrupted']); 576 | } 577 | 578 | unset($Block['li']); 579 | 580 | $text = isset($matches[1]) ? $matches[1] : ''; 581 | 582 | $Block['li'] = array( 583 | 'name' => 'li', 584 | 'handler' => 'li', 585 | 'text' => array( 586 | $text, 587 | ), 588 | ); 589 | 590 | $Block['element']['text'] []= & $Block['li']; 591 | 592 | return $Block; 593 | } 594 | 595 | if ($Line['text'][0] === '[' and $this->blockReference($Line)) 596 | { 597 | return $Block; 598 | } 599 | 600 | if ( ! isset($Block['interrupted'])) 601 | { 602 | $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']); 603 | 604 | $Block['li']['text'] []= $text; 605 | 606 | return $Block; 607 | } 608 | 609 | if ($Line['indent'] > 0) 610 | { 611 | $Block['li']['text'] []= ''; 612 | 613 | $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']); 614 | 615 | $Block['li']['text'] []= $text; 616 | 617 | unset($Block['interrupted']); 618 | 619 | return $Block; 620 | } 621 | } 622 | 623 | # 624 | # Quote 625 | 626 | protected function blockQuote($Line) 627 | { 628 | if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) 629 | { 630 | $Block = array( 631 | 'element' => array( 632 | 'name' => 'blockquote', 633 | 'handler' => 'lines', 634 | 'text' => (array) $matches[1], 635 | ), 636 | ); 637 | 638 | return $Block; 639 | } 640 | } 641 | 642 | protected function blockQuoteContinue($Line, array $Block) 643 | { 644 | if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) 645 | { 646 | if (isset($Block['interrupted'])) 647 | { 648 | $Block['element']['text'] []= ''; 649 | 650 | unset($Block['interrupted']); 651 | } 652 | 653 | $Block['element']['text'] []= $matches[1]; 654 | 655 | return $Block; 656 | } 657 | 658 | if ( ! isset($Block['interrupted'])) 659 | { 660 | $Block['element']['text'] []= $Line['text']; 661 | 662 | return $Block; 663 | } 664 | } 665 | 666 | # 667 | # Rule 668 | 669 | protected function blockRule($Line) 670 | { 671 | if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text'])) 672 | { 673 | $Block = array( 674 | 'element' => array( 675 | 'name' => 'hr' 676 | ), 677 | ); 678 | 679 | return $Block; 680 | } 681 | } 682 | 683 | # 684 | # Setext 685 | 686 | protected function blockSetextHeader($Line, array $Block = null) 687 | { 688 | if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted'])) 689 | { 690 | return; 691 | } 692 | 693 | if (chop($Line['text'], $Line['text'][0]) === '') 694 | { 695 | $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2'; 696 | 697 | return $Block; 698 | } 699 | } 700 | 701 | # 702 | # Markup 703 | 704 | protected function blockMarkup($Line) 705 | { 706 | if ($this->markupEscaped) 707 | { 708 | return; 709 | } 710 | 711 | if (preg_match('/^<(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches)) 712 | { 713 | $element = strtolower($matches[1]); 714 | 715 | if (in_array($element, $this->textLevelElements)) 716 | { 717 | return; 718 | } 719 | 720 | $Block = array( 721 | 'name' => $matches[1], 722 | 'depth' => 0, 723 | 'markup' => $Line['text'], 724 | ); 725 | 726 | $length = strlen($matches[0]); 727 | 728 | $remainder = substr($Line['text'], $length); 729 | 730 | if (trim($remainder) === '') 731 | { 732 | if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) 733 | { 734 | $Block['closed'] = true; 735 | 736 | $Block['void'] = true; 737 | } 738 | } 739 | else 740 | { 741 | if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) 742 | { 743 | return; 744 | } 745 | 746 | if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder)) 747 | { 748 | $Block['closed'] = true; 749 | } 750 | } 751 | 752 | return $Block; 753 | } 754 | } 755 | 756 | protected function blockMarkupContinue($Line, array $Block) 757 | { 758 | if (isset($Block['closed'])) 759 | { 760 | return; 761 | } 762 | 763 | if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open 764 | { 765 | $Block['depth'] ++; 766 | } 767 | 768 | if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close 769 | { 770 | if ($Block['depth'] > 0) 771 | { 772 | $Block['depth'] --; 773 | } 774 | else 775 | { 776 | $Block['closed'] = true; 777 | } 778 | } 779 | 780 | if (isset($Block['interrupted'])) 781 | { 782 | $Block['markup'] .= "\n"; 783 | 784 | unset($Block['interrupted']); 785 | } 786 | 787 | $Block['markup'] .= "\n".$Line['body']; 788 | 789 | return $Block; 790 | } 791 | 792 | # 793 | # Reference 794 | 795 | protected function blockReference($Line) 796 | { 797 | if (preg_match('/^\[(.+?)\]:[ ]*?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches)) 798 | { 799 | $id = strtolower($matches[1]); 800 | 801 | $Data = array( 802 | 'url' => $matches[2], 803 | 'title' => null, 804 | ); 805 | 806 | if (isset($matches[3])) 807 | { 808 | $Data['title'] = $matches[3]; 809 | } 810 | 811 | $this->DefinitionData['Reference'][$id] = $Data; 812 | 813 | $Block = array( 814 | 'hidden' => true, 815 | ); 816 | 817 | return $Block; 818 | } 819 | } 820 | 821 | # 822 | # Table 823 | 824 | protected function blockTable($Line, array $Block = null) 825 | { 826 | if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted'])) 827 | { 828 | return; 829 | } 830 | 831 | if (strpos($Block['element']['text'], '|') !== false and chop($Line['text'], ' -:|') === '') 832 | { 833 | $alignments = array(); 834 | 835 | $divider = $Line['text']; 836 | 837 | $divider = trim($divider); 838 | $divider = trim($divider, '|'); 839 | 840 | $dividerCells = explode('|', $divider); 841 | 842 | foreach ($dividerCells as $dividerCell) 843 | { 844 | $dividerCell = trim($dividerCell); 845 | 846 | if ($dividerCell === '') 847 | { 848 | continue; 849 | } 850 | 851 | $alignment = null; 852 | 853 | if ($dividerCell[0] === ':') 854 | { 855 | $alignment = 'left'; 856 | } 857 | 858 | if (substr($dividerCell, - 1) === ':') 859 | { 860 | $alignment = $alignment === 'left' ? 'center' : 'right'; 861 | } 862 | 863 | $alignments []= $alignment; 864 | } 865 | 866 | # ~ 867 | 868 | $HeaderElements = array(); 869 | 870 | $header = $Block['element']['text']; 871 | 872 | $header = trim($header); 873 | $header = trim($header, '|'); 874 | 875 | $headerCells = explode('|', $header); 876 | 877 | foreach ($headerCells as $index => $headerCell) 878 | { 879 | $headerCell = trim($headerCell); 880 | 881 | $HeaderElement = array( 882 | 'name' => 'th', 883 | 'text' => $headerCell, 884 | 'handler' => 'line', 885 | ); 886 | 887 | if (isset($alignments[$index])) 888 | { 889 | $alignment = $alignments[$index]; 890 | 891 | $HeaderElement['attributes'] = array( 892 | 'style' => 'text-align: '.$alignment.';', 893 | ); 894 | } 895 | 896 | $HeaderElements []= $HeaderElement; 897 | } 898 | 899 | # ~ 900 | 901 | $Block = array( 902 | 'alignments' => $alignments, 903 | 'identified' => true, 904 | 'element' => array( 905 | 'name' => 'table', 906 | 'handler' => 'elements', 907 | ), 908 | ); 909 | 910 | $Block['element']['text'] []= array( 911 | 'name' => 'thead', 912 | 'handler' => 'elements', 913 | ); 914 | 915 | $Block['element']['text'] []= array( 916 | 'name' => 'tbody', 917 | 'handler' => 'elements', 918 | 'text' => array(), 919 | ); 920 | 921 | $Block['element']['text'][0]['text'] []= array( 922 | 'name' => 'tr', 923 | 'handler' => 'elements', 924 | 'text' => $HeaderElements, 925 | ); 926 | 927 | return $Block; 928 | } 929 | } 930 | 931 | protected function blockTableContinue($Line, array $Block) 932 | { 933 | if (isset($Block['interrupted'])) 934 | { 935 | return; 936 | } 937 | 938 | if ($Line['text'][0] === '|' or strpos($Line['text'], '|')) 939 | { 940 | $Elements = array(); 941 | 942 | $row = $Line['text']; 943 | 944 | $row = trim($row); 945 | $row = trim($row, '|'); 946 | 947 | preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches); 948 | 949 | foreach ($matches[0] as $index => $cell) 950 | { 951 | $cell = trim($cell); 952 | 953 | $Element = array( 954 | 'name' => 'td', 955 | 'handler' => 'line', 956 | 'text' => $cell, 957 | ); 958 | 959 | if (isset($Block['alignments'][$index])) 960 | { 961 | $Element['attributes'] = array( 962 | 'style' => 'text-align: '.$Block['alignments'][$index].';', 963 | ); 964 | } 965 | 966 | $Elements []= $Element; 967 | } 968 | 969 | $Element = array( 970 | 'name' => 'tr', 971 | 'handler' => 'elements', 972 | 'text' => $Elements, 973 | ); 974 | 975 | $Block['element']['text'][1]['text'] []= $Element; 976 | 977 | return $Block; 978 | } 979 | } 980 | 981 | # 982 | # ~ 983 | # 984 | 985 | protected function paragraph($Line) 986 | { 987 | $Block = array( 988 | 'element' => array( 989 | 'name' => 'p', 990 | 'text' => $Line['text'], 991 | 'handler' => 'line', 992 | ), 993 | ); 994 | 995 | return $Block; 996 | } 997 | 998 | # 999 | # Inline Elements 1000 | # 1001 | 1002 | protected $InlineTypes = array( 1003 | '"' => array('SpecialCharacter'), 1004 | '!' => array('Image'), 1005 | '&' => array('SpecialCharacter'), 1006 | '*' => array('Emphasis'), 1007 | ':' => array('Url'), 1008 | '<' => array('UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'), 1009 | '>' => array('SpecialCharacter'), 1010 | '[' => array('Link'), 1011 | '_' => array('Emphasis'), 1012 | '`' => array('Code'), 1013 | '~' => array('Strikethrough'), 1014 | '\\' => array('EscapeSequence'), 1015 | ); 1016 | 1017 | # ~ 1018 | 1019 | protected $inlineMarkerList = '!"*_&[:<>`~\\'; 1020 | 1021 | # 1022 | # ~ 1023 | # 1024 | 1025 | public function line($text) 1026 | { 1027 | $markup = ''; 1028 | 1029 | # $excerpt is based on the first occurrence of a marker 1030 | 1031 | while ($excerpt = strpbrk($text, $this->inlineMarkerList)) 1032 | { 1033 | $marker = $excerpt[0]; 1034 | 1035 | $markerPosition = strpos($text, $marker); 1036 | 1037 | $Excerpt = array('text' => $excerpt, 'context' => $text); 1038 | 1039 | foreach ($this->InlineTypes[$marker] as $inlineType) 1040 | { 1041 | $Inline = $this->{'inline'.$inlineType}($Excerpt); 1042 | 1043 | if ( ! isset($Inline)) 1044 | { 1045 | continue; 1046 | } 1047 | 1048 | # makes sure that the inline belongs to "our" marker 1049 | 1050 | if (isset($Inline['position']) and $Inline['position'] > $markerPosition) 1051 | { 1052 | continue; 1053 | } 1054 | 1055 | # sets a default inline position 1056 | 1057 | if ( ! isset($Inline['position'])) 1058 | { 1059 | $Inline['position'] = $markerPosition; 1060 | } 1061 | 1062 | # the text that comes before the inline 1063 | $unmarkedText = substr($text, 0, $Inline['position']); 1064 | 1065 | # compile the unmarked text 1066 | $markup .= $this->unmarkedText($unmarkedText); 1067 | 1068 | # compile the inline 1069 | $markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']); 1070 | 1071 | # remove the examined text 1072 | $text = substr($text, $Inline['position'] + $Inline['extent']); 1073 | 1074 | continue 2; 1075 | } 1076 | 1077 | # the marker does not belong to an inline 1078 | 1079 | $unmarkedText = substr($text, 0, $markerPosition + 1); 1080 | 1081 | $markup .= $this->unmarkedText($unmarkedText); 1082 | 1083 | $text = substr($text, $markerPosition + 1); 1084 | } 1085 | 1086 | $markup .= $this->unmarkedText($text); 1087 | 1088 | return $markup; 1089 | } 1090 | 1091 | # 1092 | # ~ 1093 | # 1094 | 1095 | protected function inlineCode($Excerpt) 1096 | { 1097 | $marker = $Excerpt['text'][0]; 1098 | 1099 | if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(? strlen($matches[0]), 1107 | 'element' => array( 1108 | 'name' => 'code', 1109 | 'text' => $text, 1110 | ), 1111 | ); 1112 | } 1113 | } 1114 | 1115 | protected function inlineEmailTag($Excerpt) 1116 | { 1117 | if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches)) 1118 | { 1119 | $url = $matches[1]; 1120 | 1121 | if ( ! isset($matches[2])) 1122 | { 1123 | $url = 'mailto:' . $url; 1124 | } 1125 | 1126 | return array( 1127 | 'extent' => strlen($matches[0]), 1128 | 'element' => array( 1129 | 'name' => 'a', 1130 | 'text' => $matches[1], 1131 | 'attributes' => array( 1132 | 'href' => $url, 1133 | ), 1134 | ), 1135 | ); 1136 | } 1137 | } 1138 | 1139 | protected function inlineEmphasis($Excerpt) 1140 | { 1141 | if ( ! isset($Excerpt['text'][1])) 1142 | { 1143 | return; 1144 | } 1145 | 1146 | $marker = $Excerpt['text'][0]; 1147 | 1148 | if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches)) 1149 | { 1150 | $emphasis = 'strong'; 1151 | } 1152 | elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches)) 1153 | { 1154 | $emphasis = 'em'; 1155 | } 1156 | else 1157 | { 1158 | return; 1159 | } 1160 | 1161 | return array( 1162 | 'extent' => strlen($matches[0]), 1163 | 'element' => array( 1164 | 'name' => $emphasis, 1165 | 'handler' => 'line', 1166 | 'text' => $matches[1], 1167 | ), 1168 | ); 1169 | } 1170 | 1171 | protected function inlineEscapeSequence($Excerpt) 1172 | { 1173 | if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters)) 1174 | { 1175 | return array( 1176 | 'markup' => $Excerpt['text'][1], 1177 | 'extent' => 2, 1178 | ); 1179 | } 1180 | } 1181 | 1182 | protected function inlineImage($Excerpt) 1183 | { 1184 | if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[') 1185 | { 1186 | return; 1187 | } 1188 | 1189 | $Excerpt['text']= substr($Excerpt['text'], 1); 1190 | 1191 | $Link = $this->inlineLink($Excerpt); 1192 | 1193 | if ($Link === null) 1194 | { 1195 | return; 1196 | } 1197 | 1198 | $Inline = array( 1199 | 'extent' => $Link['extent'] + 1, 1200 | 'element' => array( 1201 | 'name' => 'img', 1202 | 'attributes' => array( 1203 | 'src' => $Link['element']['attributes']['href'], 1204 | 'alt' => $Link['element']['text'], 1205 | ), 1206 | ), 1207 | ); 1208 | 1209 | $Inline['element']['attributes'] += $Link['element']['attributes']; 1210 | 1211 | unset($Inline['element']['attributes']['href']); 1212 | 1213 | return $Inline; 1214 | } 1215 | 1216 | protected function inlineLink($Excerpt) 1217 | { 1218 | $Element = array( 1219 | 'name' => 'a', 1220 | 'handler' => 'line', 1221 | 'text' => null, 1222 | 'attributes' => array( 1223 | 'href' => null, 1224 | 'title' => null, 1225 | ), 1226 | ); 1227 | 1228 | $extent = 0; 1229 | 1230 | $remainder = $Excerpt['text']; 1231 | 1232 | if (preg_match('/\[((?:[^][]++|(?R))*+)\]/', $remainder, $matches)) 1233 | { 1234 | $Element['text'] = $matches[1]; 1235 | 1236 | $extent += strlen($matches[0]); 1237 | 1238 | $remainder = substr($remainder, $extent); 1239 | } 1240 | else 1241 | { 1242 | return; 1243 | } 1244 | 1245 | if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*"|\'[^\']*\'))?\s*[)]/', $remainder, $matches)) 1246 | { 1247 | $Element['attributes']['href'] = $matches[1]; 1248 | 1249 | if (isset($matches[2])) 1250 | { 1251 | $Element['attributes']['title'] = substr($matches[2], 1, - 1); 1252 | } 1253 | 1254 | $extent += strlen($matches[0]); 1255 | } 1256 | else 1257 | { 1258 | if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches)) 1259 | { 1260 | $definition = strlen($matches[1]) ? $matches[1] : $Element['text']; 1261 | $definition = strtolower($definition); 1262 | 1263 | $extent += strlen($matches[0]); 1264 | } 1265 | else 1266 | { 1267 | $definition = strtolower($Element['text']); 1268 | } 1269 | 1270 | if ( ! isset($this->DefinitionData['Reference'][$definition])) 1271 | { 1272 | return; 1273 | } 1274 | 1275 | $Definition = $this->DefinitionData['Reference'][$definition]; 1276 | 1277 | $Element['attributes']['href'] = $Definition['url']; 1278 | $Element['attributes']['title'] = $Definition['title']; 1279 | } 1280 | 1281 | $Element['attributes']['href'] = str_replace(array('&', '<'), array('&', '<'), $Element['attributes']['href']); 1282 | 1283 | return array( 1284 | 'extent' => $extent, 1285 | 'element' => $Element, 1286 | ); 1287 | } 1288 | 1289 | protected function inlineMarkup($Excerpt) 1290 | { 1291 | if ($this->markupEscaped or strpos($Excerpt['text'], '>') === false) 1292 | { 1293 | return; 1294 | } 1295 | 1296 | if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w*[ ]*>/s', $Excerpt['text'], $matches)) 1297 | { 1298 | return array( 1299 | 'markup' => $matches[0], 1300 | 'extent' => strlen($matches[0]), 1301 | ); 1302 | } 1303 | 1304 | if ($Excerpt['text'][1] === '!' and preg_match('/^/s', $Excerpt['text'], $matches)) 1305 | { 1306 | return array( 1307 | 'markup' => $matches[0], 1308 | 'extent' => strlen($matches[0]), 1309 | ); 1310 | } 1311 | 1312 | if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches)) 1313 | { 1314 | return array( 1315 | 'markup' => $matches[0], 1316 | 'extent' => strlen($matches[0]), 1317 | ); 1318 | } 1319 | } 1320 | 1321 | protected function inlineSpecialCharacter($Excerpt) 1322 | { 1323 | if ($Excerpt['text'][0] === '&' and ! preg_match('/^&#?\w+;/', $Excerpt['text'])) 1324 | { 1325 | return array( 1326 | 'markup' => '&', 1327 | 'extent' => 1, 1328 | ); 1329 | } 1330 | 1331 | $SpecialCharacter = array('>' => 'gt', '<' => 'lt', '"' => 'quot'); 1332 | 1333 | if (isset($SpecialCharacter[$Excerpt['text'][0]])) 1334 | { 1335 | return array( 1336 | 'markup' => '&'.$SpecialCharacter[$Excerpt['text'][0]].';', 1337 | 'extent' => 1, 1338 | ); 1339 | } 1340 | } 1341 | 1342 | protected function inlineStrikethrough($Excerpt) 1343 | { 1344 | if ( ! isset($Excerpt['text'][1])) 1345 | { 1346 | return; 1347 | } 1348 | 1349 | if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches)) 1350 | { 1351 | return array( 1352 | 'extent' => strlen($matches[0]), 1353 | 'element' => array( 1354 | 'name' => 'del', 1355 | 'text' => $matches[1], 1356 | 'handler' => 'line', 1357 | ), 1358 | ); 1359 | } 1360 | } 1361 | 1362 | protected function inlineUrl($Excerpt) 1363 | { 1364 | if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/') 1365 | { 1366 | return; 1367 | } 1368 | 1369 | if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE)) 1370 | { 1371 | $Inline = array( 1372 | 'extent' => strlen($matches[0][0]), 1373 | 'position' => $matches[0][1], 1374 | 'element' => array( 1375 | 'name' => 'a', 1376 | 'text' => $matches[0][0], 1377 | 'attributes' => array( 1378 | 'href' => $matches[0][0], 1379 | ), 1380 | ), 1381 | ); 1382 | 1383 | return $Inline; 1384 | } 1385 | } 1386 | 1387 | protected function inlineUrlTag($Excerpt) 1388 | { 1389 | if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches)) 1390 | { 1391 | $url = str_replace(array('&', '<'), array('&', '<'), $matches[1]); 1392 | 1393 | return array( 1394 | 'extent' => strlen($matches[0]), 1395 | 'element' => array( 1396 | 'name' => 'a', 1397 | 'text' => $url, 1398 | 'attributes' => array( 1399 | 'href' => $url, 1400 | ), 1401 | ), 1402 | ); 1403 | } 1404 | } 1405 | 1406 | # ~ 1407 | 1408 | protected function unmarkedText($text) 1409 | { 1410 | if ($this->breaksEnabled) 1411 | { 1412 | $text = preg_replace('/[ ]*\n/', "
\n", $text); 1413 | } 1414 | else 1415 | { 1416 | $text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "
\n", $text); 1417 | $text = str_replace(" \n", "\n", $text); 1418 | } 1419 | 1420 | return $text; 1421 | } 1422 | 1423 | # 1424 | # Handlers 1425 | # 1426 | 1427 | protected function element(array $Element) 1428 | { 1429 | $markup = '<'.$Element['name']; 1430 | 1431 | if (isset($Element['attributes'])) 1432 | { 1433 | foreach ($Element['attributes'] as $name => $value) 1434 | { 1435 | if ($value === null) 1436 | { 1437 | continue; 1438 | } 1439 | 1440 | $markup .= ' '.$name.'="'.$value.'"'; 1441 | } 1442 | } 1443 | 1444 | if (isset($Element['text'])) 1445 | { 1446 | $markup .= '>'; 1447 | 1448 | if (isset($Element['handler'])) 1449 | { 1450 | $markup .= $this->{$Element['handler']}($Element['text']); 1451 | } 1452 | else 1453 | { 1454 | $markup .= $Element['text']; 1455 | } 1456 | 1457 | $markup .= ''; 1458 | } 1459 | else 1460 | { 1461 | $markup .= ' />'; 1462 | } 1463 | 1464 | return $markup; 1465 | } 1466 | 1467 | protected function elements(array $Elements) 1468 | { 1469 | $markup = ''; 1470 | 1471 | foreach ($Elements as $Element) 1472 | { 1473 | $markup .= "\n" . $this->element($Element); 1474 | } 1475 | 1476 | $markup .= "\n"; 1477 | 1478 | return $markup; 1479 | } 1480 | 1481 | # ~ 1482 | 1483 | protected function li($lines) 1484 | { 1485 | $markup = $this->lines($lines); 1486 | 1487 | $trimmedMarkup = trim($markup); 1488 | 1489 | if ( ! in_array('', $lines) and substr($trimmedMarkup, 0, 3) === '

') 1490 | { 1491 | $markup = $trimmedMarkup; 1492 | $markup = substr($markup, 3); 1493 | 1494 | $position = strpos($markup, "

"); 1495 | 1496 | $markup = substr_replace($markup, '', $position, 4); 1497 | } 1498 | 1499 | return $markup; 1500 | } 1501 | 1502 | # 1503 | # Deprecated Methods 1504 | # 1505 | 1506 | function parse($text) 1507 | { 1508 | $markup = $this->text($text); 1509 | 1510 | return $markup; 1511 | } 1512 | 1513 | # 1514 | # Static Methods 1515 | # 1516 | 1517 | static function instance($name = 'default') 1518 | { 1519 | if (isset(self::$instances[$name])) 1520 | { 1521 | return self::$instances[$name]; 1522 | } 1523 | 1524 | $instance = new static(); 1525 | 1526 | self::$instances[$name] = $instance; 1527 | 1528 | return $instance; 1529 | } 1530 | 1531 | private static $instances = array(); 1532 | 1533 | # 1534 | # Fields 1535 | # 1536 | 1537 | protected $DefinitionData; 1538 | 1539 | # 1540 | # Read-Only 1541 | 1542 | protected $specialCharacters = array( 1543 | '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|', 1544 | ); 1545 | 1546 | protected $StrongRegex = array( 1547 | '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s', 1548 | '_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us', 1549 | ); 1550 | 1551 | protected $EmRegex = array( 1552 | '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s', 1553 | '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us', 1554 | ); 1555 | 1556 | protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?'; 1557 | 1558 | protected $voidElements = array( 1559 | 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 1560 | ); 1561 | 1562 | protected $textLevelElements = array( 1563 | 'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont', 1564 | 'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing', 1565 | 'i', 'rp', 'del', 'code', 'strike', 'marquee', 1566 | 'q', 'rt', 'ins', 'font', 'strong', 1567 | 's', 'tt', 'kbd', 'mark', 1568 | 'u', 'xm', 'sub', 'nobr', 1569 | 'sup', 'ruby', 1570 | 'var', 'span', 1571 | 'wbr', 'time', 1572 | ); 1573 | } 1574 | 1575 | -------------------------------------------------------------------------------- /links.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'title' => rex_i18n::msg('api_docs_api'), 6 | 'links' => [ 7 | [ 8 | 'title' => 'REDAXO 5 API', 9 | 'description' => 'Die komplette R5 API', 10 | 'href' => 'http://www.redaxo.org/docs/master/' 11 | ], 12 | ] 13 | ], 14 | 'handbook' => [ 15 | 'title' => rex_i18n::msg('api_docs_handbook'), 16 | 'links' => [ 17 | [ 18 | 'title' => 'REDAXO 5 Handbuch', 19 | 'description' => 'Das REDAXO 5 Handbuch', 20 | 'href' => 'https://redaxo.org/doku/master' 21 | ], 22 | ] 23 | ], 24 | 'extension_points' => [ 25 | 'title' => rex_i18n::msg('api_docs_extension_points'), 26 | 'links' => [ 27 | [ 28 | 'title' => 'Extension Points aus Entwickler-Handbuch', 29 | 'description' => 'Extension Points sind unten auf der Seite aufgeführt', 30 | 'href' => 'https://redaxo.org/doku/master/extension-points' 31 | ], 32 | [ 33 | 'title' => 'Cheatsheet AddOn', 34 | 'description' => 'Extrahiert und listet on-the-fly alle Extension Points aus Core und AddOns', 35 | 'href' => 'https://github.com/tbaddade/redaxo_cheatsheet' 36 | ], 37 | [ 38 | 'title' => 'Änderungen in REDAXO 5', 39 | 'description' => 'Siehe Abschnitt Extension Points', 40 | 'href' => 'https://redaxo.org/doku/master/aenderungen-v4-v5' 41 | ], 42 | ] 43 | ], 44 | 'rexvars' => [ 45 | 'title' => rex_i18n::msg('api_docs_rexvars'), 46 | 'links' => [ 47 | [ 48 | 'title' => 'REDAXO Variablen aus Dokumentation', 49 | 'description' => 'Enthält zahlreiche Beispiele', 50 | 'href' => 'https://redaxo.org/doku/master/redaxo-variablen' 51 | ], 52 | [ 53 | 'title' => 'Cheatsheet AddOn', 54 | 'description' => 'Extrahiert und listet on-the-fly alle REDAXO Variablen', 55 | 'href' => 'https://github.com/tbaddade/redaxo_cheatsheet' 56 | ], 57 | [ 58 | 'title' => 'Änderungen in REDAXO 5', 59 | 'description' => 'Siehe Abschnitt REX_VAR und $REX weiter oben', 60 | 'href' => 'https://redaxo.org/doku/master/aenderungen-v4-v5' 61 | ], 62 | ] 63 | ], 64 | 'r5_changes' => [ 65 | 'title' => rex_i18n::msg('api_docs_r5_changes'), 66 | 'links' => [ 67 | [ 68 | 'title' => 'Änderungen in REDAXO 5', 69 | 'description' => 'Alle Änderungen von R4 zu R5 im Überblick', 70 | 'href' => 'https://redaxo.org/doku/master/aenderungen-v4-v5' 71 | ], 72 | ] 73 | ], 74 | 'code_snippets' => [ 75 | 'title' => rex_i18n::msg('api_docs_code_snippets'), 76 | 'links' => [ 77 | [ 78 | 'title' => 'Tips und Tricks', 79 | 'description' => 'Code Snippet Sammlung von Friends Of REDAXO', 80 | 'href' => 'https://github.com/FriendsOfREDAXO/tricks' 81 | ], 82 | [ 83 | 'title' => 'Weitere Tips und Tricks', 84 | 'description' => 'Offene Issues aus der Code Snippet Sammlung von Friends Of REDAXO', 85 | 'href' => 'https://github.com/FriendsOfREDAXO/tricks/issues' 86 | ], 87 | [ 88 | 'title' => 'Passwort vergessen', 89 | 'description' => 'Thema Passwort vergessen aus der neuen Dokumentation', 90 | 'href' => 'https://github.com/redaxo/docs/blob/master/passwort-vergessen.md' 91 | ], 92 | [ 93 | 'title' => 'Aktionen', 94 | 'description' => 'Thema Aktionen aus der neuen Dokumentation', 95 | 'href' => 'https://redaxo.org/doku/master/modul-aktionen' 96 | ], 97 | [ 98 | 'title' => 'Fragmente', 99 | 'description' => 'Thema Fragmente aus der neuen Dokumentation', 100 | 'href' => 'https://redaxo.org/doku/master/fragmente' 101 | ], 102 | [ 103 | 'title' => 'Paginierung', 104 | 'description' => 'Thema Paginierung aus der neuen Dokumentation', 105 | 'href' => 'https://redaxo.org/doku/master/paginierung' 106 | ], 107 | ] 108 | ], 109 | 'faq' => [ 110 | 'title' => rex_i18n::msg('api_docs_faq'), 111 | 'links' => [ 112 | [ 113 | 'title' => 'FAQ by Alex Plus', 114 | 'description' => 'Aktuell nur als Issues auf GitHub', 115 | 'href' => 'https://github.com/redaxo/docs/issues?utf8=%E2%9C%93&q=Q%26A%20is%3Aissue%20author%3Aalexplusde' 116 | ], 117 | ] 118 | ], 119 | 'github' => [ 120 | 'title' => rex_i18n::msg('api_docs_github'), 121 | 'links' => [ 122 | [ 123 | 'title' => 'REDAXO Core', 124 | 'description' => 'GitHub Repository des Cores', 125 | 'href' => 'https://github.com/redaxo/redaxo/' 126 | ], 127 | [ 128 | 'title' => 'AddOns von REDAXO', 129 | 'description' => 'GitHub Repository mit AddOns wie Media Manager, Meta Info und Backup', 130 | 'href' => 'https://github.com/redaxo/redaxo/tree/master/redaxo/src/addons' 131 | ], 132 | [ 133 | 'title' => 'AddOns von Yakamara', 134 | 'description' => 'GitHub Repository mit AddOns wie yForm, yCom und yRewrite', 135 | 'href' => ' https://github.com/yakamara/' 136 | ], 137 | [ 138 | 'title' => 'AddOns von Friends Of REDAXO', 139 | 'description' => 'GitHub Repository mit AddOns wie Developer, MForm, Search It', 140 | 'href' => 'https://github.com/FriendsOfREDAXO' 141 | ], 142 | [ 143 | 'title' => 'AddOns von tbaddade', 144 | 'description' => 'GitHub Repository mit AddOns Url, Sprog und Cheatsheet', 145 | 'href' => 'https://github.com/tbaddade/' 146 | ], 147 | [ 148 | 'title' => 'AddOns von RexDude', 149 | 'description' => 'GitHub Repository mit AddOns wie CKEditor, Globale Einstellungen und Navigation Factory', 150 | 'href' => 'https://github.com/RexDude/' 151 | ], 152 | ] 153 | ], 154 | 'cheatsheet' => [ 155 | 'title' => rex_i18n::msg('api_docs_cheatsheet'), 156 | 'links' => [ 157 | [ 158 | 'title' => 'Altes Cheatsheet für REDAXO 4 von tbaddade', 159 | 'description' => 'Siehe REDAXO API und Änderungen in REDAXO 5 für Umschreibungen', 160 | 'href' => 'http://blumbeet.com/files/cheatsheet_redaxo_42.pdf' 161 | ], 162 | [ 163 | 'title' => 'Altes Cheatsheet für REDAXO 4 von jdlx', 164 | 'description' => 'Siehe REDAXO API und Änderungen in REDAXO 5 für Umschreibungen', 165 | 'href' => 'http://rexdev.de/cheatsheets/rex450.html' 166 | ], 167 | ] 168 | ], 169 | ]; 170 | -------------------------------------------------------------------------------- /package.yml: -------------------------------------------------------------------------------- 1 | package: api_docs 2 | version: '1.2.0-dev' 3 | author: Friends Of REDAXO 4 | supportpage: https://github.com/FriendsOfREDAXO/api_docs 5 | 6 | page: 7 | title: translate:api_docs_title 8 | perm: admin[] 9 | pjax: false 10 | icon: rex-icon fa-book 11 | subpages: 12 | docs: { title: 'translate:docs' } 13 | help: 14 | title: 'translate:help' 15 | perm: admin[] 16 | subpages: 17 | readme: { title: 'translate:help_readme' } 18 | changelog: { title: 'translate:help_changelog' } 19 | license: { title: 'translate:help_license' } 20 | 21 | requires: 22 | redaxo: '^5.0' 23 | -------------------------------------------------------------------------------- /pages/docs.php: -------------------------------------------------------------------------------- 1 | '; 5 | $content .= ''; 21 | $content .= '
'; 22 | 23 | $first = true; 24 | 25 | foreach ($apiDocs as $section => $value) { 26 | if ($first) { 27 | $class = 'active'; 28 | $first = false; 29 | } else { 30 | $class = ''; 31 | } 32 | 33 | $content .= '
' . rex_api_docs::getLinks($apiDocs[$section]) . '
'; 34 | } 35 | 36 | $content .= '
'; 37 | $content .= ''; 38 | 39 | $fragment = new rex_fragment(); 40 | $fragment->setVar('title', rex_i18n::msg('api_docs_docs'), false); 41 | $fragment->setVar('body', $content, false); 42 | 43 | echo $fragment->parse('core/page/section.php'); 44 | -------------------------------------------------------------------------------- /pages/help.changelog.php: -------------------------------------------------------------------------------- 1 | getPath('CHANGELOG.md')); 3 | 4 | $parsedown = new rex_api_docs_parsedown(); 5 | $content = $parsedown->text($file); 6 | 7 | $fragment = new rex_fragment(); 8 | $fragment->setVar('title', $this->i18n('help_changelog')); 9 | $fragment->setVar('body', $content, false); 10 | 11 | echo $fragment->parse('core/page/section.php'); 12 | -------------------------------------------------------------------------------- /pages/help.license.php: -------------------------------------------------------------------------------- 1 | getPath('LICENSE.md')); 3 | 4 | $parsedown = new rex_api_docs_parsedown(); 5 | $content = $parsedown->text($file); 6 | 7 | $fragment = new rex_fragment(); 8 | $fragment->setVar('title', $this->i18n('help_license')); 9 | $fragment->setVar('body', $content, false); 10 | 11 | echo $fragment->parse('core/page/section.php'); 12 | -------------------------------------------------------------------------------- /pages/help.readme.php: -------------------------------------------------------------------------------- 1 | getPath('README.md')); 3 | 4 | $parsedown = new rex_api_docs_parsedown(); 5 | $content = $parsedown->text($file); 6 | 7 | $fragment = new rex_fragment(); 8 | $fragment->setVar('title', $this->i18n('help_readme')); 9 | $fragment->setVar('body', $content, false); 10 | 11 | echo $fragment->parse('core/page/section.php'); 12 | -------------------------------------------------------------------------------- /pages/index.php: -------------------------------------------------------------------------------- 1 | i18n('title')); 7 | 8 | $subpage = rex_be_controller::getCurrentPagePart(2); 9 | 10 | include rex_be_controller::getCurrentPageObject()->getSubPath(); 11 | 12 | 13 | -------------------------------------------------------------------------------- /uninstall.php: -------------------------------------------------------------------------------- 1 |