| [ Index ] |
|
Code source de Dotclear 2.0-beta6 |
1 <?php 2 ########################################################### 3 # /!\ /!\ /!\ EDIT THIS FILE IN UTF8 /!\ /!\ /!\ # 4 ########################################################### 5 6 # ***** BEGIN LICENSE BLOCK ***** 7 # This file is part of Clearbricks. 8 # Copyright (c) 2006 Olivier Meunier and contributors. All rights 9 # reserved. 10 # 11 # Clearbricks is free software; you can redistribute it and/or modify 12 # it under the terms of the GNU General Public License as published by 13 # the Free Software Foundation; either version 2 of the License, or 14 # (at your option) any later version. 15 # 16 # Clearbricks is distributed in the hope that it will be useful, 17 # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 # GNU General Public License for more details. 20 # 21 # You should have received a copy of the GNU General Public License 22 # along with Clearbricks; if not, write to the Free Software 23 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 24 # 25 # ***** END LICENSE BLOCK ***** 26 # 27 # Contributor(s): 28 # Stephanie Booth 29 # Mathieu Pillard 30 # Christophe Bonijol 31 # Jean-Charles Bagneris 32 # Nicolas Chachereau 33 # Jérôme Lipowicz 34 # 35 # Version : 3.1d 36 # Release date : Monday 2004-06-06 37 38 # History : 39 # 40 # 3.2.4 41 # => Auto links 42 # => Code cleanup 43 # 44 # 3.2.3 45 # Olivier 46 # => PHP5 Strict 47 # 48 # 3.2.2 49 # Olivier 50 # => Changement de la gestion des URL spéciales 51 # 52 # 3.2.1 53 # Olivier 54 # => Changement syntaxe des macros 55 # 56 # 3.2 57 # Olivier 58 # => Changement de fonctionnement des macros 59 # => Passage de fonctions externes pour les macros et les mots wiki 60 # 61 # 3.1d 62 # Jérôme Lipowicz 63 # => antispam 64 # Olivier 65 # => centrage d'image 66 # 67 # 3.1c 68 # Olivier 69 # => Possibilité d'échaper les | dans les marqueurs avec \ 70 # 71 # 3.1b 72 # Nicolas Chachereau 73 # => Changement de regexp pour la correction syntaxique 74 # 75 # 3.1a 76 # Olivier 77 # => Bug du Call-time pass-by-reference 78 # 79 # 3.1 80 # Olivier 81 # => Ajout des macros «««..»»» 82 # => Ajout des blocs vides øøø 83 # => Ajout du niveau de titre paramétrable 84 # => Option de blocage du parseur dans les <pre> 85 # => Titres au format setext (experimental, désactivé) 86 # 87 # 3.0 88 # Olivier => Récriture du parseur inline, plus d'erreur XHTML 89 # => Ajout d'une vérification d'intégrité pour les listes 90 # => Les acronymes sont maintenant dans un fichier texte 91 # => Ajout d'un tag images ((..)), del --..-- et ins ++..++ 92 # => Plus possible de faire des liens JS [lien|javascript:...] 93 # => Ajout des notes de bas de page §§...§§ 94 # => Ajout des mots wiki 95 # 96 # 2.5 97 # Olivier => Récriture du code, plus besoin du saut de ligne entre blocs != 98 # 99 # 2.0 100 # Stephanie => correction des PCRE et ajout de fonctionnalités 101 # Mathieu => ajout du strip-tags, implementation des options, reconnaissance automatique d'url, etc. 102 # Olivier => chagement de active_link en active_urls 103 # => ajout des options pour les blocs 104 # => intégration de l'aide dans le code, avec les options 105 # => début de quelque chose pour la reconnaissance auto d'url (avec Mat) 106 107 # TODO : 108 # Mathieu => active_wiki_urls (modifier wikiParseUrl ?) 109 # => active_auto_urls 110 # 111 # * => ajouter des options. 112 # => trouver un meilleur nom pour active_link ? (pour le jour ou ca sera tellement une usine 113 # a gaz que on generera des tags <link> :) 114 # 115 116 # Wiki2xhtml 117 118 class wiki2xhtml 119 { 120 public $__version__ = '3.2.4'; 121 122 public $T; 123 public $opt; 124 public $line; 125 public $acro_table; 126 public $foot_notes; 127 public $macros; 128 public $functions; 129 130 public $tags; 131 public $open_tags; 132 public $close_tags; 133 public $custom_tags = array(); 134 public $all_tags; 135 public $tag_pattern; 136 public $escape_table; 137 public $allowed_inline = array(); 138 139 function __construct() 140 { 141 # Mise en place des options 142 $this->setOpt('active_title',1); # Activation des titres !!! 143 $this->setOpt('active_setext_title',0); # Activation des titres setext (EXPERIMENTAL) 144 $this->setOpt('active_hr',1); # Activation des <hr /> 145 $this->setOpt('active_lists',1); # Activation des listes 146 $this->setOpt('active_quote',1); # Activation du <blockquote> 147 $this->setOpt('active_pre',1); # Activation du <pre> 148 $this->setOpt('active_empty',1); # Activation du bloc vide øøø 149 $this->setOpt('active_auto_urls',0); # Activation de la reconnaissance d'url 150 $this->setOpt('active_auto_br',0); # Activation du saut de ligne automatique (dans les paragraphes) 151 $this->setOpt('active_antispam',1); # Activation de l'antispam pour les emails 152 $this->setOpt('active_urls',1); # Activation des liens [] 153 $this->setOpt('active_auto_img',1); # Activation des images automatiques dans les liens [] 154 $this->setOpt('active_img',1); # Activation des images (()) 155 $this->setOpt('active_anchor',1); # Activation des ancres ~...~ 156 $this->setOpt('active_em',1); # Activation du <em> ''...'' 157 $this->setOpt('active_strong',1); # Activation du <strong> __...__ 158 $this->setOpt('active_br',1); # Activation du <br /> %%% 159 $this->setOpt('active_q',1); # Activation du <q> {{...}} 160 $this->setOpt('active_code',1); # Activation du <code> @@...@@ 161 $this->setOpt('active_acronym',1); # Activation des acronymes 162 $this->setOpt('active_ins',1); # Activation des ins ++..++ 163 $this->setOpt('active_del',1); # Activation des del --..-- 164 $this->setOpt('active_footnotes',1); # Activation des notes de bas de page 165 $this->setOpt('active_wikiwords',0); # Activation des mots wiki 166 $this->setOpt('active_macros',1); # Activation des macros {{{ }}} 167 168 $this->setOpt('parse_pre',1); # Parser l'intérieur de blocs <pre> ? 169 170 $this->setOpt('active_fr_syntax',1); # Corrections syntaxe FR 171 172 $this->setOpt('first_title_level',3); # Premier niveau de titre <h..> 173 174 $this->setOpt('note_prefix','wiki-footnote'); 175 $this->setOpt('note_str','<div class="footnotes"><h4>Notes</h4>%s</div>'); 176 $this->setOpt('words_pattern', 177 '((?<![A-Za-z0-9])([A-Z][a-z]+){2,}(?![A-Za-z0-9]))'); 178 179 $this->setOpt('auto_url_pattern', 180 '%(?<![\[\|])(http://|https://|ftp://|news:)([^"\s\)!]+)%msu'); 181 182 $this->setOpt('acronyms_file',dirname(__FILE__).'/acronyms.txt'); 183 184 $this->acro_table = $this->__getAcronyms(); 185 $this->foot_notes = array(); 186 $this->functions = array(); 187 $this->macros = array(); 188 189 $this->registerFunction('macro:html',array($this,'__macroHTML')); 190 } 191 192 function setOpt($option, $value) 193 { 194 $this->opt[$option] = $value; 195 } 196 197 function setOpts($options) 198 { 199 if (!is_array($options)) { 200 return; 201 } 202 203 foreach ($options as $k => $v) { 204 $this->opt[$k] = $v; 205 } 206 } 207 208 function getOpt($option) 209 { 210 return (!empty($this->opt[$option])) ? $this->opt[$option] : false; 211 } 212 213 function registerFunction($type,$name) 214 { 215 if (is_callable($name)) { 216 $this->functions[$type] = $name; 217 } 218 } 219 220 function transform($in) 221 { 222 # Initialisation des tags 223 $this->__initTags(); 224 $this->foot_notes = array(); 225 226 # Récupération des macros 227 if ($this->getOpt('active_macros')) { 228 $in = preg_replace('#^///(.*?)///($|\r)#mse',"\\\$this->__getMacro('\\1')",$in); 229 } 230 231 # Vérification du niveau de titre 232 if ($this->getOpt('first_title_level') > 4) { 233 $this->setOpt('first_title_level',4); 234 } 235 236 $res = str_replace("\r", '', $in); 237 238 $escape_pattern = array(); 239 240 # traitement des titres à la setext 241 if ($this->getOpt('active_setext_title') && $this->getOpt('active_title')) { 242 $res = preg_replace('/^(.*)\n[=]{5,}$/m','!!!$1',$res); 243 $res = preg_replace('/^(.*)\n[-]{5,}$/m','!!$1',$res); 244 } 245 246 # Transformation des mots Wiki 247 if ($this->getOpt('active_wikiwords') && $this->getOpt('words_pattern')) { 248 $res = preg_replace('/'.$this->getOpt('words_pattern').'/msu','¶¶¶$1¶¶¶',$res); 249 } 250 251 # Transformation des URLs automatiques 252 if ($this->getOpt('active_auto_urls')) 253 { 254 $active_urls = $this->getOpt('active_urls'); 255 256 $this->setOpt('active_urls',1); 257 $this->__initTags(); 258 259 # If urls are not active, escape URLs tags 260 if (!$active_urls) 261 { 262 $res = preg_replace( 263 '%(?<!\\\\)(['.preg_quote(implode('',$this->tags['a'])).'])%msU', 264 '\\\$1',$res 265 ); 266 } 267 268 # Transforms urls 269 $res = preg_replace($this->getOpt('auto_url_pattern'),'[$1$2]',$res); 270 271 } 272 273 $this->T = explode("\n",$res); 274 $this->T[] = ''; 275 276 # Parse les blocs 277 $res = $this->__parseBlocks(); 278 279 # Line break 280 if ($this->getOpt('active_br')) { 281 $res = preg_replace('/(?<!\\\)%%%/', '<br />', $res); 282 $escape_pattern[] = '%%%'; 283 } 284 285 # Nettoyage des \s en trop 286 $res = preg_replace('/([\s]+)(<\/p>|<\/li>|<\/pre>)/', '$2', $res); 287 $res = preg_replace('/(<li>)([\s]+)/', '$1', $res); 288 289 # On vire les escapes 290 if (!empty($escape_pattern)) { 291 $res = preg_replace('/\\\('.implode('|',$escape_pattern).')/','$1',$res); 292 } 293 294 # On vire les ¶¶¶MotWiki¶¶¶ qui sont resté (dans les url...) 295 if ($this->getOpt('active_wikiwords') && $this->getOpt('words_pattern')) { 296 $res = preg_replace('/¶¶¶'.$this->getOpt('words_pattern').'¶¶¶/msu','$1',$res); 297 } 298 299 # On remet les macros 300 if ($this->getOpt('active_macros')) { 301 $res = preg_replace('/^##########MACRO#([0-9]+)#$/mse','\$this->__putMacro("$1")',$res); 302 } 303 304 # Auto line break dans les paragraphes 305 if ($this->getOpt('active_auto_br')) { 306 $res = preg_replace_callback('%(<p>)(.*?)(</p>)%msu',array($this,'__autoBR'),$res); 307 } 308 309 # On ajoute les notes 310 if (count($this->foot_notes) > 0) 311 { 312 $res_notes = ''; 313 $i = 1; 314 foreach ($this->foot_notes as $k => $v) { 315 $res_notes .= "\n".'<p>[<a href="#rev-'.$k.'" id="'.$k.'">'.$i.'</a>] '.$v.'</p>'; 316 $i++; 317 } 318 $res .= sprintf("\n".$this->getOpt('note_str')."\n",$res_notes); 319 } 320 321 return $res; 322 } 323 324 /* PRIVATE 325 --------------------------------------------------- */ 326 327 function __initTags() 328 { 329 $tags = array( 330 'em' => array("''","''"), 331 'strong' => array('__','__'), 332 'acronym' => array('??','??'), 333 'a' => array('[',']'), 334 'img' => array('((','))'), 335 'q' => array('{{','}}'), 336 'code' => array('@@','@@'), 337 'anchor' => array('~','~'), 338 'del' => array('--','--'), 339 'ins' => array('++','++'), 340 'note' => array('$$','$$'), 341 'word' => array('¶¶¶','¶¶¶') 342 ); 343 344 $this->tags = array_merge($tags,$this->custom_tags); 345 346 # Suppression des tags selon les options 347 if (!$this->getOpt('active_urls')) { 348 unset($this->tags['a']); 349 } 350 if (!$this->getOpt('active_img')) { 351 unset($this->tags['img']); 352 } 353 if (!$this->getOpt('active_anchor')) { 354 unset($this->tags['anchor']); 355 } 356 if (!$this->getOpt('active_em')) { 357 unset($this->tags['em']); 358 } 359 if (!$this->getOpt('active_strong')) { 360 unset($this->tags['strong']); 361 } 362 if (!$this->getOpt('active_q')) { 363 unset($this->tags['q']); 364 } 365 if (!$this->getOpt('active_code')) { 366 unset($this->tags['code']); 367 } 368 if (!$this->getOpt('active_acronym')) { 369 unset($this->tags['acronym']); 370 } 371 if (!$this->getOpt('active_ins')) { 372 unset($this->tags['ins']); 373 } 374 if (!$this->getOpt('active_del')) { 375 unset($this->tags['del']); 376 } 377 if (!$this->getOpt('active_footnotes')) { 378 unset($this->tags['note']); 379 } 380 if (!$this->getOpt('active_wikiwords')) { 381 unset($this->tags['word']); 382 } 383 384 $this->open_tags = $this->__getTags(); 385 $this->close_tags = $this->__getTags(false); 386 $this->all_tags = $this->__getAllTags(); 387 $this->tag_pattern = $this->__getTagsPattern(); 388 389 $this->escape_table = $this->all_tags; 390 array_walk($this->escape_table,create_function('&$a','$a = \'\\\\\'.$a;')); 391 } 392 393 function __getTags($open=true) 394 { 395 $res = array(); 396 foreach ($this->tags as $k => $v) { 397 $res[$k] = ($open) ? $v[0] : $v[1]; 398 } 399 return $res; 400 } 401 402 function __getAllTags() 403 { 404 $res = array(); 405 foreach ($this->tags as $v) { 406 $res[] = $v[0]; 407 $res[] = $v[1]; 408 } 409 return array_values(array_unique($res)); 410 } 411 412 function __getTagsPattern($escape=false) 413 { 414 $res = $this->all_tags; 415 array_walk($res,create_function('&$a','$a = preg_quote($a,"/");')); 416 417 if (!$escape) { 418 return '/(?<!\\\)('.implode('|',$res).')/'; 419 } else { 420 return '('.implode('|',$res).')'; 421 } 422 } 423 424 /* Blocs 425 --------------------------------------------------- */ 426 function __parseBlocks() 427 { 428 $mode = $type = NULL; 429 $res = ''; 430 $max = count($this->T); 431 432 for ($i=0; $i<$max; $i++) 433 { 434 $pre_mode = $mode; 435 $pre_type = $type; 436 $end = ($i+1 == $max); 437 438 $line = $this->__getLine($i,$type,$mode); 439 440 if ($type != 'pre' || $this->getOpt('parse_pre')) { 441 $line = $this->__inlineWalk($line); 442 } 443 444 $res .= $this->__closeLine($type,$mode,$pre_type,$pre_mode); 445 $res .= $this->__openLine($type,$mode,$pre_type,$pre_mode); 446 447 # P dans les blockquotes 448 if ($type == 'blockquote' && trim($line) == '' && $pre_type == $type) { 449 $res .= "</p>\n<p>"; 450 } 451 452 # Correction de la syntaxe FR dans tous sauf pre et hr 453 # Sur idée de Christophe Bonijol 454 # Changement de regex (Nicolas Chachereau) 455 if ($this->getOpt('active_fr_syntax') && $type != NULL && $type != 'pre' && $type != 'hr') { 456 $line = preg_replace('%[ ]+([:?!;\x{00BB}](\s|$))%u',' $1',$line); 457 $line = preg_replace('%(\x{00AB})[ ]+%u','$1 ',$line); 458 } 459 460 $res .= $line; 461 } 462 463 return trim($res); 464 } 465 466 function __getLine($i,&$type,&$mode) 467 { 468 $pre_type = $type; 469 $pre_mode = $mode; 470 $type = $mode = NULL; 471 472 if (empty($this->T[$i])) { 473 return false; 474 } 475 476 $line = htmlspecialchars($this->T[$i],ENT_NOQUOTES); 477 478 # Ligne vide 479 if (empty($line)) 480 { 481 $type = NULL; 482 } 483 elseif ($this->getOpt('active_empty') && preg_match('/^øøø(.*)$/',$line,$cap)) 484 { 485 $type = NULL; 486 $line = trim($cap[1]); 487 } 488 # Titre 489 elseif ($this->getOpt('active_title') && preg_match('/^([!]{1,4})(.*)$/',$line,$cap)) 490 { 491 $type = 'title'; 492 $mode = strlen($cap[1]); 493 $line = trim($cap[2]); 494 } 495 # Ligne HR 496 elseif ($this->getOpt('active_hr') && preg_match('/^[-]{4}[- ]*$/',$line)) 497 { 498 $type = 'hr'; 499 $line = NULL; 500 } 501 # Blockquote 502 elseif ($this->getOpt('active_quote') && preg_match('/^(>|;:)(.*)$/',$line,$cap)) 503 { 504 $type = 'blockquote'; 505 $line = trim($cap[2]); 506 } 507 # Liste 508 elseif ($this->getOpt('active_lists') && preg_match('/^([*#]+)(.*)$/',$line,$cap)) 509 { 510 $type = 'list'; 511 $mode = $cap[1]; 512 $valid = true; 513 514 # Vérification d'intégrité 515 $dl = ($type != $pre_type) ? 0 : strlen($pre_mode); 516 $d = strlen($mode); 517 $delta = $d-$dl; 518 519 if ($delta < 0 && strpos($pre_mode,$mode) !== 0) { 520 $valid = false; 521 } 522 if ($delta > 0 && $type == $pre_type && strpos($mode,$pre_mode) !== 0) { 523 $valid = false; 524 } 525 if ($delta == 0 && $mode != $pre_mode) { 526 $valid = false; 527 } 528 if ($delta > 1) { 529 $valid = false; 530 } 531 532 if (!$valid) { 533 $type = 'p'; 534 $mode = NULL; 535 $line = '<br />'.$line; 536 } else { 537 $line = trim($cap[2]); 538 } 539 } 540 # Préformaté 541 elseif ($this->getOpt('active_pre') && preg_match('/^[ ]{1}(.*)$/',$line,$cap)) 542 { 543 $type = 'pre'; 544 $line = $cap[1]; 545 } 546 # Paragraphe 547 else { 548 $type = 'p'; 549 $line = trim($line); 550 } 551 552 return $line; 553 } 554 555 function __openLine($type,$mode,$pre_type,$pre_mode) 556 { 557 $open = ($type != $pre_type); 558 559 if ($open && $type == 'p') 560 { 561 return "\n<p>"; 562 } 563 elseif ($open && $type == 'blockquote') 564 { 565 return "\n<blockquote><p>"; 566 } 567 elseif (($open || $mode != $pre_mode) && $type == 'title') 568 { 569 $fl = $this->getOpt('first_title_level'); 570 $fl = $fl+3; 571 $l = $fl-$mode; 572 return "\n<h".($l).'>'; 573 } 574 elseif ($open && $type == 'pre') 575 { 576 return "\n<pre>"; 577 } 578 elseif ($open && $type == 'hr') 579 { 580 return "\n<hr />"; 581 } 582 elseif ($type == 'list') 583 { 584 $dl = ($open) ? 0 : strlen($pre_mode); 585 $d = strlen($mode); 586 $delta = $d-$dl; 587 $res = ''; 588 589 if($delta > 0) { 590 if(substr($mode, -1, 1) == '*') { 591 $res .= "<ul>\n"; 592 } else { 593 $res .= "<ol>\n"; 594 } 595 } elseif ($delta < 0) { 596 $res .= "</li>\n"; 597 for($j = 0; $j < abs($delta); $j++) { 598 if (substr($pre_mode,(0 - $j - 1), 1) == '*') { 599 $res .= "</ul>\n</li>\n"; 600 } else { 601 $res .= "</ol>\n</li>\n"; 602 } 603 } 604 } else { 605 $res .= "</li>\n"; 606 } 607 608 return $res."<li>"; 609 } 610 else 611 { 612 return NULL; 613 } 614 } 615 616 function __closeLine($type,$mode,$pre_type,$pre_mode) 617 { 618 $close = ($type != $pre_type); 619 620 if ($close && $pre_type == 'p') 621 { 622 return "</p>\n"; 623 } 624 elseif ($close && $pre_type == 'blockquote') 625 { 626 return "</p></blockquote>\n"; 627 } 628 elseif (($close || $mode != $pre_mode) && $pre_type == 'title') 629 { 630 $fl = $this->getOpt('first_title_level'); 631 $fl = $fl+3; 632 $l = $fl-$pre_mode; 633 return '</h'.($l).">\n"; 634 } 635 elseif ($close && $pre_type == 'pre') 636 { 637 return "</pre>\n"; 638 } 639 elseif ($close && $pre_type == 'list') 640 { 641 $res = ''; 642 for($j = 0; $j < strlen($pre_mode); $j++) { 643 if(substr($pre_mode,(0 - $j - 1), 1) == '*') { 644 $res .= "</li>\n</ul>"; 645 } else { 646 $res .= "</li>\n</ol>"; 647 } 648 } 649 return $res; 650 } 651 else 652 { 653 return "\n"; 654 } 655 } 656 657 658 /* Inline 659 --------------------------------------------------- */ 660 function __inlineWalk($str,$allow_only=NULL) 661 { 662 $tree = preg_split($this->tag_pattern,$str,-1,PREG_SPLIT_DELIM_CAPTURE); 663 664 $res = ''; 665 for ($i=0; $i<count($tree); $i++) 666 { 667 $attr = ''; 668 669 if (in_array($tree[$i],array_values($this->open_tags)) && 670 ($allow_only == NULL || in_array(array_search($tree[$i],$this->open_tags),$allow_only))) 671 { 672 $tag = array_search($tree[$i],$this->open_tags); 673 $tag_type = 'open'; 674 675 if (($tidy = $this->__makeTag($tree,$tag,$i,$i,$attr,$tag_type)) !== false) 676 { 677 if ($tag != '') { 678 $res .= '<'.$tag.$attr; 679 $res .= ($tag_type == 'open') ? '>' : ' />'; 680 } 681 $res .= $tidy; 682 } 683 else 684 { 685 $res .= $tree[$i]; 686 } 687 } 688 else 689 { 690 $res .= $tree[$i]; 691 } 692 } 693 694 # Suppression des echappements 695 $res = str_replace($this->escape_table,$this->all_tags,$res); 696 697 return $res; 698 } 699 700 function __makeTag(&$tree,&$tag,$position,&$j,&$attr,&$type) 701 { 702 $res = ''; 703 $closed = false; 704 705 $itag = $this->close_tags[$tag]; 706 707 # Recherche fermeture 708 for ($i=$position+1;$i<count($tree);$i++) 709 { 710 if ($tree[$i] == $itag) 711 { 712 $closed = true; 713 break; 714 } 715 } 716 717 # Résultat 718 if ($closed) 719 { 720 for ($i=$position+1;$i<count($tree);$i++) 721 { 722 if ($tree[$i] != $itag) 723 { 724 $res .= $tree[$i]; 725 } 726 else 727 { 728 switch ($tag) 729 { 730 case 'a': 731 $res = $this->__parseLink($res,$tag,$attr,$type); 732 break; 733 case 'img': 734 $type = 'close'; 735 $res = $this->__parseImg($res,$attr); 736 break; 737 case 'acronym': 738 $res = $this->__parseAcronym($res,$attr); 739 break; 740 case 'q': 741 $res = $this->__parseQ($res,$attr); 742 break; 743 case 'anchor': 744 $tag = 'a'; 745 $res = $this->__parseAnchor($res,$attr); 746 break; 747 case 'note': 748 $tag = ''; 749 $res = $this->__parseNote($res); 750 break; 751 case 'word': 752 $res = $this->parseWikiWord($res,$tag,$attr,$type); 753 break; 754 default : 755 $res = $this->__inlineWalk($res); 756 break; 757 } 758 759 if ($type == 'open' && $tag != '') { 760 $res .= '</'.$tag.'>'; 761 } 762 $j = $i; 763 break; 764 } 765 } 766 767 return $res; 768 } 769 else 770 { 771 return false; 772 } 773 } 774 775 function __splitTagsAttr($str) 776 { 777 $res = preg_split('/(?<!\\\)\|/',$str); 778 779 foreach ($res as $k => $v) { 780 $res[$k] = str_replace("\|",'|',$v); 781 } 782 783 return $res; 784 } 785 786 # Antispam (Jérôme Lipowicz) 787 function __antiSpam($str) 788 { 789 $encoded = bin2hex($str); 790 $encoded = chunk_split($encoded, 2, '%'); 791 $encoded = '%'.substr($encoded, 0, strlen($encoded) - 1); 792 return $encoded; 793 } 794 795 function __parseLink($str,&$tag,&$attr,&$type) 796 { 797 $n_str = $this->__inlineWalk($str,array('acronym','img')); 798 $data = $this->__splitTagsAttr($n_str); 799 $no_image = false; 800 801 # Only URL in data 802 if (count($data) == 1) 803 { 804 $url = trim($str); 805 $content = strlen($url) > 35 ? substr($url,0,35).'...' : $url; 806 $lang = ''; 807 $title = $url; 808 } 809 elseif (count($data) > 1) 810 { 811 $url = trim($data[1]); 812 $content = $data[0]; 813 $lang = (!empty($data[2])) ? $this->protectAttr($data[2],true) : ''; 814 $title = (!empty($data[3])) ? $data[3] : ''; 815 $no_image = (!empty($data[4])) ? (boolean) $data[4] : false; 816 } 817 818 # Remplacement si URL spéciale 819 $this->__specialUrls($url,$content,$lang,$title); 820 821 # On vire les dans l'url 822 $url = str_replace(' ',' ',$url); 823 824 if (ereg('^(.+)[.](gif|jpg|jpeg|png)$', $url) && !$no_image && $this->getOpt('active_auto_img')) 825 { 826 # On ajoute les dimensions de l'image si locale 827 # Idée de Stephanie 828 $img_size = NULL; 829 if (!ereg('[a-zA-Z]+://', $url)) { 830 if (ereg('^/',$url)) { 831 $path_img = $_SERVER['DOCUMENT_ROOT'] . $url; 832 } else { 833 $path_img = $url; 834 } 835 836 $img_size = @getimagesize($path_img); 837 } 838 839 $attr = ' src="'.$this->protectAttr($this->protectUrls($url)).'"'. 840 $attr .= (count($data) > 1) ? ' alt="'.$this->protectAttr($content).'"' : ' alt=""'; 841 $attr .= ($lang) ? ' lang="'.$lang.'"' : ''; 842 $attr .= ($title) ? ' title="'.$this->protectAttr($title).'"' : ''; 843 $attr .= (is_array($img_size)) ? ' '.$img_size[3] : ''; 844 845 $tag = 'img'; 846 $type = 'close'; 847 return NULL; 848 } 849 else 850 { 851 if ($this->getOpt('active_antispam') && preg_match('/^mailto:/',$url)) { 852 $content = $content == $url ? preg_replace('%^mailto:%','',$content) : $content; 853 $url = 'mailto:'.$this->__antiSpam(substr($url,7)); 854 855 } 856 857 $attr = ' href="'.$this->protectAttr($this->protectUrls($url)).'"'; 858 $attr .= ($lang) ? ' hreflang="'.$lang.'"' : ''; 859 $attr .= ($title) ? ' title="'.$this->protectAttr($title).'"' : ''; 860 861 return $content; 862 } 863 } 864 865 function __specialUrls(&$url,&$content,&$lang,&$title) 866 { 867 foreach ($this->functions as $k => $v) 868 { 869 if (strpos($k,'url:') === 0 && strpos($url,substr($k,4)) === 0) 870 { 871 $res = call_user_func($v,$url,$content); 872 873 $url = isset($res['url']) ? $res['url'] : $url; 874 $content = isset($res['content']) ? $res['content'] : $content; 875 $lang = isset($res['lang']) ? $res['lang'] : $lang; 876 $title = isset($res['title']) ? $res['title'] : $title; 877 878 break; 879 } 880 } 881 } 882 883 function __parseImg($str,&$attr) 884 { 885 $data = $this->__splitTagsAttr($str); 886 887 $alt = ''; 888 $url = $data[0]; 889 if (!empty($data[1])) { 890 $alt = $data[1]; 891 } 892 893 $attr = ' src="'.$this->protectAttr($this->protectUrls($url)).'"'; 894 $attr .= ' alt="'.$this->protectAttr($alt).'"'; 895 896 if (!empty($data[2])) { 897 $data[2] = strtoupper($data[2]); 898 if ($data[2] == 'G' || $data[2] == 'L') { 899 $attr .= ' style="float:left; margin: 0 1em 1em 0;"'; 900 } elseif ($data[2] == 'D' || $data[2] == 'R') { 901 $attr .= ' style="float:right; margin: 0 0 1em 1em;"'; 902 } elseif ($data[2] == 'C') { 903 $attr .= ' style="display:block; margin:0 auto;"'; 904 } 905 } 906 907 if (!empty($data[3])) { 908 $attr .= ' longdesc="'.$this->protectAttr($data[3]).'"'; 909 } 910 911 return NULL; 912 } 913 914 function __parseQ($str,&$attr) 915 { 916 $str = $this->__inlineWalk($str); 917 $data = $this->__splitTagsAttr($str); 918 919 $content = $data[0]; 920 $lang = (!empty($data[1])) ? $this->protectAttr($data[1],true) : ''; 921 922 $attr .= (!empty($lang)) ? ' lang="'.$lang.'"' : ''; 923 $attr .= (!empty($data[2])) ? ' cite="'.$this->protectAttr($this->protectUrls($data[2])).'"' : ''; 924 925 return $content; 926 } 927 928 function __parseAnchor($str,&$attr) 929 { 930 $name = $this->protectAttr($str,true); 931 932 if ($name != '') { 933 $attr = ' name="'.$name.'"'; 934 } 935 return null; 936 } 937 938 function __parseNote($str) 939 { 940 $i = count($this->foot_notes)+1; 941 $id = $this->getOpt('note_prefix').'-'.$i; 942 $this->foot_notes[$id] = $this->__inlineWalk($str); 943 return '<sup>\[<a href="#'.$id.'" id="rev-'.$id.'">'.$i.'</a>\]</sup>'; 944 } 945 946 # Obtenir un acronyme 947 function __parseAcronym($str,&$attr) 948 { 949 $data = $this->__splitTagsAttr($str); 950 951 $acronym = $data[0]; 952 $title = $lang = ''; 953 954 if (count($data) > 1) 955 { 956 $title = $data[1]; 957 $lang = (!empty($data[2])) ? $this->protectAttr($data[2],true) : ''; 958 } 959 960 if ($title == '' && !empty($this->acro_table[$acronym])) { 961 $title = $this->acro_table[$acronym]; 962 } 963 964 $attr = ($title) ? ' title="'.$this->protectAttr($title).'"' : ''; 965 $attr .= ($lang) ? ' lang="'.$lang.'"' : ''; 966 967 return $acronym; 968 } 969 970 # Définition des acronymes, dans le fichier acronyms.txt 971 function __getAcronyms() 972 { 973 $file = $this->getOpt('acronyms_file'); 974 $res = array(); 975 976 if (file_exists($file)) 977 { 978 if (($fc = @file($file)) !== false) 979 { 980 foreach ($fc as $v) 981 { 982 $v = trim($v); 983 if ($v != '') 984 { 985 $p = strpos($v,':'); 986 $K = (string) trim(substr($v,0,$p)); 987 $V = (string) trim(substr($v,($p+1))); 988 989 if ($K) { 990 $res[$K] = $V; 991 } 992 } 993 } 994 } 995 } 996 997 return $res; 998 } 999 1000 # Mots wiki (pour héritage) 1001 function parseWikiWord($str,&$tag,&$attr,&$type) 1002 { 1003 $tag = $attr = ''; 1004 1005 if (isset($this->functions['wikiword'])) { 1006 return call_user_func($this->functions['wikiword'],$str); 1007 } 1008 1009 return $str; 1010 } 1011 1012 /* Protection des attributs */ 1013 function protectAttr($str,$name=false) 1014 { 1015 if ($name && !preg_match('/^[A-Za-z][A-Za-z0-9_:.-]*$/',$str)) { 1016 return ''; 1017 } 1018 1019 return str_replace(array("'",'"'),array(''','"'),$str); 1020 } 1021 1022 /* Protection des urls */ 1023 function protectUrls($str) 1024 { 1025 if (preg_match('/^javascript:/',$str)) { 1026 $str = '#'; 1027 } 1028 1029 return $str; 1030 } 1031 1032 /* Auto BR */ 1033 function __autoBR($m) 1034 { 1035 return $m[1].str_replace("\n","<br />\n",$m[2]).$m[3]; 1036 } 1037 1038 /* Macro 1039 --------------------------------------------------- */ 1040 function __getMacro($s) 1041 { 1042 $this->macros[] = str_replace('\"','"',$s); 1043 return 'øøø##########MACRO#'.(count($this->macros)-1).'#'; 1044 } 1045 1046 function __putMacro($id) 1047 { 1048 $id = (integer) $id; 1049 if (isset($this->macros[$id])) 1050 { 1051 $content = str_replace("\r",'',$this->macros[$id]); 1052 1053 $c = explode("\n",$content); 1054 1055 # première ligne, premier mot 1056 $fl = trim($c[0]); 1057 $fw = $fl; 1058 1059 if ($fl) { 1060 if (strpos($fl,' ') !== false) { 1061 $fw = substr($fl,0,strpos($fl,' ')); 1062 } 1063 $content = implode("\n",array_slice($c,1)); 1064 } 1065 1066 if ($c[0] == "\n") { 1067 $content = implode("\n",array_slice($c,1)); 1068 } 1069 1070 if ($fw) 1071 { 1072 if (isset($this->functions['macro:'.$fw])) 1073 { 1074 return call_user_func($this->functions['macro:'.$fw],$content,$fl); 1075 } 1076 } 1077 1078 # Si on n'a rien pu faire, on retourne le tout sous 1079 # forme de <pre> 1080 return '<pre>'.htmlspecialchars($this->macros[$id]).'</pre>'; 1081 } 1082 1083 return null; 1084 } 1085 1086 function __macroHTML($s) 1087 { 1088 return $s; 1089 } 1090 1091 /* Aide et debug 1092 --------------------------------------------------- */ 1093 function help() 1094 { 1095 $help['b'] = array(); 1096 $help['i'] = array(); 1097 1098 $help['b'][] = 'Laisser une ligne vide entre chaque bloc <em>de même nature</em>.'; 1099 $help['b'][] = '<strong>Paragraphe</strong> : du texte et une ligne vide'; 1100 1101 if ($this->getOpt('active_title')) { 1102 $help['b'][] = '<strong>Titre</strong> : <code>!!!</code>, <code>!!</code>, '. 1103 '<code>!</code> pour des titres plus ou moins importants'; 1104 } 1105 1106 if ($this->getOpt('active_hr')) { 1107 $help['b'][] = '<strong>Trait horizontal</strong> : <code>----</code>'; 1108 } 1109 1110 if ($this->getOpt('active_lists')) { 1111 $help['b'][] = '<strong>Liste</strong> : ligne débutant par <code>*</code> ou '. 1112 '<code>#</code>. Il est possible de mélanger les listes '. 1113 '(<code>*#*</code>) pour faire des listes de plusieurs niveaux. '. 1114 'Respecter le style de chaque niveau'; 1115 } 1116 1117 if ($this->getOpt('active_pre')) { 1118 $help['b'][] = '<strong>Texte préformaté</strong> : espace devant chaque ligne de texte'; 1119 } 1120 1121 if ($this->getOpt('active_quote')) { 1122 $help['b'][] = '<strong>Bloc de citation</strong> : <code>></code> ou '. 1123 '<code>;:</code> devant chaque ligne de texte'; 1124 } 1125 1126 if ($this->getOpt('active_fr_syntax')) { 1127 $help['i'][] = 'La correction de ponctuation est active. Un espace '. 1128 'insécable remplacera automatiquement tout espace '. 1129 'précédant les marques ";","?",":" et "!".'; 1130 } 1131 1132 if ($this->getOpt('active_em')) { 1133 $help['i'][] = '<strong>Emphase</strong> : deux apostrophes <code>\'\'texte\'\'</code>'; 1134 } 1135 1136 if ($this->getOpt('active_strong')) { 1137 $help['i'][] = '<strong>Forte emphase</strong> : deux soulignés <code>__texte__</code>'; 1138 } 1139 1140 if ($this->getOpt('active_br')) { 1141 $help['i'][] = '<strong>Retour forcé à la ligne</strong> : <code>%%%</code>'; 1142 } 1143 1144 if ($this->getOpt('active_ins')) { 1145 $help['i'][] = '<strong>Insertion</strong> : deux plus <code>++texte++</code>'; 1146 } 1147 1148 if ($this->getOpt('active_del')) { 1149 $help['i'][] = '<strong>Suppression</strong> : deux moins <code>--texte--</code>'; 1150 } 1151 1152 if ($this->getOpt('active_urls')) { 1153 $help['i'][] = '<strong>Lien</strong> : <code>[url]</code>, <code>[nom|url]</code>, '. 1154 '<code>[nom|url|langue]</code> ou <code>[nom|url|langue|titre]</code>.'; 1155 1156 $help['i'][] = '<strong>Image</strong> : comme un lien mais avec une extension d\'image.'. 1157 '<br />Pour désactiver la reconnaissance d\'image mettez 0 dans un dernier '. 1158 'argument. Par exemple <code>[image|image.gif||0]</code> fera un lien vers l\'image au '. 1159 'lieu de l\'afficher.'. 1160 '<br />Il est conseillé d\'utiliser la nouvelle syntaxe.'; 1161 } 1162 1163 if ($this->getOpt('active_img')) { 1164 $help['i'][] = '<strong>Image</strong> (nouvelle syntaxe) : '. 1165 '<code>((url|texte alternatif))</code>, '. 1166 '<code>((url|texte alternatif|position))</code> ou '. 1167 '<code>((url|texte alternatif|position|description longue))</code>. '. 1168 '<br />La position peut prendre les valeur L ou G (gauche), R ou D (droite) ou C (centré).'; 1169 } 1170 1171 if ($this->getOpt('active_anchor')) { 1172 $help['i'][] = '<strong>Ancre</strong> : <code>~ancre~</code>'; 1173 } 1174 1175 if ($this->getOpt('active_acronym')) { 1176 $help['i'][] = '<strong>Acronyme</strong> : <code>??acronyme??</code> ou '. 1177 '<code>??acronyme|titre??</code>'; 1178 } 1179 1180 if ($this->getOpt('active_q')) { 1181 $help['i'][] = '<strong>Citation</strong> : <code>{{citation}}</code>, '. 1182 '<code>{{citation|langue}}</code> ou <code>{{citation|langue|url}}</code>'; 1183 } 1184 1185 if ($this->getOpt('active_code')) { 1186 $help['i'][] = '<strong>Code</strong> : <code>@@code ici@@</code>'; 1187 } 1188 1189 if ($this->getOpt('active_footnotes')) { 1190 $help['i'][] = '<strong>Note de bas de page</strong> : <code>$$Corps de la note$$</code>'; 1191 } 1192 1193 $res = '<dl class="wikiHelp">'; 1194 1195 $res .= '<dt>Blocs</dt><dd>'; 1196 if (count($help['b']) > 0) 1197 { 1198 $res .= '<ul><li>'; 1199 $res .= implode(' ;</li><li>', $help['b']); 1200 $res .= '.</li></ul>'; 1201 } 1202 $res .= '</dd>'; 1203 1204 $res .= '<dt>Éléments en ligne</dt><dd>'; 1205 if (count($help['i']) > 0) 1206 { 1207 $res .= '<ul><li>'; 1208 $res .= implode(' ;</li><li>', $help['i']); 1209 $res .= '.</li></ul>'; 1210 } 1211 $res .= '</dd>'; 1212 1213 $res .= '</dl>'; 1214 1215 return $res; 1216 } 1217 1218 /* 1219 function debug() 1220 { 1221 $mode = $type = NULL; 1222 $max = count($this->T); 1223 1224 $res = 1225 '<table border="1">'. 1226 '<tr><th>p-mode</th><th>p-type</th><th>mode</th><th>type</th><th>chaine</th></tr>'; 1227 1228 for ($i=0; $i<$max; $i++) 1229 { 1230 $pre_mode = $mode; 1231 $pre_type = $type; 1232 1233 $line = $this->__getLine($i,$type,$mode); 1234 1235 $res .= 1236 '<tr><td>'.$pre_mode.'</td><td>'.$pre_type.'</td>'. 1237 '<td>'.$mode.'</td><td>'.$type.'</td><td>'.$line.'</td></tr>'; 1238 1239 } 1240 $res .= '</table>'; 1241 1242 return $res; 1243 } 1244 //*/ 1245 } 1246 1247 ?>
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
| Généré le : Fri Feb 23 22:16:06 2007 | par Balluche grâce à PHPXref 0.7 |