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