1<?php 2 3/** 4 * Mikio Core Syntax Plugin 5 * 6 * @link http://github.com/nomadjimbob/mikioplugin 7 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 8 * @author James Collins <james.collins@outlook.com.au> 9 */ 10if (!defined('DOKU_INC')) { die(); 11} 12if (!defined('DOKU_PLUGIN')) { define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/'); 13} 14 15require_once(dirname(__FILE__).'/../disabled-tags.php'); 16 17define('MIKIO_LEXER_AUTO', 0); 18define('MIKIO_LEXER_ENTER', 1); 19define('MIKIO_LEXER_EXIT', 2); 20define('MIKIO_LEXER_SPECIAL', 3); 21 22class syntax_plugin_mikioplugin_core extends DokuWiki_Syntax_Plugin 23{ 24 public $pattern_entry = ''; 25 public $pattern = ''; 26 public $pattern_exit = ''; 27 public $tag = ''; 28 public $requires_tag = ''; 29 public $hasEndTag = true; 30 public $options = array(); 31 32 protected $tagPrefix = ''; //'mikio-'; 33 protected $classPrefix = 'mikiop-'; 34 protected $elemClass = 'mikiop'; 35 36 private $values = array(); 37 38 39 function __construct() 40 { 41 } 42 43 public function isDisabled() 44 { 45 global $mikio_disabled_tags; 46 47 if (isset($mikio_disabled_tags) === true) { 48 if(array_key_exists($this->tag, $mikio_disabled_tags) === true && $mikio_disabled_tags[$this->tag] === true) { 49 return true; 50 } 51 52 // check requirements 53 if($this->requires_tag !== '') { 54 if(array_key_exists($this->requires_tag, $mikio_disabled_tags) === true && $mikio_disabled_tags[$this->requires_tag] === true) { 55 return true; 56 } 57 } 58 } 59 60 return false; 61 } 62 63 public function getType() 64 { 65 return 'formatting'; 66 } 67 public function getAllowedTypes() 68 { 69 return array('formatting', 'substition', 'disabled', 'paragraphs'); 70 } 71 // public function getAllowedTypes() { return array('formatting', 'substition', 'disabled'); } 72 public function getSort() 73 { 74 return 32; 75 } 76 public function getPType() 77 { 78 return 'stack'; 79 } 80 81 82 public function connectTo($mode) 83 { 84 if($this->isDisabled() == true) { 85 return; 86 } 87 88 if ($this->pattern_entry == '' && $this->tag != '') { 89 if ($this->hasEndTag) { 90 $this->pattern_entry = '<(?i:' . $this->tagPrefix . $this->tag . ')(?=[ >]).*?>(?=.*?</(?i:' . $this->tagPrefix . $this->tag . ')>)'; 91 } else { 92 $this->pattern_entry = '<(?i:' . $this->tagPrefix . $this->tag . ').*?>'; 93 } 94 } 95 96 if ($this->pattern_entry != '') { 97 if ($this->hasEndTag) { 98 $this->Lexer->addEntryPattern($this->pattern_entry, $mode, 'plugin_mikioplugin_' . $this->getPluginComponent()); 99 } else { 100 $this->Lexer->addSpecialPattern($this->pattern_entry, $mode, 'plugin_mikioplugin_' . $this->getPluginComponent()); 101 } 102 } 103 } 104 105 106 public function postConnect() 107 { 108 if ($this->hasEndTag) { 109 if ($this->pattern_exit == '' && $this->tag != '') { 110 $this->pattern_exit = '</(?i:' . $this->tagPrefix . $this->tag . ')>'; 111 } 112 113 if ($this->pattern_exit != '') { 114 $this->Lexer->addExitPattern($this->pattern_exit, 'plugin_mikioplugin_' . $this->getPluginComponent()); 115 } 116 } 117 } 118 119 public function handle($match, $state, $pos, Doku_Handler $handler) 120 { 121 if($this->isDisabled() != true) { 122 switch ($state) { 123 case DOKU_LEXER_ENTER: 124 case DOKU_LEXER_SPECIAL: 125 $match_fix = preg_replace('/\s*=\s*/', '=', trim(substr($match, strlen($this->tagPrefix . $this->tag) + 1, -1))); 126 $optionlist = preg_split('/\s(?=([^"]*"[^"]*")*[^"]*$)/', $match_fix); 127 128 $options = array(); 129 foreach ($optionlist as $item) { 130 $i = strpos($item, '='); 131 if ($i !== false) { 132 $value = substr($item, $i + 1); 133 134 if (substr($value, 0, 1) == '"') { $value = substr($value, 1); 135 } 136 if (substr($value, -1) == '"') { $value = substr($value, 0, -1); 137 } 138 139 $options[substr($item, 0, $i)] = $value; 140 } else { 141 $options[$item] = true; 142 } 143 } 144 145 if (count($this->options) > 0) { 146 $options_clean = $this->cleanOptions($options); 147 } else { 148 $options_clean = $options; 149 } 150 151 $this->values = $options_clean; 152 153 return array($state, $options_clean); 154 155 case DOKU_LEXER_MATCHED: 156 return array($state, $match); 157 158 case DOKU_LEXER_UNMATCHED: 159 return array($state, $match); 160 161 case DOKU_LEXER_EXIT: 162 return array($state, $this->values); 163 } 164 } 165 166 return array(); 167 } 168 169 170 /* 171 * clean element options to only supported attributes, setting defaults if required 172 * 173 * @param $options options passed to element 174 * @return array of options supported with default set 175 */ 176 protected function cleanOptions($data, $options = null) 177 { 178 $optionsCleaned = array(); 179 180 if ($options == null) { $options = $this->options; 181 } 182 183 // Match DokuWiki passed options to syntax options 184 foreach ($data as $optionKey => $optionValue) { 185 foreach ($options as $syntaxKey => $syntaxValue) { 186 if (strcasecmp($optionKey, $syntaxKey) == 0) { 187 if (array_key_exists('type', $options[$syntaxKey])) { 188 $type = $options[$syntaxKey]['type']; 189 190 switch ($type) { 191 case 'boolean': 192 $optionsCleaned[$syntaxKey] = filter_var($optionValue, FILTER_VALIDATE_BOOLEAN); 193 break; 194 case 'number': 195 $optionsCleaned[$syntaxKey] = filter_var($optionValue, FILTER_VALIDATE_INT); 196 break; 197 case 'float': 198 $optionsCleaned[$syntaxKey] = filter_var($optionValue, FILTER_VALIDATE_FLOAT); 199 break; 200 case 'text': 201 $optionsCleaned[$syntaxKey] = $optionValue; 202 break; 203 case 'size': 204 $s = strtolower($optionValue); 205 $i = ''; 206 if (substr($s, -3) == 'rem') { 207 $i = substr($s, 0, -3); 208 $s = 'rem'; 209 } elseif (substr($s, -2) == 'em') { 210 $i = substr($s, 0, -2); 211 $s = 'em'; 212 } elseif (substr($s, -2) == 'px') { 213 $i = substr($s, 0, -2); 214 $s = 'px'; 215 } elseif (substr($s, -1) == '%') { 216 $i = substr($s, 0, -1); 217 $s = '%'; 218 } else { 219 if ($s != 'auto') { 220 $i = filter_var($s, FILTER_VALIDATE_INT); 221 if ($i == '') { $i = '1'; 222 } 223 $s = 'rem'; 224 } 225 } 226 227 $optionsCleaned[$syntaxKey] = $i . $s; 228 break; 229 case 'multisize': 230 $val = ''; 231 $parts = explode(' ', $optionValue); 232 foreach ($parts as &$part) { 233 $s = strtolower($part); 234 $i = ''; 235 if (substr($s, -3) == 'rem') { 236 $i = substr($s, 0, -3); 237 $s = 'rem'; 238 } elseif (substr($s, -2) == 'em') { 239 $i = substr($s, 0, -2); 240 $s = 'em'; 241 } elseif (substr($s, -2) == 'px') { 242 $i = substr($s, 0, -2); 243 $s = 'px'; 244 } elseif (substr($s, -2) == 'fr') { 245 $i = substr($s, 0, -2); 246 $s = 'fr'; 247 } elseif (substr($s, -1) == '%') { 248 $i = substr($s, 0, -1); 249 $s = '%'; 250 } else { 251 if ($s != 'auto') { 252 $i = filter_var($s, FILTER_VALIDATE_INT); 253 if ($i === '') { $i = '1'; 254 } 255 if ($i != 0) { 256 $s = 'rem'; 257 } else { 258 $s = ''; 259 } 260 } 261 } 262 263 $part = $i . $s; 264 } 265 266 $optionsCleaned[$syntaxKey] = implode(' ', $parts); 267 break; 268 case 'color': 269 if (strlen($optionValue) == 3 || strlen($optionValue) == 6) { 270 preg_match('/([[:xdigit:]]{3}){1,2}/', $optionValue, $matches); 271 if (count($matches) > 1) { 272 $optionsCleaned[$syntaxKey] = '#' . $matches[0]; 273 } else { 274 $optionsCleaned[$syntaxKey] = $optionValue; 275 } 276 } else { 277 $optionsCleaned[$syntaxKey] = $optionValue; 278 } 279 break; 280 case 'url': 281 $optionsCleaned[$syntaxKey] = $this->buildLink($optionValue); 282 break; 283 case 'media': 284 $optionsCleaned[$syntaxKey] = $this->buildMediaLink($optionValue); 285 break; 286 case 'choice': 287 if (array_key_exists('data', $options[$syntaxKey])) { 288 foreach ($options[$syntaxKey]['data'] as $choiceKey => $choiceValue) { 289 if (strcasecmp($optionValue, $choiceKey) == 0) { 290 $optionsCleaned[$syntaxKey] = $choiceKey; 291 break; 292 } 293 294 if (is_array($choiceValue)) { 295 foreach ($choiceValue as $choiceItem) { 296 if (strcasecmp($optionValue, $choiceItem) == 0) { 297 $optionsCleaned[$syntaxKey] = $choiceKey; 298 break 2; 299 } 300 } 301 } else { 302 if (strcasecmp($optionValue, $choiceValue) == 0) { 303 $optionsCleaned[$syntaxKey] = $choiceValue; 304 break; 305 } 306 } 307 } 308 } 309 break; 310 case 'set': 311 if (array_key_exists('option', $options[$syntaxKey]) && array_key_exists('data', $options[$syntaxKey])) { 312 $optionsCleaned[$options[$syntaxKey]['option']] = $options[$syntaxKey]['data']; 313 } 314 break; 315 } 316 } 317 318 break; 319 } 320 } 321 } 322 323 324 foreach ($data as $optionKey => $optionValue) { 325 if (!array_key_exists($optionKey, $optionsCleaned)) { 326 foreach ($options as $syntaxKey => $syntaxValue) { 327 if (array_key_exists('type', $options[$syntaxKey])) { 328 if (array_key_exists('data', $options[$syntaxKey]) && is_array($options[$syntaxKey]['data'])) { 329 foreach ($options[$syntaxKey]['data'] as $choiceKey => $choiceValue) { 330 if (is_array($choiceValue)) { 331 if (in_array($optionKey, $choiceValue)) { 332 $optionsCleaned[$syntaxKey] = $choiceKey; 333 } 334 } else { 335 if (strcasecmp($choiceValue, $optionKey) == 0) { 336 $optionsCleaned[$syntaxKey] = $choiceValue; 337 } 338 } 339 } 340 } 341 } 342 } 343 } 344 } 345 346 // Add in syntax options that are missing 347 foreach ($options as $optionKey => $optionValue) { 348 if (!array_key_exists($optionKey, $optionsCleaned)) { 349 if (array_key_exists('default', $options[$optionKey])) { 350 switch ($options[$optionKey]['type']) { 351 case 'boolean': 352 $optionsCleaned[$optionKey] = filter_var($options[$optionKey]['default'], FILTER_VALIDATE_BOOLEAN); 353 break; 354 case 'number': 355 $optionsCleaned[$optionKey] = filter_var($options[$optionKey]['default'], FILTER_VALIDATE_INT); 356 break; 357 default: 358 $optionsCleaned[$optionKey] = $options[$optionKey]['default']; 359 break; 360 } 361 } 362 } 363 } 364 365 return $optionsCleaned; 366 } 367 368 /* Lexer renderers */ 369 protected function render_lexer_enter(Doku_Renderer $renderer, $data) 370 { 371 } 372 protected function render_lexer_unmatched(Doku_Renderer $renderer, $data) 373 { 374 $renderer->doc .= $renderer->_xmlEntities($data); 375 } 376 protected function render_lexer_exit(Doku_Renderer $renderer, $data) 377 { 378 } 379 protected function render_lexer_special(Doku_Renderer $renderer, $data) 380 { 381 } 382 protected function render_lexer_match(Doku_Renderer $renderer, $data) 383 { 384 } 385 386 /* Renderer */ 387 public function render($mode, Doku_Renderer $renderer, $data) 388 { 389 if ($mode == 'xhtml' && $this->isDisabled() != true) { 390 list($state, $match) = $data; 391 392 switch ($state) { 393 case DOKU_LEXER_ENTER: 394 $this->render_lexer_enter($renderer, $match); 395 return true; 396 397 case DOKU_LEXER_UNMATCHED: 398 $this->render_lexer_unmatched($renderer, $match); 399 return true; 400 401 case DOKU_LEXER_MATCHED: 402 $this->render_lexer_match($renderer, $match); 403 return true; 404 405 case DOKU_LEXER_EXIT: 406 $this->render_lexer_exit($renderer, $match); 407 return true; 408 409 case DOKU_LEXER_SPECIAL: 410 $this->render_lexer_special($renderer, $match); 411 return true; 412 } 413 414 return true; 415 } 416 417 return false; 418 } 419 420 /* 421 * return a class list with mikiop- prefix 422 * 423 * @param $options options of syntax element. Options with key 'class'=true are automatically added 424 * @param $classes classes to build from options as array 425 * @param $inclAttr include class="" in the return string 426 * @param $optionsTemplate allow a different options template instead of $this->options (for findTags) 427 * @return a string of classes from options/classes variable 428 */ 429 public function buildClass($options = null, $classes = null, $inclAttr = false, $optionsTemplate = null) 430 { 431 $s = array(); 432 433 if (is_array($options)) { 434 if ($classes == null) { $classes = array(); 435 } 436 if ($optionsTemplate == null) { $optionsTemplate = $this->options; 437 } 438 439 foreach ($optionsTemplate as $key => $value) { 440 if (array_key_exists('class', $value) && $value['class'] == true) { 441 array_push($classes, $key); 442 } 443 } 444 445 foreach ($classes as $class) { 446 if (array_key_exists($class, $options) && $options[$class] !== false && $options[$class] != '') { 447 $prefix = $this->classPrefix; 448 449 if (array_key_exists($class, $optionsTemplate) && array_key_exists('prefix', $optionsTemplate[$class])) { 450 $prefix .= $optionsTemplate[$class]['prefix']; 451 } 452 453 if (array_key_exists($class, $optionsTemplate) && array_key_exists('classNoSuffix', $optionsTemplate[$class]) && $optionsTemplate[$class]['classNoSuffix'] == true) { 454 $s[] = $prefix . $class; 455 } else { 456 $s[] = $prefix . $class . ($options[$class] !== true ? '-' . $options[$class] : ''); 457 } 458 } 459 } 460 } 461 462 $s = implode(' ', $s); 463 if ($s != '') { $s = ' ' . $s; 464 } 465 466 if ($inclAttr) { $s = ' classes="' . $s . '"'; 467 } 468 469 return $s; 470 } 471 472 473 474 475 /* 476 * build style string 477 * 478 * @param $list style list as key => value. Empty values are not included 479 * @param $inclAttr include style="" in the return string 480 * @return style list string 481 */ 482 public function buildStyle($list, $inclAttr = false) 483 { 484 $s = ''; 485 486 if (is_array($list) && count($list) > 0) { 487 foreach ($list as $key => $value) { 488 if ($value != '') { 489 $s .= $key . ':' . $value . ';'; 490 } 491 } 492 } 493 494 if ($s != '' && $inclAttr) { 495 $s = ' style="' . $s . '"'; 496 } 497 498 return $s; 499 } 500 501 502 public function buildTooltipString($options) 503 { 504 $dataPlacement = 'top'; 505 $dataHtml = false; 506 $title = ''; 507 508 if ($options != null) { 509 if (array_key_exists('tooltip-html-top', $options) && $options['tooltip-html-top'] != '') { 510 $title = $options['tooltip-html-top']; 511 $dataPlacement = 'top'; 512 } 513 514 if (array_key_exists('tooltip-html-left', $options) && $options['tooltip-html-left'] != '') { 515 $title = $options['tooltip-html-left']; 516 $dataPlacement = 'left'; 517 } 518 519 if (array_key_exists('tooltip-html-bottom', $options) && $options['tooltip-html-bottom'] != '') { 520 $title = $options['tooltip-html-bottom']; 521 $dataPlacement = 'bottom'; 522 } 523 524 if (array_key_exists('tooltip-html-right', $options) && $options['tooltip-html-right'] != '') { 525 $title = $options['tooltip-html-right']; 526 $dataPlacement = 'right'; 527 } 528 529 if (array_key_exists('tooltip-top', $options) && $options['tooltip-top'] != '') { 530 $title = $options['tooltip-top']; 531 $dataPlacement = 'top'; 532 } 533 534 if (array_key_exists('tooltip-left', $options) && $options['tooltip-left'] != '') { 535 $title = $options['tooltip-left']; 536 $dataPlacement = 'left'; 537 } 538 539 if (array_key_exists('tooltip-bottom', $options) && $options['tooltip-bottom'] != '') { 540 $title = $options['tooltip-bottom']; 541 $dataPlacement = 'bottom'; 542 } 543 544 if (array_key_exists('tooltip-right', $options) && $options['tooltip-right'] != '') { 545 $title = $options['tooltip-right']; 546 $dataPlacement = 'right'; 547 } 548 549 if (array_key_exists('tooltip-html', $options) && $options['tooltip-html'] != '') { 550 $title = $options['tooltip-html']; 551 $dataPlacement = 'top'; 552 } 553 554 if (array_key_exists('tooltip', $options) && $options['tooltip'] != '') { 555 $title = $options['tooltip']; 556 $dataPlacement = 'top'; 557 } 558 } 559 560 if ($title != '') { 561 return ' data-toggle="tooltip" data-placement="' . $dataPlacement . '" ' . ($dataHtml == true ? 'data-html="true" ' : '') . 'title="' . $title . '" '; 562 } 563 564 return ''; 565 } 566 567 /* 568 * convert the URL to a DokuWiki media link (if required) 569 * 570 * @param $url url to parse 571 * @return url string 572 */ 573 public function buildMediaLink($url) 574 { 575 $i = strpos($url, '?'); 576 if ($i !== false) { $url = substr($url, 0, $i); 577 } 578 579 $url = preg_replace('/[^\da-zA-Z:_.-]+/', '', $url); 580 581 return (tpl_getMediaFile(array($url), false)); 582 } 583 584 585 /* 586 * returns either a url or dokuwiki link 587 * 588 * @param $url link to build from 589 * @return built link 590 */ 591 public function buildLink($url) 592 { 593 $i = strpos($url, '://'); 594 if ($i !== false || substr($url, 0, 1) == '#') { return $url; 595 } 596 597 return wl($url); 598 } 599 600 /* 601 * Call syntax renderer of mikio syntax plugin 602 * 603 * @param $renderer DokuWiki renderer object 604 * @param $className mikio syntax class to call 605 * @param $text unmatched text to pass outside of lexer. Only used when $lexer=MIKIO_LEXER_AUTO 606 * @param $data tag options to pass to syntax class. Runs through cleanOptions to validate first 607 * @param $lexer which lexer to call 608 */ 609 public function syntaxRender(Doku_Renderer $renderer, $className, $text, $data = null, $lexer = MIKIO_LEXER_AUTO) 610 { 611 $className = 'syntax_plugin_mikioplugin_' . str_replace('-', '', $className); 612 613 if (class_exists($className)) { 614 $class = new $className; 615 616 if (!is_array($data)) { $data = array(); 617 } 618 619 620 if (count($class->options) > 0) { 621 $data = $class->cleanOptions($data, $class->options); 622 } 623 624 switch ($lexer) { 625 case MIKIO_LEXER_AUTO: 626 if ($class->hasEndTag) { 627 if (method_exists($class, 'render_lexer_enter')) { $class->render_lexer_enter($renderer, $data); 628 } 629 $renderer->doc .= $text; 630 if (method_exists($class, 'render_lexer_exit')) { $class->render_lexer_exit($renderer, $data); 631 } 632 } else { 633 if (method_exists($class, 'render_lexer_special')) { $class->render_lexer_special($renderer, $data); 634 } 635 } 636 637 break; 638 case MIKIO_LEXER_ENTER: 639 if (method_exists($class, 'render_lexer_enter')) { $class->render_lexer_enter($renderer, $data); 640 } 641 break; 642 case MIKIO_LEXER_EXIT: 643 if (method_exists($class, 'render_lexer_exit')) { $class->render_lexer_exit($renderer, $data); 644 } 645 break; 646 case MIKIO_LEXER_SPECIAL: 647 if (method_exists($class, 'render_lexer_special')) { $class->render_lexer_special($renderer, $data); 648 } 649 break; 650 } 651 } 652 } 653 654 655 protected function callMikioTag($className, $data) 656 { 657 // $className = 'syntax_plugin_mikioplugin_'.$className; 658 659 660 // if(class_exists($className)) { 661 //$class = new $className; 662 if (!plugin_isdisabled('mikioplugin')) { 663 $class = plugin_load('syntax', 'mikioplugin_' . $className); 664 // echo '^^'.$className.'^^'; 665 666 667 if (method_exists($class, 'mikioCall')) { return $class->mikioCall($data); 668 } 669 } 670 671 // } 672 673 return ''; 674 } 675 676 677 protected function callMikioOptionDefault($className, $option) 678 { 679 $className = 'syntax_plugin_mikioplugin_' . $className; 680 681 if (class_exists($className)) { 682 $class = new $className; 683 684 if (array_key_exists($option, $class->options) && array_key_exists('default', $class->options[$option])) { 685 return $class->options[$option]['default']; 686 } 687 } 688 689 return ''; 690 } 691 692 693 protected function buildTooltip($text) 694 { 695 if ($text != '') { 696 return ' data-tooltip="' . $text . '"'; 697 } 698 699 return ''; 700 } 701 702 /* 703 * Create array with passed elements and include them if their values are not empty 704 * 705 * @param ... array items 706 */ 707 protected function arrayRemoveEmpties($items) 708 { 709 $result = array(); 710 711 foreach ($items as $key => $value) { 712 if ($value != '') { 713 $result[$key] = $value; 714 } 715 } 716 717 return $result; 718 } 719 720 public function getFirstArrayKey($data) 721 { 722 if (!function_exists('array_key_first')) { 723 foreach ($data as $key => $unused) { 724 return $key; 725 } 726 } 727 728 return array_key_first($data); 729 } 730 731 732 /* 733 * add common options to options 734 * 735 * @param $typelist common option to add 736 * @param $options save in options 737 */ 738 public function addCommonOptions($typelist) 739 { 740 $types = explode(' ', $typelist); 741 foreach ($types as $type) { 742 if (strcasecmp($type, 'shadow') == 0) { 743 $this->options['shadow'] = array( 744 'type' => 'choice', 745 'data' => array('large' => array('shadow-large', 'shadow-lg'), 'small' => array('shadow-small', 'shadow-sm'), true), 746 'default' => '', 747 'class' => true 748 ); 749 } 750 751 if (strcasecmp($type, 'width') == 0) { 752 $this->options['width'] = array( 753 'type' => 'size', 754 'default' => '' 755 ); 756 } 757 758 if (strcasecmp($type, 'height') == 0) { 759 $this->options['height'] = array( 760 'type' => 'size', 761 'default' => '' 762 ); 763 } 764 765 if (strcasecmp($type, 'text-color') == 0) { 766 $this->options['text-color'] = array( 767 'type' => 'color', 768 'default' => '' 769 ); 770 } 771 772 if (strcasecmp($type, 'type') == 0) { 773 $this->options['type'] = array( 774 'type' => 'text', 775 'data' => array('primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark', 'outline-primary', 'outline-secondary', 'outline-success', 'outline-danger', 'outline-warning', 'outline-info', 'outline-light', 'outline-dark'), 776 'default' => '', 777 'class' => true 778 ); 779 } 780 781 if (strcasecmp($type, 'text-align') == 0) { 782 $this->options['text-align'] = array( 783 'type' => 'choice', 784 'data' => array('left' => array('text-left'), 'center' => array('text-center'), 'right' => array('text-right')), 785 'default' => '', 786 'class' => true 787 ); 788 } 789 790 if (strcasecmp($type, 'align') == 0) { 791 $this->options['align'] = array( 792 'type' => 'choice', 793 'data' => array('left' => array('align-left'), 'center' => array('align-center'), 'right' => array('align-right')), 794 'default' => '', 795 'class' => true 796 ); 797 } 798 799 if (strcasecmp($type, 'tooltip') == 0) { 800 $this->options['tooltip'] = array( 801 'type' => 'text', 802 'default' => '', 803 'class' => true, 804 'classNoSuffix' => true 805 ); 806 } 807 808 if (strcasecmp($type, 'vertical-align') == 0) { 809 $this->options['vertical-align'] = array( 810 'type' => 'choice', 811 'data' => array('top' => array('align-top'), 'middle' => array('align-middle'), 'bottom' => array('align-bottom')), 812 'default' => '', 813 'class' => true 814 ); 815 } 816 817 if (strcasecmp($type, 'links-match') == 0) { 818 $this->options['links-match'] = array( 819 'type' => 'boolean', 820 'default' => 'false', 821 'class' => true 822 ); 823 } 824 } 825 } 826 827 828 /* 829 * Find HTML tags in string. Parse tags options. Used in parsing subtags 830 * 831 * @param $tagName tagName to search for. Name is exclusive 832 * @param $content search within content 833 * @param $options parse options similar to syntax element options 834 * @param $hasEndTag tagName search also looks for an end tag 835 * @return array of tags containing 'options' => array of 'name' => 'value', 'content' => content inside the tag 836 */ 837 protected function findTags($tagName, $content, $options, $hasEndTag = true) 838 { 839 $items = array(); 840 $search = '/<(?i:' . $tagName . ')(.*?)>(.*?)<\/(?i:' . $tagName . ')>/s'; 841 842 if (!$hasEndTag) { 843 $search = '/<(?i:' . $tagName . ')(.*?)>/s'; 844 } 845 846 if (preg_match_all($search, $content, $match)) { 847 if (count($match) >= 2) { 848 for ($i = 0; $i < count($match[1]); $i++) { 849 $item = array('options' => array(), 'content' => $this->render_text($match[2][$i])); 850 851 $optionlist = preg_split('/\s(?=([^"]*"[^"]*")*[^"]*$)/', trim($match[1][$i])); 852 853 foreach ($optionlist as $option) { 854 $j = strpos($option, '='); 855 if ($j !== false) { 856 $value = substr($option, $j + 1); 857 858 if (substr($value, 0, 1) == '"') { $value = substr($value, 1); 859 } 860 if (substr($value, -1) == '"') { $value = substr($value, 0, -1); 861 } 862 863 $item['options'][substr($option, 0, $j)] = $value; 864 } else { 865 $item['options'][$option] = true; 866 } 867 } 868 869 $item['options'] = $this->cleanOptions($item['options'], $options); 870 871 $items[] = $item; 872 } 873 } 874 } 875 876 return $items; 877 } 878} 879