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 $customStyles = []; 324 325 foreach ($data as $optionKey => $optionValue) { 326 if (!array_key_exists($optionKey, $optionsCleaned)) { 327 if($optionValue === true && $this->customStyleExists($optionKey)) { 328 array_push($customStyles, $optionKey); 329 } 330 331 foreach ($options as $syntaxKey => $syntaxValue) { 332 if (array_key_exists('type', $options[$syntaxKey])) { 333 if (array_key_exists('data', $options[$syntaxKey]) && is_array($options[$syntaxKey]['data'])) { 334 foreach ($options[$syntaxKey]['data'] as $choiceKey => $choiceValue) { 335 if (is_array($choiceValue)) { 336 if (in_array($optionKey, $choiceValue)) { 337 $optionsCleaned[$syntaxKey] = $choiceKey; 338 } 339 } else { 340 if (strcasecmp($choiceValue, $optionKey) == 0) { 341 $optionsCleaned[$syntaxKey] = $choiceValue; 342 } 343 } 344 } 345 } 346 } 347 } 348 } 349 } 350 351 if(array_key_exists('type', $options) === true 352 && array_key_exists('type', $optionsCleaned) === false 353 && count($customStyles) > 0) { 354 $optionsCleaned['type'] = $customStyles[0]; 355 } 356 357 // Add in syntax options that are missing 358 foreach ($options as $optionKey => $optionValue) { 359 if (!array_key_exists($optionKey, $optionsCleaned)) { 360 if (array_key_exists('default', $options[$optionKey])) { 361 switch ($options[$optionKey]['type']) { 362 case 'boolean': 363 $optionsCleaned[$optionKey] = filter_var($options[$optionKey]['default'], FILTER_VALIDATE_BOOLEAN); 364 break; 365 case 'number': 366 $optionsCleaned[$optionKey] = filter_var($options[$optionKey]['default'], FILTER_VALIDATE_INT); 367 break; 368 default: 369 $optionsCleaned[$optionKey] = $options[$optionKey]['default']; 370 break; 371 } 372 } 373 } 374 } 375 376 return $optionsCleaned; 377 } 378 379 /* Lexer renderers */ 380 protected function render_lexer_enter(Doku_Renderer $renderer, $data) 381 { 382 } 383 protected function render_lexer_unmatched(Doku_Renderer $renderer, $data) 384 { 385 $renderer->doc .= $renderer->_xmlEntities($data); 386 } 387 protected function render_lexer_exit(Doku_Renderer $renderer, $data) 388 { 389 } 390 protected function render_lexer_special(Doku_Renderer $renderer, $data) 391 { 392 } 393 protected function render_lexer_match(Doku_Renderer $renderer, $data) 394 { 395 } 396 397 /* Renderer */ 398 public function render($mode, Doku_Renderer $renderer, $data) 399 { 400 if ($mode == 'xhtml' && $this->isDisabled() != true) { 401 list($state, $match) = $data; 402 403 switch ($state) { 404 case DOKU_LEXER_ENTER: 405 $this->render_lexer_enter($renderer, $match); 406 return true; 407 408 case DOKU_LEXER_UNMATCHED: 409 $this->render_lexer_unmatched($renderer, $match); 410 return true; 411 412 case DOKU_LEXER_MATCHED: 413 $this->render_lexer_match($renderer, $match); 414 return true; 415 416 case DOKU_LEXER_EXIT: 417 $this->render_lexer_exit($renderer, $match); 418 return true; 419 420 case DOKU_LEXER_SPECIAL: 421 $this->render_lexer_special($renderer, $match); 422 return true; 423 } 424 425 return true; 426 } 427 428 return false; 429 } 430 431 /* 432 * return a class list with mikiop- prefix 433 * 434 * @param $options options of syntax element. Options with key 'class'=true are automatically added 435 * @param $classes classes to build from options as array 436 * @param $inclAttr include class="" in the return string 437 * @param $optionsTemplate allow a different options template instead of $this->options (for findTags) 438 * @return a string of classes from options/classes variable 439 */ 440 public function buildClass($options = null, $classes = null, $inclAttr = false, $optionsTemplate = null) 441 { 442 $s = array(); 443 444 if (is_array($options)) { 445 if ($classes == null) { $classes = array(); 446 } 447 if ($optionsTemplate == null) { $optionsTemplate = $this->options; 448 } 449 450 foreach ($optionsTemplate as $key => $value) { 451 if (array_key_exists('class', $value) && $value['class'] == true) { 452 array_push($classes, $key); 453 } 454 } 455 456 foreach ($classes as $class) { 457 if (array_key_exists($class, $options) && $options[$class] !== false && $options[$class] != '') { 458 $prefix = $this->classPrefix; 459 460 if (array_key_exists($class, $optionsTemplate) && array_key_exists('prefix', $optionsTemplate[$class])) { 461 $prefix .= $optionsTemplate[$class]['prefix']; 462 } 463 464 if (array_key_exists($class, $optionsTemplate) && array_key_exists('classNoSuffix', $optionsTemplate[$class]) && $optionsTemplate[$class]['classNoSuffix'] == true) { 465 $s[] = $prefix . $class; 466 } else { 467 $s[] = $prefix . $class . ($options[$class] !== true ? '-' . $options[$class] : ''); 468 } 469 } 470 } 471 } 472 473 $s = implode(' ', $s); 474 if ($s != '') { $s = ' ' . $s; 475 } 476 477 if ($inclAttr) { $s = ' classes="' . $s . '"'; 478 } 479 480 return $s; 481 } 482 483 484 485 486 /* 487 * build style string 488 * 489 * @param $list style list as key => value. Empty values are not included 490 * @param $inclAttr include style="" in the return string 491 * @return style list string 492 */ 493 public function buildStyle($list, $inclAttr = false) 494 { 495 $s = ''; 496 497 if (is_array($list) && count($list) > 0) { 498 foreach ($list as $key => $value) { 499 if ($value != '') { 500 $s .= $key . ':' . $value . ';'; 501 } 502 } 503 } 504 505 if ($s != '' && $inclAttr) { 506 $s = ' style="' . $s . '"'; 507 } 508 509 return $s; 510 } 511 512 513 public function buildTooltipString($options) 514 { 515 $dataPlacement = 'top'; 516 $dataHtml = false; 517 $title = ''; 518 519 if ($options != null) { 520 if (array_key_exists('tooltip-html-top', $options) && $options['tooltip-html-top'] != '') { 521 $title = $options['tooltip-html-top']; 522 $dataPlacement = 'top'; 523 } 524 525 if (array_key_exists('tooltip-html-left', $options) && $options['tooltip-html-left'] != '') { 526 $title = $options['tooltip-html-left']; 527 $dataPlacement = 'left'; 528 } 529 530 if (array_key_exists('tooltip-html-bottom', $options) && $options['tooltip-html-bottom'] != '') { 531 $title = $options['tooltip-html-bottom']; 532 $dataPlacement = 'bottom'; 533 } 534 535 if (array_key_exists('tooltip-html-right', $options) && $options['tooltip-html-right'] != '') { 536 $title = $options['tooltip-html-right']; 537 $dataPlacement = 'right'; 538 } 539 540 if (array_key_exists('tooltip-top', $options) && $options['tooltip-top'] != '') { 541 $title = $options['tooltip-top']; 542 $dataPlacement = 'top'; 543 } 544 545 if (array_key_exists('tooltip-left', $options) && $options['tooltip-left'] != '') { 546 $title = $options['tooltip-left']; 547 $dataPlacement = 'left'; 548 } 549 550 if (array_key_exists('tooltip-bottom', $options) && $options['tooltip-bottom'] != '') { 551 $title = $options['tooltip-bottom']; 552 $dataPlacement = 'bottom'; 553 } 554 555 if (array_key_exists('tooltip-right', $options) && $options['tooltip-right'] != '') { 556 $title = $options['tooltip-right']; 557 $dataPlacement = 'right'; 558 } 559 560 if (array_key_exists('tooltip-html', $options) && $options['tooltip-html'] != '') { 561 $title = $options['tooltip-html']; 562 $dataPlacement = 'top'; 563 } 564 565 if (array_key_exists('tooltip', $options) && $options['tooltip'] != '') { 566 $title = $options['tooltip']; 567 $dataPlacement = 'top'; 568 } 569 } 570 571 if ($title != '') { 572 return ' data-toggle="tooltip" data-placement="' . $dataPlacement . '" ' . ($dataHtml == true ? 'data-html="true" ' : '') . 'title="' . $title . '" '; 573 } 574 575 return ''; 576 } 577 578 /* 579 * convert the URL to a DokuWiki media link (if required) 580 * 581 * @param $url url to parse 582 * @return url string 583 */ 584 public function buildMediaLink($url) 585 { 586 $i = strpos($url, '?'); 587 if ($i !== false) { $url = substr($url, 0, $i); 588 } 589 590 $url = preg_replace('/[^\da-zA-Z:_.-]+/', '', $url); 591 592 return (tpl_getMediaFile(array($url), false)); 593 } 594 595 596 /* 597 * returns either a url or dokuwiki link 598 * 599 * @param $url link to build from 600 * @return built link 601 */ 602 public function buildLink($url) 603 { 604 $i = strpos($url, '://'); 605 if ($i !== false || substr($url, 0, 1) == '#') { return $url; 606 } 607 608 return wl($url); 609 } 610 611 /* 612 * Call syntax renderer of mikio syntax plugin 613 * 614 * @param $renderer DokuWiki renderer object 615 * @param $className mikio syntax class to call 616 * @param $text unmatched text to pass outside of lexer. Only used when $lexer=MIKIO_LEXER_AUTO 617 * @param $data tag options to pass to syntax class. Runs through cleanOptions to validate first 618 * @param $lexer which lexer to call 619 */ 620 public function syntaxRender(Doku_Renderer $renderer, $className, $text, $data = null, $lexer = MIKIO_LEXER_AUTO) 621 { 622 $className = 'syntax_plugin_mikioplugin_' . str_replace('-', '', $className); 623 624 if (class_exists($className)) { 625 $class = new $className; 626 627 if (!is_array($data)) { $data = array(); 628 } 629 630 631 if (count($class->options) > 0) { 632 $data = $class->cleanOptions($data, $class->options); 633 } 634 635 switch ($lexer) { 636 case MIKIO_LEXER_AUTO: 637 if ($class->hasEndTag) { 638 if (method_exists($class, 'render_lexer_enter')) { $class->render_lexer_enter($renderer, $data); 639 } 640 $renderer->doc .= $text; 641 if (method_exists($class, 'render_lexer_exit')) { $class->render_lexer_exit($renderer, $data); 642 } 643 } else { 644 if (method_exists($class, 'render_lexer_special')) { $class->render_lexer_special($renderer, $data); 645 } 646 } 647 648 break; 649 case MIKIO_LEXER_ENTER: 650 if (method_exists($class, 'render_lexer_enter')) { $class->render_lexer_enter($renderer, $data); 651 } 652 break; 653 case MIKIO_LEXER_EXIT: 654 if (method_exists($class, 'render_lexer_exit')) { $class->render_lexer_exit($renderer, $data); 655 } 656 break; 657 case MIKIO_LEXER_SPECIAL: 658 if (method_exists($class, 'render_lexer_special')) { $class->render_lexer_special($renderer, $data); 659 } 660 break; 661 } 662 } 663 } 664 665 666 protected function callMikioTag($className, $data) 667 { 668 // $className = 'syntax_plugin_mikioplugin_'.$className; 669 670 671 // if(class_exists($className)) { 672 //$class = new $className; 673 if (!plugin_isdisabled('mikioplugin')) { 674 $class = plugin_load('syntax', 'mikioplugin_' . $className); 675 // echo '^^'.$className.'^^'; 676 677 678 if (method_exists($class, 'mikioCall')) { return $class->mikioCall($data); 679 } 680 } 681 682 // } 683 684 return ''; 685 } 686 687 688 protected function callMikioOptionDefault($className, $option) 689 { 690 $className = 'syntax_plugin_mikioplugin_' . $className; 691 692 if (class_exists($className)) { 693 $class = new $className; 694 695 if (array_key_exists($option, $class->options) && array_key_exists('default', $class->options[$option])) { 696 return $class->options[$option]['default']; 697 } 698 } 699 700 return ''; 701 } 702 703 704 protected function buildTooltip($text) 705 { 706 if ($text != '') { 707 return ' data-tooltip="' . $text . '"'; 708 } 709 710 return ''; 711 } 712 713 /* 714 * Create array with passed elements and include them if their values are not empty 715 * 716 * @param ... array items 717 */ 718 protected function arrayRemoveEmpties($items) 719 { 720 $result = array(); 721 722 foreach ($items as $key => $value) { 723 if ($value != '') { 724 $result[$key] = $value; 725 } 726 } 727 728 return $result; 729 } 730 731 public function getFirstArrayKey($data) 732 { 733 if (!function_exists('array_key_first')) { 734 foreach ($data as $key => $unused) { 735 return $key; 736 } 737 } 738 739 return array_key_first($data); 740 } 741 742 743 /* 744 * add common options to options 745 * 746 * @param $typelist common option to add 747 * @param $options save in options 748 */ 749 public function addCommonOptions($typelist) 750 { 751 $types = explode(' ', $typelist); 752 foreach ($types as $type) { 753 if (strcasecmp($type, 'shadow') == 0) { 754 $this->options['shadow'] = array( 755 'type' => 'choice', 756 'data' => array('large' => array('shadow-large', 'shadow-lg'), 'small' => array('shadow-small', 'shadow-sm'), true), 757 'default' => '', 758 'class' => true 759 ); 760 } 761 762 if (strcasecmp($type, 'width') == 0) { 763 $this->options['width'] = array( 764 'type' => 'size', 765 'default' => '' 766 ); 767 } 768 769 if (strcasecmp($type, 'height') == 0) { 770 $this->options['height'] = array( 771 'type' => 'size', 772 'default' => '' 773 ); 774 } 775 776 if (strcasecmp($type, 'text-color') == 0) { 777 $this->options['text-color'] = array( 778 'type' => 'color', 779 'default' => '' 780 ); 781 } 782 783 if (strcasecmp($type, 'type') == 0) { 784 $this->options['type'] = array( 785 'type' => 'text', 786 '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'), 787 'default' => '', 788 'class' => true 789 ); 790 } 791 792 if (strcasecmp($type, 'text-align') == 0) { 793 $this->options['text-align'] = array( 794 'type' => 'choice', 795 'data' => array('left' => array('text-left'), 'center' => array('text-center'), 'right' => array('text-right')), 796 'default' => '', 797 'class' => true 798 ); 799 } 800 801 if (strcasecmp($type, 'align') == 0) { 802 $this->options['align'] = array( 803 'type' => 'choice', 804 'data' => array('left' => array('align-left'), 'center' => array('align-center'), 'right' => array('align-right')), 805 'default' => '', 806 'class' => true 807 ); 808 } 809 810 if (strcasecmp($type, 'tooltip') == 0) { 811 $this->options['tooltip'] = array( 812 'type' => 'text', 813 'default' => '', 814 'class' => true, 815 'classNoSuffix' => true 816 ); 817 } 818 819 if (strcasecmp($type, 'vertical-align') == 0) { 820 $this->options['vertical-align'] = array( 821 'type' => 'choice', 822 'data' => array('top' => array('align-top'), 'middle' => array('align-middle'), 'bottom' => array('align-bottom')), 823 'default' => '', 824 'class' => true 825 ); 826 } 827 828 if (strcasecmp($type, 'links-match') == 0) { 829 $this->options['links-match'] = array( 830 'type' => 'boolean', 831 'default' => 'false', 832 'class' => true 833 ); 834 } 835 } 836 } 837 838 839 /* 840 * Find HTML tags in string. Parse tags options. Used in parsing subtags 841 * 842 * @param $tagName tagName to search for. Name is exclusive 843 * @param $content search within content 844 * @param $options parse options similar to syntax element options 845 * @param $hasEndTag tagName search also looks for an end tag 846 * @return array of tags containing 'options' => array of 'name' => 'value', 'content' => content inside the tag 847 */ 848 protected function findTags($tagName, $content, $options, $hasEndTag = true) 849 { 850 $items = array(); 851 $search = '/<(?i:' . $tagName . ')(.*?)>(.*?)<\/(?i:' . $tagName . ')>/s'; 852 853 if (!$hasEndTag) { 854 $search = '/<(?i:' . $tagName . ')(.*?)>/s'; 855 } 856 857 if (preg_match_all($search, $content, $match)) { 858 if (count($match) >= 2) { 859 for ($i = 0; $i < count($match[1]); $i++) { 860 $item = array('options' => array(), 'content' => $this->render_text($match[2][$i])); 861 862 $optionlist = preg_split('/\s(?=([^"]*"[^"]*")*[^"]*$)/', trim($match[1][$i])); 863 864 foreach ($optionlist as $option) { 865 $j = strpos($option, '='); 866 if ($j !== false) { 867 $value = substr($option, $j + 1); 868 869 if (substr($value, 0, 1) == '"') { $value = substr($value, 1); 870 } 871 if (substr($value, -1) == '"') { $value = substr($value, 0, -1); 872 } 873 874 $item['options'][substr($option, 0, $j)] = $value; 875 } else { 876 $item['options'][$option] = true; 877 } 878 } 879 880 $item['options'] = $this->cleanOptions($item['options'], $options); 881 882 $items[] = $item; 883 } 884 } 885 } 886 887 return $items; 888 } 889 890 /* 891 * Check if a custom style exists in styles.less 892 * 893 * @param $name The style name to search foe 894 * @return true if the style name exists 895 */ 896 protected function customStyleExists($name) 897 { 898 $stylePath = __DIR__.'/../styles/styles.less'; 899 900 if(file_exists($stylePath)) { 901 $styleData = file_get_contents($stylePath); 902 $searchString = '._mikiop-custom-type('.$name.');'; 903 904 return (strpos($styleData, $searchString) !== false); 905 } 906 907 return false; 908 } 909} 910