1*7017fceaSAndreas Gohr<?php 2*7017fceaSAndreas Gohr 3*7017fceaSAndreas Gohr# 4*7017fceaSAndreas Gohr# 5*7017fceaSAndreas Gohr# Parsedown 6*7017fceaSAndreas Gohr# http://parsedown.org 7*7017fceaSAndreas Gohr# 8*7017fceaSAndreas Gohr# (c) Emanuil Rusev 9*7017fceaSAndreas Gohr# http://erusev.com 10*7017fceaSAndreas Gohr# 11*7017fceaSAndreas Gohr# For the full license information, view the LICENSE file that was distributed 12*7017fceaSAndreas Gohr# with this source code. 13*7017fceaSAndreas Gohr# 14*7017fceaSAndreas Gohr# 15*7017fceaSAndreas Gohr 16*7017fceaSAndreas Gohrclass Parsedown 17*7017fceaSAndreas Gohr{ 18*7017fceaSAndreas Gohr # ~ 19*7017fceaSAndreas Gohr 20*7017fceaSAndreas Gohr const version = '1.7.4'; 21*7017fceaSAndreas Gohr 22*7017fceaSAndreas Gohr # ~ 23*7017fceaSAndreas Gohr 24*7017fceaSAndreas Gohr function text($text) 25*7017fceaSAndreas Gohr { 26*7017fceaSAndreas Gohr # make sure no definitions are set 27*7017fceaSAndreas Gohr $this->DefinitionData = array(); 28*7017fceaSAndreas Gohr 29*7017fceaSAndreas Gohr # standardize line breaks 30*7017fceaSAndreas Gohr $text = str_replace(array("\r\n", "\r"), "\n", $text); 31*7017fceaSAndreas Gohr 32*7017fceaSAndreas Gohr # remove surrounding line breaks 33*7017fceaSAndreas Gohr $text = trim($text, "\n"); 34*7017fceaSAndreas Gohr 35*7017fceaSAndreas Gohr # split text into lines 36*7017fceaSAndreas Gohr $lines = explode("\n", $text); 37*7017fceaSAndreas Gohr 38*7017fceaSAndreas Gohr # iterate through lines to identify blocks 39*7017fceaSAndreas Gohr $markup = $this->lines($lines); 40*7017fceaSAndreas Gohr 41*7017fceaSAndreas Gohr # trim line breaks 42*7017fceaSAndreas Gohr $markup = trim($markup, "\n"); 43*7017fceaSAndreas Gohr 44*7017fceaSAndreas Gohr return $markup; 45*7017fceaSAndreas Gohr } 46*7017fceaSAndreas Gohr 47*7017fceaSAndreas Gohr # 48*7017fceaSAndreas Gohr # Setters 49*7017fceaSAndreas Gohr # 50*7017fceaSAndreas Gohr 51*7017fceaSAndreas Gohr function setBreaksEnabled($breaksEnabled) 52*7017fceaSAndreas Gohr { 53*7017fceaSAndreas Gohr $this->breaksEnabled = $breaksEnabled; 54*7017fceaSAndreas Gohr 55*7017fceaSAndreas Gohr return $this; 56*7017fceaSAndreas Gohr } 57*7017fceaSAndreas Gohr 58*7017fceaSAndreas Gohr protected $breaksEnabled; 59*7017fceaSAndreas Gohr 60*7017fceaSAndreas Gohr function setMarkupEscaped($markupEscaped) 61*7017fceaSAndreas Gohr { 62*7017fceaSAndreas Gohr $this->markupEscaped = $markupEscaped; 63*7017fceaSAndreas Gohr 64*7017fceaSAndreas Gohr return $this; 65*7017fceaSAndreas Gohr } 66*7017fceaSAndreas Gohr 67*7017fceaSAndreas Gohr protected $markupEscaped; 68*7017fceaSAndreas Gohr 69*7017fceaSAndreas Gohr function setUrlsLinked($urlsLinked) 70*7017fceaSAndreas Gohr { 71*7017fceaSAndreas Gohr $this->urlsLinked = $urlsLinked; 72*7017fceaSAndreas Gohr 73*7017fceaSAndreas Gohr return $this; 74*7017fceaSAndreas Gohr } 75*7017fceaSAndreas Gohr 76*7017fceaSAndreas Gohr protected $urlsLinked = true; 77*7017fceaSAndreas Gohr 78*7017fceaSAndreas Gohr function setSafeMode($safeMode) 79*7017fceaSAndreas Gohr { 80*7017fceaSAndreas Gohr $this->safeMode = (bool) $safeMode; 81*7017fceaSAndreas Gohr 82*7017fceaSAndreas Gohr return $this; 83*7017fceaSAndreas Gohr } 84*7017fceaSAndreas Gohr 85*7017fceaSAndreas Gohr protected $safeMode; 86*7017fceaSAndreas Gohr 87*7017fceaSAndreas Gohr protected $safeLinksWhitelist = array( 88*7017fceaSAndreas Gohr 'http://', 89*7017fceaSAndreas Gohr 'https://', 90*7017fceaSAndreas Gohr 'ftp://', 91*7017fceaSAndreas Gohr 'ftps://', 92*7017fceaSAndreas Gohr 'mailto:', 93*7017fceaSAndreas Gohr 'data:image/png;base64,', 94*7017fceaSAndreas Gohr 'data:image/gif;base64,', 95*7017fceaSAndreas Gohr 'data:image/jpeg;base64,', 96*7017fceaSAndreas Gohr 'irc:', 97*7017fceaSAndreas Gohr 'ircs:', 98*7017fceaSAndreas Gohr 'git:', 99*7017fceaSAndreas Gohr 'ssh:', 100*7017fceaSAndreas Gohr 'news:', 101*7017fceaSAndreas Gohr 'steam:', 102*7017fceaSAndreas Gohr ); 103*7017fceaSAndreas Gohr 104*7017fceaSAndreas Gohr # 105*7017fceaSAndreas Gohr # Lines 106*7017fceaSAndreas Gohr # 107*7017fceaSAndreas Gohr 108*7017fceaSAndreas Gohr protected $BlockTypes = array( 109*7017fceaSAndreas Gohr '#' => array('Header'), 110*7017fceaSAndreas Gohr '*' => array('Rule', 'List'), 111*7017fceaSAndreas Gohr '+' => array('List'), 112*7017fceaSAndreas Gohr '-' => array('SetextHeader', 'Table', 'Rule', 'List'), 113*7017fceaSAndreas Gohr '0' => array('List'), 114*7017fceaSAndreas Gohr '1' => array('List'), 115*7017fceaSAndreas Gohr '2' => array('List'), 116*7017fceaSAndreas Gohr '3' => array('List'), 117*7017fceaSAndreas Gohr '4' => array('List'), 118*7017fceaSAndreas Gohr '5' => array('List'), 119*7017fceaSAndreas Gohr '6' => array('List'), 120*7017fceaSAndreas Gohr '7' => array('List'), 121*7017fceaSAndreas Gohr '8' => array('List'), 122*7017fceaSAndreas Gohr '9' => array('List'), 123*7017fceaSAndreas Gohr ':' => array('Table'), 124*7017fceaSAndreas Gohr '<' => array('Comment', 'Markup'), 125*7017fceaSAndreas Gohr '=' => array('SetextHeader'), 126*7017fceaSAndreas Gohr '>' => array('Quote'), 127*7017fceaSAndreas Gohr '[' => array('Reference'), 128*7017fceaSAndreas Gohr '_' => array('Rule'), 129*7017fceaSAndreas Gohr '`' => array('FencedCode'), 130*7017fceaSAndreas Gohr '|' => array('Table'), 131*7017fceaSAndreas Gohr '~' => array('FencedCode'), 132*7017fceaSAndreas Gohr ); 133*7017fceaSAndreas Gohr 134*7017fceaSAndreas Gohr # ~ 135*7017fceaSAndreas Gohr 136*7017fceaSAndreas Gohr protected $unmarkedBlockTypes = array( 137*7017fceaSAndreas Gohr 'Code', 138*7017fceaSAndreas Gohr ); 139*7017fceaSAndreas Gohr 140*7017fceaSAndreas Gohr # 141*7017fceaSAndreas Gohr # Blocks 142*7017fceaSAndreas Gohr # 143*7017fceaSAndreas Gohr 144*7017fceaSAndreas Gohr protected function lines(array $lines) 145*7017fceaSAndreas Gohr { 146*7017fceaSAndreas Gohr $CurrentBlock = null; 147*7017fceaSAndreas Gohr 148*7017fceaSAndreas Gohr foreach ($lines as $line) 149*7017fceaSAndreas Gohr { 150*7017fceaSAndreas Gohr if (chop($line) === '') 151*7017fceaSAndreas Gohr { 152*7017fceaSAndreas Gohr if (isset($CurrentBlock)) 153*7017fceaSAndreas Gohr { 154*7017fceaSAndreas Gohr $CurrentBlock['interrupted'] = true; 155*7017fceaSAndreas Gohr } 156*7017fceaSAndreas Gohr 157*7017fceaSAndreas Gohr continue; 158*7017fceaSAndreas Gohr } 159*7017fceaSAndreas Gohr 160*7017fceaSAndreas Gohr if (strpos($line, "\t") !== false) 161*7017fceaSAndreas Gohr { 162*7017fceaSAndreas Gohr $parts = explode("\t", $line); 163*7017fceaSAndreas Gohr 164*7017fceaSAndreas Gohr $line = $parts[0]; 165*7017fceaSAndreas Gohr 166*7017fceaSAndreas Gohr unset($parts[0]); 167*7017fceaSAndreas Gohr 168*7017fceaSAndreas Gohr foreach ($parts as $part) 169*7017fceaSAndreas Gohr { 170*7017fceaSAndreas Gohr $shortage = 4 - mb_strlen($line, 'utf-8') % 4; 171*7017fceaSAndreas Gohr 172*7017fceaSAndreas Gohr $line .= str_repeat(' ', $shortage); 173*7017fceaSAndreas Gohr $line .= $part; 174*7017fceaSAndreas Gohr } 175*7017fceaSAndreas Gohr } 176*7017fceaSAndreas Gohr 177*7017fceaSAndreas Gohr $indent = 0; 178*7017fceaSAndreas Gohr 179*7017fceaSAndreas Gohr while (isset($line[$indent]) and $line[$indent] === ' ') 180*7017fceaSAndreas Gohr { 181*7017fceaSAndreas Gohr $indent ++; 182*7017fceaSAndreas Gohr } 183*7017fceaSAndreas Gohr 184*7017fceaSAndreas Gohr $text = $indent > 0 ? substr($line, $indent) : $line; 185*7017fceaSAndreas Gohr 186*7017fceaSAndreas Gohr # ~ 187*7017fceaSAndreas Gohr 188*7017fceaSAndreas Gohr $Line = array('body' => $line, 'indent' => $indent, 'text' => $text); 189*7017fceaSAndreas Gohr 190*7017fceaSAndreas Gohr # ~ 191*7017fceaSAndreas Gohr 192*7017fceaSAndreas Gohr if (isset($CurrentBlock['continuable'])) 193*7017fceaSAndreas Gohr { 194*7017fceaSAndreas Gohr $Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock); 195*7017fceaSAndreas Gohr 196*7017fceaSAndreas Gohr if (isset($Block)) 197*7017fceaSAndreas Gohr { 198*7017fceaSAndreas Gohr $CurrentBlock = $Block; 199*7017fceaSAndreas Gohr 200*7017fceaSAndreas Gohr continue; 201*7017fceaSAndreas Gohr } 202*7017fceaSAndreas Gohr else 203*7017fceaSAndreas Gohr { 204*7017fceaSAndreas Gohr if ($this->isBlockCompletable($CurrentBlock['type'])) 205*7017fceaSAndreas Gohr { 206*7017fceaSAndreas Gohr $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); 207*7017fceaSAndreas Gohr } 208*7017fceaSAndreas Gohr } 209*7017fceaSAndreas Gohr } 210*7017fceaSAndreas Gohr 211*7017fceaSAndreas Gohr # ~ 212*7017fceaSAndreas Gohr 213*7017fceaSAndreas Gohr $marker = $text[0]; 214*7017fceaSAndreas Gohr 215*7017fceaSAndreas Gohr # ~ 216*7017fceaSAndreas Gohr 217*7017fceaSAndreas Gohr $blockTypes = $this->unmarkedBlockTypes; 218*7017fceaSAndreas Gohr 219*7017fceaSAndreas Gohr if (isset($this->BlockTypes[$marker])) 220*7017fceaSAndreas Gohr { 221*7017fceaSAndreas Gohr foreach ($this->BlockTypes[$marker] as $blockType) 222*7017fceaSAndreas Gohr { 223*7017fceaSAndreas Gohr $blockTypes []= $blockType; 224*7017fceaSAndreas Gohr } 225*7017fceaSAndreas Gohr } 226*7017fceaSAndreas Gohr 227*7017fceaSAndreas Gohr # 228*7017fceaSAndreas Gohr # ~ 229*7017fceaSAndreas Gohr 230*7017fceaSAndreas Gohr foreach ($blockTypes as $blockType) 231*7017fceaSAndreas Gohr { 232*7017fceaSAndreas Gohr $Block = $this->{'block'.$blockType}($Line, $CurrentBlock); 233*7017fceaSAndreas Gohr 234*7017fceaSAndreas Gohr if (isset($Block)) 235*7017fceaSAndreas Gohr { 236*7017fceaSAndreas Gohr $Block['type'] = $blockType; 237*7017fceaSAndreas Gohr 238*7017fceaSAndreas Gohr if ( ! isset($Block['identified'])) 239*7017fceaSAndreas Gohr { 240*7017fceaSAndreas Gohr $Blocks []= $CurrentBlock; 241*7017fceaSAndreas Gohr 242*7017fceaSAndreas Gohr $Block['identified'] = true; 243*7017fceaSAndreas Gohr } 244*7017fceaSAndreas Gohr 245*7017fceaSAndreas Gohr if ($this->isBlockContinuable($blockType)) 246*7017fceaSAndreas Gohr { 247*7017fceaSAndreas Gohr $Block['continuable'] = true; 248*7017fceaSAndreas Gohr } 249*7017fceaSAndreas Gohr 250*7017fceaSAndreas Gohr $CurrentBlock = $Block; 251*7017fceaSAndreas Gohr 252*7017fceaSAndreas Gohr continue 2; 253*7017fceaSAndreas Gohr } 254*7017fceaSAndreas Gohr } 255*7017fceaSAndreas Gohr 256*7017fceaSAndreas Gohr # ~ 257*7017fceaSAndreas Gohr 258*7017fceaSAndreas Gohr if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted'])) 259*7017fceaSAndreas Gohr { 260*7017fceaSAndreas Gohr $CurrentBlock['element']['text'] .= "\n".$text; 261*7017fceaSAndreas Gohr } 262*7017fceaSAndreas Gohr else 263*7017fceaSAndreas Gohr { 264*7017fceaSAndreas Gohr $Blocks []= $CurrentBlock; 265*7017fceaSAndreas Gohr 266*7017fceaSAndreas Gohr $CurrentBlock = $this->paragraph($Line); 267*7017fceaSAndreas Gohr 268*7017fceaSAndreas Gohr $CurrentBlock['identified'] = true; 269*7017fceaSAndreas Gohr } 270*7017fceaSAndreas Gohr } 271*7017fceaSAndreas Gohr 272*7017fceaSAndreas Gohr # ~ 273*7017fceaSAndreas Gohr 274*7017fceaSAndreas Gohr if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type'])) 275*7017fceaSAndreas Gohr { 276*7017fceaSAndreas Gohr $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); 277*7017fceaSAndreas Gohr } 278*7017fceaSAndreas Gohr 279*7017fceaSAndreas Gohr # ~ 280*7017fceaSAndreas Gohr 281*7017fceaSAndreas Gohr $Blocks []= $CurrentBlock; 282*7017fceaSAndreas Gohr 283*7017fceaSAndreas Gohr unset($Blocks[0]); 284*7017fceaSAndreas Gohr 285*7017fceaSAndreas Gohr # ~ 286*7017fceaSAndreas Gohr 287*7017fceaSAndreas Gohr $markup = ''; 288*7017fceaSAndreas Gohr 289*7017fceaSAndreas Gohr foreach ($Blocks as $Block) 290*7017fceaSAndreas Gohr { 291*7017fceaSAndreas Gohr if (isset($Block['hidden'])) 292*7017fceaSAndreas Gohr { 293*7017fceaSAndreas Gohr continue; 294*7017fceaSAndreas Gohr } 295*7017fceaSAndreas Gohr 296*7017fceaSAndreas Gohr $markup .= "\n"; 297*7017fceaSAndreas Gohr $markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']); 298*7017fceaSAndreas Gohr } 299*7017fceaSAndreas Gohr 300*7017fceaSAndreas Gohr $markup .= "\n"; 301*7017fceaSAndreas Gohr 302*7017fceaSAndreas Gohr # ~ 303*7017fceaSAndreas Gohr 304*7017fceaSAndreas Gohr return $markup; 305*7017fceaSAndreas Gohr } 306*7017fceaSAndreas Gohr 307*7017fceaSAndreas Gohr protected function isBlockContinuable($Type) 308*7017fceaSAndreas Gohr { 309*7017fceaSAndreas Gohr return method_exists($this, 'block'.$Type.'Continue'); 310*7017fceaSAndreas Gohr } 311*7017fceaSAndreas Gohr 312*7017fceaSAndreas Gohr protected function isBlockCompletable($Type) 313*7017fceaSAndreas Gohr { 314*7017fceaSAndreas Gohr return method_exists($this, 'block'.$Type.'Complete'); 315*7017fceaSAndreas Gohr } 316*7017fceaSAndreas Gohr 317*7017fceaSAndreas Gohr # 318*7017fceaSAndreas Gohr # Code 319*7017fceaSAndreas Gohr 320*7017fceaSAndreas Gohr protected function blockCode($Line, $Block = null) 321*7017fceaSAndreas Gohr { 322*7017fceaSAndreas Gohr if (isset($Block) and ! isset($Block['type']) and ! isset($Block['interrupted'])) 323*7017fceaSAndreas Gohr { 324*7017fceaSAndreas Gohr return; 325*7017fceaSAndreas Gohr } 326*7017fceaSAndreas Gohr 327*7017fceaSAndreas Gohr if ($Line['indent'] >= 4) 328*7017fceaSAndreas Gohr { 329*7017fceaSAndreas Gohr $text = substr($Line['body'], 4); 330*7017fceaSAndreas Gohr 331*7017fceaSAndreas Gohr $Block = array( 332*7017fceaSAndreas Gohr 'element' => array( 333*7017fceaSAndreas Gohr 'name' => 'pre', 334*7017fceaSAndreas Gohr 'handler' => 'element', 335*7017fceaSAndreas Gohr 'text' => array( 336*7017fceaSAndreas Gohr 'name' => 'code', 337*7017fceaSAndreas Gohr 'text' => $text, 338*7017fceaSAndreas Gohr ), 339*7017fceaSAndreas Gohr ), 340*7017fceaSAndreas Gohr ); 341*7017fceaSAndreas Gohr 342*7017fceaSAndreas Gohr return $Block; 343*7017fceaSAndreas Gohr } 344*7017fceaSAndreas Gohr } 345*7017fceaSAndreas Gohr 346*7017fceaSAndreas Gohr protected function blockCodeContinue($Line, $Block) 347*7017fceaSAndreas Gohr { 348*7017fceaSAndreas Gohr if ($Line['indent'] >= 4) 349*7017fceaSAndreas Gohr { 350*7017fceaSAndreas Gohr if (isset($Block['interrupted'])) 351*7017fceaSAndreas Gohr { 352*7017fceaSAndreas Gohr $Block['element']['text']['text'] .= "\n"; 353*7017fceaSAndreas Gohr 354*7017fceaSAndreas Gohr unset($Block['interrupted']); 355*7017fceaSAndreas Gohr } 356*7017fceaSAndreas Gohr 357*7017fceaSAndreas Gohr $Block['element']['text']['text'] .= "\n"; 358*7017fceaSAndreas Gohr 359*7017fceaSAndreas Gohr $text = substr($Line['body'], 4); 360*7017fceaSAndreas Gohr 361*7017fceaSAndreas Gohr $Block['element']['text']['text'] .= $text; 362*7017fceaSAndreas Gohr 363*7017fceaSAndreas Gohr return $Block; 364*7017fceaSAndreas Gohr } 365*7017fceaSAndreas Gohr } 366*7017fceaSAndreas Gohr 367*7017fceaSAndreas Gohr protected function blockCodeComplete($Block) 368*7017fceaSAndreas Gohr { 369*7017fceaSAndreas Gohr $text = $Block['element']['text']['text']; 370*7017fceaSAndreas Gohr 371*7017fceaSAndreas Gohr $Block['element']['text']['text'] = $text; 372*7017fceaSAndreas Gohr 373*7017fceaSAndreas Gohr return $Block; 374*7017fceaSAndreas Gohr } 375*7017fceaSAndreas Gohr 376*7017fceaSAndreas Gohr # 377*7017fceaSAndreas Gohr # Comment 378*7017fceaSAndreas Gohr 379*7017fceaSAndreas Gohr protected function blockComment($Line) 380*7017fceaSAndreas Gohr { 381*7017fceaSAndreas Gohr if ($this->markupEscaped or $this->safeMode) 382*7017fceaSAndreas Gohr { 383*7017fceaSAndreas Gohr return; 384*7017fceaSAndreas Gohr } 385*7017fceaSAndreas Gohr 386*7017fceaSAndreas Gohr if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!') 387*7017fceaSAndreas Gohr { 388*7017fceaSAndreas Gohr $Block = array( 389*7017fceaSAndreas Gohr 'markup' => $Line['body'], 390*7017fceaSAndreas Gohr ); 391*7017fceaSAndreas Gohr 392*7017fceaSAndreas Gohr if (preg_match('/-->$/', $Line['text'])) 393*7017fceaSAndreas Gohr { 394*7017fceaSAndreas Gohr $Block['closed'] = true; 395*7017fceaSAndreas Gohr } 396*7017fceaSAndreas Gohr 397*7017fceaSAndreas Gohr return $Block; 398*7017fceaSAndreas Gohr } 399*7017fceaSAndreas Gohr } 400*7017fceaSAndreas Gohr 401*7017fceaSAndreas Gohr protected function blockCommentContinue($Line, array $Block) 402*7017fceaSAndreas Gohr { 403*7017fceaSAndreas Gohr if (isset($Block['closed'])) 404*7017fceaSAndreas Gohr { 405*7017fceaSAndreas Gohr return; 406*7017fceaSAndreas Gohr } 407*7017fceaSAndreas Gohr 408*7017fceaSAndreas Gohr $Block['markup'] .= "\n" . $Line['body']; 409*7017fceaSAndreas Gohr 410*7017fceaSAndreas Gohr if (preg_match('/-->$/', $Line['text'])) 411*7017fceaSAndreas Gohr { 412*7017fceaSAndreas Gohr $Block['closed'] = true; 413*7017fceaSAndreas Gohr } 414*7017fceaSAndreas Gohr 415*7017fceaSAndreas Gohr return $Block; 416*7017fceaSAndreas Gohr } 417*7017fceaSAndreas Gohr 418*7017fceaSAndreas Gohr # 419*7017fceaSAndreas Gohr # Fenced Code 420*7017fceaSAndreas Gohr 421*7017fceaSAndreas Gohr protected function blockFencedCode($Line) 422*7017fceaSAndreas Gohr { 423*7017fceaSAndreas Gohr if (preg_match('/^['.$Line['text'][0].']{3,}[ ]*([^`]+)?[ ]*$/', $Line['text'], $matches)) 424*7017fceaSAndreas Gohr { 425*7017fceaSAndreas Gohr $Element = array( 426*7017fceaSAndreas Gohr 'name' => 'code', 427*7017fceaSAndreas Gohr 'text' => '', 428*7017fceaSAndreas Gohr ); 429*7017fceaSAndreas Gohr 430*7017fceaSAndreas Gohr if (isset($matches[1])) 431*7017fceaSAndreas Gohr { 432*7017fceaSAndreas Gohr /** 433*7017fceaSAndreas Gohr * https://www.w3.org/TR/2011/WD-html5-20110525/elements.html#classes 434*7017fceaSAndreas Gohr * Every HTML element may have a class attribute specified. 435*7017fceaSAndreas Gohr * The attribute, if specified, must have a value that is a set 436*7017fceaSAndreas Gohr * of space-separated tokens representing the various classes 437*7017fceaSAndreas Gohr * that the element belongs to. 438*7017fceaSAndreas Gohr * [...] 439*7017fceaSAndreas Gohr * The space characters, for the purposes of this specification, 440*7017fceaSAndreas Gohr * are U+0020 SPACE, U+0009 CHARACTER TABULATION (tab), 441*7017fceaSAndreas Gohr * U+000A LINE FEED (LF), U+000C FORM FEED (FF), and 442*7017fceaSAndreas Gohr * U+000D CARRIAGE RETURN (CR). 443*7017fceaSAndreas Gohr */ 444*7017fceaSAndreas Gohr $language = substr($matches[1], 0, strcspn($matches[1], " \t\n\f\r")); 445*7017fceaSAndreas Gohr 446*7017fceaSAndreas Gohr $class = 'language-'.$language; 447*7017fceaSAndreas Gohr 448*7017fceaSAndreas Gohr $Element['attributes'] = array( 449*7017fceaSAndreas Gohr 'class' => $class, 450*7017fceaSAndreas Gohr ); 451*7017fceaSAndreas Gohr } 452*7017fceaSAndreas Gohr 453*7017fceaSAndreas Gohr $Block = array( 454*7017fceaSAndreas Gohr 'char' => $Line['text'][0], 455*7017fceaSAndreas Gohr 'element' => array( 456*7017fceaSAndreas Gohr 'name' => 'pre', 457*7017fceaSAndreas Gohr 'handler' => 'element', 458*7017fceaSAndreas Gohr 'text' => $Element, 459*7017fceaSAndreas Gohr ), 460*7017fceaSAndreas Gohr ); 461*7017fceaSAndreas Gohr 462*7017fceaSAndreas Gohr return $Block; 463*7017fceaSAndreas Gohr } 464*7017fceaSAndreas Gohr } 465*7017fceaSAndreas Gohr 466*7017fceaSAndreas Gohr protected function blockFencedCodeContinue($Line, $Block) 467*7017fceaSAndreas Gohr { 468*7017fceaSAndreas Gohr if (isset($Block['complete'])) 469*7017fceaSAndreas Gohr { 470*7017fceaSAndreas Gohr return; 471*7017fceaSAndreas Gohr } 472*7017fceaSAndreas Gohr 473*7017fceaSAndreas Gohr if (isset($Block['interrupted'])) 474*7017fceaSAndreas Gohr { 475*7017fceaSAndreas Gohr $Block['element']['text']['text'] .= "\n"; 476*7017fceaSAndreas Gohr 477*7017fceaSAndreas Gohr unset($Block['interrupted']); 478*7017fceaSAndreas Gohr } 479*7017fceaSAndreas Gohr 480*7017fceaSAndreas Gohr if (preg_match('/^'.$Block['char'].'{3,}[ ]*$/', $Line['text'])) 481*7017fceaSAndreas Gohr { 482*7017fceaSAndreas Gohr $Block['element']['text']['text'] = substr($Block['element']['text']['text'], 1); 483*7017fceaSAndreas Gohr 484*7017fceaSAndreas Gohr $Block['complete'] = true; 485*7017fceaSAndreas Gohr 486*7017fceaSAndreas Gohr return $Block; 487*7017fceaSAndreas Gohr } 488*7017fceaSAndreas Gohr 489*7017fceaSAndreas Gohr $Block['element']['text']['text'] .= "\n".$Line['body']; 490*7017fceaSAndreas Gohr 491*7017fceaSAndreas Gohr return $Block; 492*7017fceaSAndreas Gohr } 493*7017fceaSAndreas Gohr 494*7017fceaSAndreas Gohr protected function blockFencedCodeComplete($Block) 495*7017fceaSAndreas Gohr { 496*7017fceaSAndreas Gohr $text = $Block['element']['text']['text']; 497*7017fceaSAndreas Gohr 498*7017fceaSAndreas Gohr $Block['element']['text']['text'] = $text; 499*7017fceaSAndreas Gohr 500*7017fceaSAndreas Gohr return $Block; 501*7017fceaSAndreas Gohr } 502*7017fceaSAndreas Gohr 503*7017fceaSAndreas Gohr # 504*7017fceaSAndreas Gohr # Header 505*7017fceaSAndreas Gohr 506*7017fceaSAndreas Gohr protected function blockHeader($Line) 507*7017fceaSAndreas Gohr { 508*7017fceaSAndreas Gohr if (isset($Line['text'][1])) 509*7017fceaSAndreas Gohr { 510*7017fceaSAndreas Gohr $level = 1; 511*7017fceaSAndreas Gohr 512*7017fceaSAndreas Gohr while (isset($Line['text'][$level]) and $Line['text'][$level] === '#') 513*7017fceaSAndreas Gohr { 514*7017fceaSAndreas Gohr $level ++; 515*7017fceaSAndreas Gohr } 516*7017fceaSAndreas Gohr 517*7017fceaSAndreas Gohr if ($level > 6) 518*7017fceaSAndreas Gohr { 519*7017fceaSAndreas Gohr return; 520*7017fceaSAndreas Gohr } 521*7017fceaSAndreas Gohr 522*7017fceaSAndreas Gohr $text = trim($Line['text'], '# '); 523*7017fceaSAndreas Gohr 524*7017fceaSAndreas Gohr $Block = array( 525*7017fceaSAndreas Gohr 'element' => array( 526*7017fceaSAndreas Gohr 'name' => 'h' . min(6, $level), 527*7017fceaSAndreas Gohr 'text' => $text, 528*7017fceaSAndreas Gohr 'handler' => 'line', 529*7017fceaSAndreas Gohr ), 530*7017fceaSAndreas Gohr ); 531*7017fceaSAndreas Gohr 532*7017fceaSAndreas Gohr return $Block; 533*7017fceaSAndreas Gohr } 534*7017fceaSAndreas Gohr } 535*7017fceaSAndreas Gohr 536*7017fceaSAndreas Gohr # 537*7017fceaSAndreas Gohr # List 538*7017fceaSAndreas Gohr 539*7017fceaSAndreas Gohr protected function blockList($Line) 540*7017fceaSAndreas Gohr { 541*7017fceaSAndreas Gohr list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]+[.]'); 542*7017fceaSAndreas Gohr 543*7017fceaSAndreas Gohr if (preg_match('/^('.$pattern.'[ ]+)(.*)/', $Line['text'], $matches)) 544*7017fceaSAndreas Gohr { 545*7017fceaSAndreas Gohr $Block = array( 546*7017fceaSAndreas Gohr 'indent' => $Line['indent'], 547*7017fceaSAndreas Gohr 'pattern' => $pattern, 548*7017fceaSAndreas Gohr 'element' => array( 549*7017fceaSAndreas Gohr 'name' => $name, 550*7017fceaSAndreas Gohr 'handler' => 'elements', 551*7017fceaSAndreas Gohr ), 552*7017fceaSAndreas Gohr ); 553*7017fceaSAndreas Gohr 554*7017fceaSAndreas Gohr if($name === 'ol') 555*7017fceaSAndreas Gohr { 556*7017fceaSAndreas Gohr $listStart = stristr($matches[0], '.', true); 557*7017fceaSAndreas Gohr 558*7017fceaSAndreas Gohr if($listStart !== '1') 559*7017fceaSAndreas Gohr { 560*7017fceaSAndreas Gohr $Block['element']['attributes'] = array('start' => $listStart); 561*7017fceaSAndreas Gohr } 562*7017fceaSAndreas Gohr } 563*7017fceaSAndreas Gohr 564*7017fceaSAndreas Gohr $Block['li'] = array( 565*7017fceaSAndreas Gohr 'name' => 'li', 566*7017fceaSAndreas Gohr 'handler' => 'li', 567*7017fceaSAndreas Gohr 'text' => array( 568*7017fceaSAndreas Gohr $matches[2], 569*7017fceaSAndreas Gohr ), 570*7017fceaSAndreas Gohr ); 571*7017fceaSAndreas Gohr 572*7017fceaSAndreas Gohr $Block['element']['text'] []= & $Block['li']; 573*7017fceaSAndreas Gohr 574*7017fceaSAndreas Gohr return $Block; 575*7017fceaSAndreas Gohr } 576*7017fceaSAndreas Gohr } 577*7017fceaSAndreas Gohr 578*7017fceaSAndreas Gohr protected function blockListContinue($Line, array $Block) 579*7017fceaSAndreas Gohr { 580*7017fceaSAndreas Gohr if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'(?:[ ]+(.*)|$)/', $Line['text'], $matches)) 581*7017fceaSAndreas Gohr { 582*7017fceaSAndreas Gohr if (isset($Block['interrupted'])) 583*7017fceaSAndreas Gohr { 584*7017fceaSAndreas Gohr $Block['li']['text'] []= ''; 585*7017fceaSAndreas Gohr 586*7017fceaSAndreas Gohr $Block['loose'] = true; 587*7017fceaSAndreas Gohr 588*7017fceaSAndreas Gohr unset($Block['interrupted']); 589*7017fceaSAndreas Gohr } 590*7017fceaSAndreas Gohr 591*7017fceaSAndreas Gohr unset($Block['li']); 592*7017fceaSAndreas Gohr 593*7017fceaSAndreas Gohr $text = isset($matches[1]) ? $matches[1] : ''; 594*7017fceaSAndreas Gohr 595*7017fceaSAndreas Gohr $Block['li'] = array( 596*7017fceaSAndreas Gohr 'name' => 'li', 597*7017fceaSAndreas Gohr 'handler' => 'li', 598*7017fceaSAndreas Gohr 'text' => array( 599*7017fceaSAndreas Gohr $text, 600*7017fceaSAndreas Gohr ), 601*7017fceaSAndreas Gohr ); 602*7017fceaSAndreas Gohr 603*7017fceaSAndreas Gohr $Block['element']['text'] []= & $Block['li']; 604*7017fceaSAndreas Gohr 605*7017fceaSAndreas Gohr return $Block; 606*7017fceaSAndreas Gohr } 607*7017fceaSAndreas Gohr 608*7017fceaSAndreas Gohr if ($Line['text'][0] === '[' and $this->blockReference($Line)) 609*7017fceaSAndreas Gohr { 610*7017fceaSAndreas Gohr return $Block; 611*7017fceaSAndreas Gohr } 612*7017fceaSAndreas Gohr 613*7017fceaSAndreas Gohr if ( ! isset($Block['interrupted'])) 614*7017fceaSAndreas Gohr { 615*7017fceaSAndreas Gohr $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']); 616*7017fceaSAndreas Gohr 617*7017fceaSAndreas Gohr $Block['li']['text'] []= $text; 618*7017fceaSAndreas Gohr 619*7017fceaSAndreas Gohr return $Block; 620*7017fceaSAndreas Gohr } 621*7017fceaSAndreas Gohr 622*7017fceaSAndreas Gohr if ($Line['indent'] > 0) 623*7017fceaSAndreas Gohr { 624*7017fceaSAndreas Gohr $Block['li']['text'] []= ''; 625*7017fceaSAndreas Gohr 626*7017fceaSAndreas Gohr $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']); 627*7017fceaSAndreas Gohr 628*7017fceaSAndreas Gohr $Block['li']['text'] []= $text; 629*7017fceaSAndreas Gohr 630*7017fceaSAndreas Gohr unset($Block['interrupted']); 631*7017fceaSAndreas Gohr 632*7017fceaSAndreas Gohr return $Block; 633*7017fceaSAndreas Gohr } 634*7017fceaSAndreas Gohr } 635*7017fceaSAndreas Gohr 636*7017fceaSAndreas Gohr protected function blockListComplete(array $Block) 637*7017fceaSAndreas Gohr { 638*7017fceaSAndreas Gohr if (isset($Block['loose'])) 639*7017fceaSAndreas Gohr { 640*7017fceaSAndreas Gohr foreach ($Block['element']['text'] as &$li) 641*7017fceaSAndreas Gohr { 642*7017fceaSAndreas Gohr if (end($li['text']) !== '') 643*7017fceaSAndreas Gohr { 644*7017fceaSAndreas Gohr $li['text'] []= ''; 645*7017fceaSAndreas Gohr } 646*7017fceaSAndreas Gohr } 647*7017fceaSAndreas Gohr } 648*7017fceaSAndreas Gohr 649*7017fceaSAndreas Gohr return $Block; 650*7017fceaSAndreas Gohr } 651*7017fceaSAndreas Gohr 652*7017fceaSAndreas Gohr # 653*7017fceaSAndreas Gohr # Quote 654*7017fceaSAndreas Gohr 655*7017fceaSAndreas Gohr protected function blockQuote($Line) 656*7017fceaSAndreas Gohr { 657*7017fceaSAndreas Gohr if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) 658*7017fceaSAndreas Gohr { 659*7017fceaSAndreas Gohr $Block = array( 660*7017fceaSAndreas Gohr 'element' => array( 661*7017fceaSAndreas Gohr 'name' => 'blockquote', 662*7017fceaSAndreas Gohr 'handler' => 'lines', 663*7017fceaSAndreas Gohr 'text' => (array) $matches[1], 664*7017fceaSAndreas Gohr ), 665*7017fceaSAndreas Gohr ); 666*7017fceaSAndreas Gohr 667*7017fceaSAndreas Gohr return $Block; 668*7017fceaSAndreas Gohr } 669*7017fceaSAndreas Gohr } 670*7017fceaSAndreas Gohr 671*7017fceaSAndreas Gohr protected function blockQuoteContinue($Line, array $Block) 672*7017fceaSAndreas Gohr { 673*7017fceaSAndreas Gohr if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) 674*7017fceaSAndreas Gohr { 675*7017fceaSAndreas Gohr if (isset($Block['interrupted'])) 676*7017fceaSAndreas Gohr { 677*7017fceaSAndreas Gohr $Block['element']['text'] []= ''; 678*7017fceaSAndreas Gohr 679*7017fceaSAndreas Gohr unset($Block['interrupted']); 680*7017fceaSAndreas Gohr } 681*7017fceaSAndreas Gohr 682*7017fceaSAndreas Gohr $Block['element']['text'] []= $matches[1]; 683*7017fceaSAndreas Gohr 684*7017fceaSAndreas Gohr return $Block; 685*7017fceaSAndreas Gohr } 686*7017fceaSAndreas Gohr 687*7017fceaSAndreas Gohr if ( ! isset($Block['interrupted'])) 688*7017fceaSAndreas Gohr { 689*7017fceaSAndreas Gohr $Block['element']['text'] []= $Line['text']; 690*7017fceaSAndreas Gohr 691*7017fceaSAndreas Gohr return $Block; 692*7017fceaSAndreas Gohr } 693*7017fceaSAndreas Gohr } 694*7017fceaSAndreas Gohr 695*7017fceaSAndreas Gohr # 696*7017fceaSAndreas Gohr # Rule 697*7017fceaSAndreas Gohr 698*7017fceaSAndreas Gohr protected function blockRule($Line) 699*7017fceaSAndreas Gohr { 700*7017fceaSAndreas Gohr if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text'])) 701*7017fceaSAndreas Gohr { 702*7017fceaSAndreas Gohr $Block = array( 703*7017fceaSAndreas Gohr 'element' => array( 704*7017fceaSAndreas Gohr 'name' => 'hr' 705*7017fceaSAndreas Gohr ), 706*7017fceaSAndreas Gohr ); 707*7017fceaSAndreas Gohr 708*7017fceaSAndreas Gohr return $Block; 709*7017fceaSAndreas Gohr } 710*7017fceaSAndreas Gohr } 711*7017fceaSAndreas Gohr 712*7017fceaSAndreas Gohr # 713*7017fceaSAndreas Gohr # Setext 714*7017fceaSAndreas Gohr 715*7017fceaSAndreas Gohr protected function blockSetextHeader($Line, array $Block = null) 716*7017fceaSAndreas Gohr { 717*7017fceaSAndreas Gohr if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted'])) 718*7017fceaSAndreas Gohr { 719*7017fceaSAndreas Gohr return; 720*7017fceaSAndreas Gohr } 721*7017fceaSAndreas Gohr 722*7017fceaSAndreas Gohr if (chop($Line['text'], $Line['text'][0]) === '') 723*7017fceaSAndreas Gohr { 724*7017fceaSAndreas Gohr $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2'; 725*7017fceaSAndreas Gohr 726*7017fceaSAndreas Gohr return $Block; 727*7017fceaSAndreas Gohr } 728*7017fceaSAndreas Gohr } 729*7017fceaSAndreas Gohr 730*7017fceaSAndreas Gohr # 731*7017fceaSAndreas Gohr # Markup 732*7017fceaSAndreas Gohr 733*7017fceaSAndreas Gohr protected function blockMarkup($Line) 734*7017fceaSAndreas Gohr { 735*7017fceaSAndreas Gohr if ($this->markupEscaped or $this->safeMode) 736*7017fceaSAndreas Gohr { 737*7017fceaSAndreas Gohr return; 738*7017fceaSAndreas Gohr } 739*7017fceaSAndreas Gohr 740*7017fceaSAndreas Gohr if (preg_match('/^<(\w[\w-]*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches)) 741*7017fceaSAndreas Gohr { 742*7017fceaSAndreas Gohr $element = strtolower($matches[1]); 743*7017fceaSAndreas Gohr 744*7017fceaSAndreas Gohr if (in_array($element, $this->textLevelElements)) 745*7017fceaSAndreas Gohr { 746*7017fceaSAndreas Gohr return; 747*7017fceaSAndreas Gohr } 748*7017fceaSAndreas Gohr 749*7017fceaSAndreas Gohr $Block = array( 750*7017fceaSAndreas Gohr 'name' => $matches[1], 751*7017fceaSAndreas Gohr 'depth' => 0, 752*7017fceaSAndreas Gohr 'markup' => $Line['text'], 753*7017fceaSAndreas Gohr ); 754*7017fceaSAndreas Gohr 755*7017fceaSAndreas Gohr $length = strlen($matches[0]); 756*7017fceaSAndreas Gohr 757*7017fceaSAndreas Gohr $remainder = substr($Line['text'], $length); 758*7017fceaSAndreas Gohr 759*7017fceaSAndreas Gohr if (trim($remainder) === '') 760*7017fceaSAndreas Gohr { 761*7017fceaSAndreas Gohr if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) 762*7017fceaSAndreas Gohr { 763*7017fceaSAndreas Gohr $Block['closed'] = true; 764*7017fceaSAndreas Gohr 765*7017fceaSAndreas Gohr $Block['void'] = true; 766*7017fceaSAndreas Gohr } 767*7017fceaSAndreas Gohr } 768*7017fceaSAndreas Gohr else 769*7017fceaSAndreas Gohr { 770*7017fceaSAndreas Gohr if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) 771*7017fceaSAndreas Gohr { 772*7017fceaSAndreas Gohr return; 773*7017fceaSAndreas Gohr } 774*7017fceaSAndreas Gohr 775*7017fceaSAndreas Gohr if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder)) 776*7017fceaSAndreas Gohr { 777*7017fceaSAndreas Gohr $Block['closed'] = true; 778*7017fceaSAndreas Gohr } 779*7017fceaSAndreas Gohr } 780*7017fceaSAndreas Gohr 781*7017fceaSAndreas Gohr return $Block; 782*7017fceaSAndreas Gohr } 783*7017fceaSAndreas Gohr } 784*7017fceaSAndreas Gohr 785*7017fceaSAndreas Gohr protected function blockMarkupContinue($Line, array $Block) 786*7017fceaSAndreas Gohr { 787*7017fceaSAndreas Gohr if (isset($Block['closed'])) 788*7017fceaSAndreas Gohr { 789*7017fceaSAndreas Gohr return; 790*7017fceaSAndreas Gohr } 791*7017fceaSAndreas Gohr 792*7017fceaSAndreas Gohr if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open 793*7017fceaSAndreas Gohr { 794*7017fceaSAndreas Gohr $Block['depth'] ++; 795*7017fceaSAndreas Gohr } 796*7017fceaSAndreas Gohr 797*7017fceaSAndreas Gohr if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close 798*7017fceaSAndreas Gohr { 799*7017fceaSAndreas Gohr if ($Block['depth'] > 0) 800*7017fceaSAndreas Gohr { 801*7017fceaSAndreas Gohr $Block['depth'] --; 802*7017fceaSAndreas Gohr } 803*7017fceaSAndreas Gohr else 804*7017fceaSAndreas Gohr { 805*7017fceaSAndreas Gohr $Block['closed'] = true; 806*7017fceaSAndreas Gohr } 807*7017fceaSAndreas Gohr } 808*7017fceaSAndreas Gohr 809*7017fceaSAndreas Gohr if (isset($Block['interrupted'])) 810*7017fceaSAndreas Gohr { 811*7017fceaSAndreas Gohr $Block['markup'] .= "\n"; 812*7017fceaSAndreas Gohr 813*7017fceaSAndreas Gohr unset($Block['interrupted']); 814*7017fceaSAndreas Gohr } 815*7017fceaSAndreas Gohr 816*7017fceaSAndreas Gohr $Block['markup'] .= "\n".$Line['body']; 817*7017fceaSAndreas Gohr 818*7017fceaSAndreas Gohr return $Block; 819*7017fceaSAndreas Gohr } 820*7017fceaSAndreas Gohr 821*7017fceaSAndreas Gohr # 822*7017fceaSAndreas Gohr # Reference 823*7017fceaSAndreas Gohr 824*7017fceaSAndreas Gohr protected function blockReference($Line) 825*7017fceaSAndreas Gohr { 826*7017fceaSAndreas Gohr if (preg_match('/^\[(.+?)\]:[ ]*<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches)) 827*7017fceaSAndreas Gohr { 828*7017fceaSAndreas Gohr $id = strtolower($matches[1]); 829*7017fceaSAndreas Gohr 830*7017fceaSAndreas Gohr $Data = array( 831*7017fceaSAndreas Gohr 'url' => $matches[2], 832*7017fceaSAndreas Gohr 'title' => null, 833*7017fceaSAndreas Gohr ); 834*7017fceaSAndreas Gohr 835*7017fceaSAndreas Gohr if (isset($matches[3])) 836*7017fceaSAndreas Gohr { 837*7017fceaSAndreas Gohr $Data['title'] = $matches[3]; 838*7017fceaSAndreas Gohr } 839*7017fceaSAndreas Gohr 840*7017fceaSAndreas Gohr $this->DefinitionData['Reference'][$id] = $Data; 841*7017fceaSAndreas Gohr 842*7017fceaSAndreas Gohr $Block = array( 843*7017fceaSAndreas Gohr 'hidden' => true, 844*7017fceaSAndreas Gohr ); 845*7017fceaSAndreas Gohr 846*7017fceaSAndreas Gohr return $Block; 847*7017fceaSAndreas Gohr } 848*7017fceaSAndreas Gohr } 849*7017fceaSAndreas Gohr 850*7017fceaSAndreas Gohr # 851*7017fceaSAndreas Gohr # Table 852*7017fceaSAndreas Gohr 853*7017fceaSAndreas Gohr protected function blockTable($Line, array $Block = null) 854*7017fceaSAndreas Gohr { 855*7017fceaSAndreas Gohr if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted'])) 856*7017fceaSAndreas Gohr { 857*7017fceaSAndreas Gohr return; 858*7017fceaSAndreas Gohr } 859*7017fceaSAndreas Gohr 860*7017fceaSAndreas Gohr if (strpos($Block['element']['text'], '|') !== false and chop($Line['text'], ' -:|') === '') 861*7017fceaSAndreas Gohr { 862*7017fceaSAndreas Gohr $alignments = array(); 863*7017fceaSAndreas Gohr 864*7017fceaSAndreas Gohr $divider = $Line['text']; 865*7017fceaSAndreas Gohr 866*7017fceaSAndreas Gohr $divider = trim($divider); 867*7017fceaSAndreas Gohr $divider = trim($divider, '|'); 868*7017fceaSAndreas Gohr 869*7017fceaSAndreas Gohr $dividerCells = explode('|', $divider); 870*7017fceaSAndreas Gohr 871*7017fceaSAndreas Gohr foreach ($dividerCells as $dividerCell) 872*7017fceaSAndreas Gohr { 873*7017fceaSAndreas Gohr $dividerCell = trim($dividerCell); 874*7017fceaSAndreas Gohr 875*7017fceaSAndreas Gohr if ($dividerCell === '') 876*7017fceaSAndreas Gohr { 877*7017fceaSAndreas Gohr continue; 878*7017fceaSAndreas Gohr } 879*7017fceaSAndreas Gohr 880*7017fceaSAndreas Gohr $alignment = null; 881*7017fceaSAndreas Gohr 882*7017fceaSAndreas Gohr if ($dividerCell[0] === ':') 883*7017fceaSAndreas Gohr { 884*7017fceaSAndreas Gohr $alignment = 'left'; 885*7017fceaSAndreas Gohr } 886*7017fceaSAndreas Gohr 887*7017fceaSAndreas Gohr if (substr($dividerCell, - 1) === ':') 888*7017fceaSAndreas Gohr { 889*7017fceaSAndreas Gohr $alignment = $alignment === 'left' ? 'center' : 'right'; 890*7017fceaSAndreas Gohr } 891*7017fceaSAndreas Gohr 892*7017fceaSAndreas Gohr $alignments []= $alignment; 893*7017fceaSAndreas Gohr } 894*7017fceaSAndreas Gohr 895*7017fceaSAndreas Gohr # ~ 896*7017fceaSAndreas Gohr 897*7017fceaSAndreas Gohr $HeaderElements = array(); 898*7017fceaSAndreas Gohr 899*7017fceaSAndreas Gohr $header = $Block['element']['text']; 900*7017fceaSAndreas Gohr 901*7017fceaSAndreas Gohr $header = trim($header); 902*7017fceaSAndreas Gohr $header = trim($header, '|'); 903*7017fceaSAndreas Gohr 904*7017fceaSAndreas Gohr $headerCells = explode('|', $header); 905*7017fceaSAndreas Gohr 906*7017fceaSAndreas Gohr foreach ($headerCells as $index => $headerCell) 907*7017fceaSAndreas Gohr { 908*7017fceaSAndreas Gohr $headerCell = trim($headerCell); 909*7017fceaSAndreas Gohr 910*7017fceaSAndreas Gohr $HeaderElement = array( 911*7017fceaSAndreas Gohr 'name' => 'th', 912*7017fceaSAndreas Gohr 'text' => $headerCell, 913*7017fceaSAndreas Gohr 'handler' => 'line', 914*7017fceaSAndreas Gohr ); 915*7017fceaSAndreas Gohr 916*7017fceaSAndreas Gohr if (isset($alignments[$index])) 917*7017fceaSAndreas Gohr { 918*7017fceaSAndreas Gohr $alignment = $alignments[$index]; 919*7017fceaSAndreas Gohr 920*7017fceaSAndreas Gohr $HeaderElement['attributes'] = array( 921*7017fceaSAndreas Gohr 'style' => 'text-align: '.$alignment.';', 922*7017fceaSAndreas Gohr ); 923*7017fceaSAndreas Gohr } 924*7017fceaSAndreas Gohr 925*7017fceaSAndreas Gohr $HeaderElements []= $HeaderElement; 926*7017fceaSAndreas Gohr } 927*7017fceaSAndreas Gohr 928*7017fceaSAndreas Gohr # ~ 929*7017fceaSAndreas Gohr 930*7017fceaSAndreas Gohr $Block = array( 931*7017fceaSAndreas Gohr 'alignments' => $alignments, 932*7017fceaSAndreas Gohr 'identified' => true, 933*7017fceaSAndreas Gohr 'element' => array( 934*7017fceaSAndreas Gohr 'name' => 'table', 935*7017fceaSAndreas Gohr 'handler' => 'elements', 936*7017fceaSAndreas Gohr ), 937*7017fceaSAndreas Gohr ); 938*7017fceaSAndreas Gohr 939*7017fceaSAndreas Gohr $Block['element']['text'] []= array( 940*7017fceaSAndreas Gohr 'name' => 'thead', 941*7017fceaSAndreas Gohr 'handler' => 'elements', 942*7017fceaSAndreas Gohr ); 943*7017fceaSAndreas Gohr 944*7017fceaSAndreas Gohr $Block['element']['text'] []= array( 945*7017fceaSAndreas Gohr 'name' => 'tbody', 946*7017fceaSAndreas Gohr 'handler' => 'elements', 947*7017fceaSAndreas Gohr 'text' => array(), 948*7017fceaSAndreas Gohr ); 949*7017fceaSAndreas Gohr 950*7017fceaSAndreas Gohr $Block['element']['text'][0]['text'] []= array( 951*7017fceaSAndreas Gohr 'name' => 'tr', 952*7017fceaSAndreas Gohr 'handler' => 'elements', 953*7017fceaSAndreas Gohr 'text' => $HeaderElements, 954*7017fceaSAndreas Gohr ); 955*7017fceaSAndreas Gohr 956*7017fceaSAndreas Gohr return $Block; 957*7017fceaSAndreas Gohr } 958*7017fceaSAndreas Gohr } 959*7017fceaSAndreas Gohr 960*7017fceaSAndreas Gohr protected function blockTableContinue($Line, array $Block) 961*7017fceaSAndreas Gohr { 962*7017fceaSAndreas Gohr if (isset($Block['interrupted'])) 963*7017fceaSAndreas Gohr { 964*7017fceaSAndreas Gohr return; 965*7017fceaSAndreas Gohr } 966*7017fceaSAndreas Gohr 967*7017fceaSAndreas Gohr if ($Line['text'][0] === '|' or strpos($Line['text'], '|')) 968*7017fceaSAndreas Gohr { 969*7017fceaSAndreas Gohr $Elements = array(); 970*7017fceaSAndreas Gohr 971*7017fceaSAndreas Gohr $row = $Line['text']; 972*7017fceaSAndreas Gohr 973*7017fceaSAndreas Gohr $row = trim($row); 974*7017fceaSAndreas Gohr $row = trim($row, '|'); 975*7017fceaSAndreas Gohr 976*7017fceaSAndreas Gohr preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches); 977*7017fceaSAndreas Gohr 978*7017fceaSAndreas Gohr foreach ($matches[0] as $index => $cell) 979*7017fceaSAndreas Gohr { 980*7017fceaSAndreas Gohr $cell = trim($cell); 981*7017fceaSAndreas Gohr 982*7017fceaSAndreas Gohr $Element = array( 983*7017fceaSAndreas Gohr 'name' => 'td', 984*7017fceaSAndreas Gohr 'handler' => 'line', 985*7017fceaSAndreas Gohr 'text' => $cell, 986*7017fceaSAndreas Gohr ); 987*7017fceaSAndreas Gohr 988*7017fceaSAndreas Gohr if (isset($Block['alignments'][$index])) 989*7017fceaSAndreas Gohr { 990*7017fceaSAndreas Gohr $Element['attributes'] = array( 991*7017fceaSAndreas Gohr 'style' => 'text-align: '.$Block['alignments'][$index].';', 992*7017fceaSAndreas Gohr ); 993*7017fceaSAndreas Gohr } 994*7017fceaSAndreas Gohr 995*7017fceaSAndreas Gohr $Elements []= $Element; 996*7017fceaSAndreas Gohr } 997*7017fceaSAndreas Gohr 998*7017fceaSAndreas Gohr $Element = array( 999*7017fceaSAndreas Gohr 'name' => 'tr', 1000*7017fceaSAndreas Gohr 'handler' => 'elements', 1001*7017fceaSAndreas Gohr 'text' => $Elements, 1002*7017fceaSAndreas Gohr ); 1003*7017fceaSAndreas Gohr 1004*7017fceaSAndreas Gohr $Block['element']['text'][1]['text'] []= $Element; 1005*7017fceaSAndreas Gohr 1006*7017fceaSAndreas Gohr return $Block; 1007*7017fceaSAndreas Gohr } 1008*7017fceaSAndreas Gohr } 1009*7017fceaSAndreas Gohr 1010*7017fceaSAndreas Gohr # 1011*7017fceaSAndreas Gohr # ~ 1012*7017fceaSAndreas Gohr # 1013*7017fceaSAndreas Gohr 1014*7017fceaSAndreas Gohr protected function paragraph($Line) 1015*7017fceaSAndreas Gohr { 1016*7017fceaSAndreas Gohr $Block = array( 1017*7017fceaSAndreas Gohr 'element' => array( 1018*7017fceaSAndreas Gohr 'name' => 'p', 1019*7017fceaSAndreas Gohr 'text' => $Line['text'], 1020*7017fceaSAndreas Gohr 'handler' => 'line', 1021*7017fceaSAndreas Gohr ), 1022*7017fceaSAndreas Gohr ); 1023*7017fceaSAndreas Gohr 1024*7017fceaSAndreas Gohr return $Block; 1025*7017fceaSAndreas Gohr } 1026*7017fceaSAndreas Gohr 1027*7017fceaSAndreas Gohr # 1028*7017fceaSAndreas Gohr # Inline Elements 1029*7017fceaSAndreas Gohr # 1030*7017fceaSAndreas Gohr 1031*7017fceaSAndreas Gohr protected $InlineTypes = array( 1032*7017fceaSAndreas Gohr '"' => array('SpecialCharacter'), 1033*7017fceaSAndreas Gohr '!' => array('Image'), 1034*7017fceaSAndreas Gohr '&' => array('SpecialCharacter'), 1035*7017fceaSAndreas Gohr '*' => array('Emphasis'), 1036*7017fceaSAndreas Gohr ':' => array('Url'), 1037*7017fceaSAndreas Gohr '<' => array('UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'), 1038*7017fceaSAndreas Gohr '>' => array('SpecialCharacter'), 1039*7017fceaSAndreas Gohr '[' => array('Link'), 1040*7017fceaSAndreas Gohr '_' => array('Emphasis'), 1041*7017fceaSAndreas Gohr '`' => array('Code'), 1042*7017fceaSAndreas Gohr '~' => array('Strikethrough'), 1043*7017fceaSAndreas Gohr '\\' => array('EscapeSequence'), 1044*7017fceaSAndreas Gohr ); 1045*7017fceaSAndreas Gohr 1046*7017fceaSAndreas Gohr # ~ 1047*7017fceaSAndreas Gohr 1048*7017fceaSAndreas Gohr protected $inlineMarkerList = '!"*_&[:<>`~\\'; 1049*7017fceaSAndreas Gohr 1050*7017fceaSAndreas Gohr # 1051*7017fceaSAndreas Gohr # ~ 1052*7017fceaSAndreas Gohr # 1053*7017fceaSAndreas Gohr 1054*7017fceaSAndreas Gohr public function line($text, $nonNestables=array()) 1055*7017fceaSAndreas Gohr { 1056*7017fceaSAndreas Gohr $markup = ''; 1057*7017fceaSAndreas Gohr 1058*7017fceaSAndreas Gohr # $excerpt is based on the first occurrence of a marker 1059*7017fceaSAndreas Gohr 1060*7017fceaSAndreas Gohr while ($excerpt = strpbrk($text, $this->inlineMarkerList)) 1061*7017fceaSAndreas Gohr { 1062*7017fceaSAndreas Gohr $marker = $excerpt[0]; 1063*7017fceaSAndreas Gohr 1064*7017fceaSAndreas Gohr $markerPosition = strpos($text, $marker); 1065*7017fceaSAndreas Gohr 1066*7017fceaSAndreas Gohr $Excerpt = array('text' => $excerpt, 'context' => $text); 1067*7017fceaSAndreas Gohr 1068*7017fceaSAndreas Gohr foreach ($this->InlineTypes[$marker] as $inlineType) 1069*7017fceaSAndreas Gohr { 1070*7017fceaSAndreas Gohr # check to see if the current inline type is nestable in the current context 1071*7017fceaSAndreas Gohr 1072*7017fceaSAndreas Gohr if ( ! empty($nonNestables) and in_array($inlineType, $nonNestables)) 1073*7017fceaSAndreas Gohr { 1074*7017fceaSAndreas Gohr continue; 1075*7017fceaSAndreas Gohr } 1076*7017fceaSAndreas Gohr 1077*7017fceaSAndreas Gohr $Inline = $this->{'inline'.$inlineType}($Excerpt); 1078*7017fceaSAndreas Gohr 1079*7017fceaSAndreas Gohr if ( ! isset($Inline)) 1080*7017fceaSAndreas Gohr { 1081*7017fceaSAndreas Gohr continue; 1082*7017fceaSAndreas Gohr } 1083*7017fceaSAndreas Gohr 1084*7017fceaSAndreas Gohr # makes sure that the inline belongs to "our" marker 1085*7017fceaSAndreas Gohr 1086*7017fceaSAndreas Gohr if (isset($Inline['position']) and $Inline['position'] > $markerPosition) 1087*7017fceaSAndreas Gohr { 1088*7017fceaSAndreas Gohr continue; 1089*7017fceaSAndreas Gohr } 1090*7017fceaSAndreas Gohr 1091*7017fceaSAndreas Gohr # sets a default inline position 1092*7017fceaSAndreas Gohr 1093*7017fceaSAndreas Gohr if ( ! isset($Inline['position'])) 1094*7017fceaSAndreas Gohr { 1095*7017fceaSAndreas Gohr $Inline['position'] = $markerPosition; 1096*7017fceaSAndreas Gohr } 1097*7017fceaSAndreas Gohr 1098*7017fceaSAndreas Gohr # cause the new element to 'inherit' our non nestables 1099*7017fceaSAndreas Gohr 1100*7017fceaSAndreas Gohr foreach ($nonNestables as $non_nestable) 1101*7017fceaSAndreas Gohr { 1102*7017fceaSAndreas Gohr $Inline['element']['nonNestables'][] = $non_nestable; 1103*7017fceaSAndreas Gohr } 1104*7017fceaSAndreas Gohr 1105*7017fceaSAndreas Gohr # the text that comes before the inline 1106*7017fceaSAndreas Gohr $unmarkedText = substr($text, 0, $Inline['position']); 1107*7017fceaSAndreas Gohr 1108*7017fceaSAndreas Gohr # compile the unmarked text 1109*7017fceaSAndreas Gohr $markup .= $this->unmarkedText($unmarkedText); 1110*7017fceaSAndreas Gohr 1111*7017fceaSAndreas Gohr # compile the inline 1112*7017fceaSAndreas Gohr $markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']); 1113*7017fceaSAndreas Gohr 1114*7017fceaSAndreas Gohr # remove the examined text 1115*7017fceaSAndreas Gohr $text = substr($text, $Inline['position'] + $Inline['extent']); 1116*7017fceaSAndreas Gohr 1117*7017fceaSAndreas Gohr continue 2; 1118*7017fceaSAndreas Gohr } 1119*7017fceaSAndreas Gohr 1120*7017fceaSAndreas Gohr # the marker does not belong to an inline 1121*7017fceaSAndreas Gohr 1122*7017fceaSAndreas Gohr $unmarkedText = substr($text, 0, $markerPosition + 1); 1123*7017fceaSAndreas Gohr 1124*7017fceaSAndreas Gohr $markup .= $this->unmarkedText($unmarkedText); 1125*7017fceaSAndreas Gohr 1126*7017fceaSAndreas Gohr $text = substr($text, $markerPosition + 1); 1127*7017fceaSAndreas Gohr } 1128*7017fceaSAndreas Gohr 1129*7017fceaSAndreas Gohr $markup .= $this->unmarkedText($text); 1130*7017fceaSAndreas Gohr 1131*7017fceaSAndreas Gohr return $markup; 1132*7017fceaSAndreas Gohr } 1133*7017fceaSAndreas Gohr 1134*7017fceaSAndreas Gohr # 1135*7017fceaSAndreas Gohr # ~ 1136*7017fceaSAndreas Gohr # 1137*7017fceaSAndreas Gohr 1138*7017fceaSAndreas Gohr protected function inlineCode($Excerpt) 1139*7017fceaSAndreas Gohr { 1140*7017fceaSAndreas Gohr $marker = $Excerpt['text'][0]; 1141*7017fceaSAndreas Gohr 1142*7017fceaSAndreas Gohr if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/s', $Excerpt['text'], $matches)) 1143*7017fceaSAndreas Gohr { 1144*7017fceaSAndreas Gohr $text = $matches[2]; 1145*7017fceaSAndreas Gohr $text = preg_replace("/[ ]*\n/", ' ', $text); 1146*7017fceaSAndreas Gohr 1147*7017fceaSAndreas Gohr return array( 1148*7017fceaSAndreas Gohr 'extent' => strlen($matches[0]), 1149*7017fceaSAndreas Gohr 'element' => array( 1150*7017fceaSAndreas Gohr 'name' => 'code', 1151*7017fceaSAndreas Gohr 'text' => $text, 1152*7017fceaSAndreas Gohr ), 1153*7017fceaSAndreas Gohr ); 1154*7017fceaSAndreas Gohr } 1155*7017fceaSAndreas Gohr } 1156*7017fceaSAndreas Gohr 1157*7017fceaSAndreas Gohr protected function inlineEmailTag($Excerpt) 1158*7017fceaSAndreas Gohr { 1159*7017fceaSAndreas Gohr if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches)) 1160*7017fceaSAndreas Gohr { 1161*7017fceaSAndreas Gohr $url = $matches[1]; 1162*7017fceaSAndreas Gohr 1163*7017fceaSAndreas Gohr if ( ! isset($matches[2])) 1164*7017fceaSAndreas Gohr { 1165*7017fceaSAndreas Gohr $url = 'mailto:' . $url; 1166*7017fceaSAndreas Gohr } 1167*7017fceaSAndreas Gohr 1168*7017fceaSAndreas Gohr return array( 1169*7017fceaSAndreas Gohr 'extent' => strlen($matches[0]), 1170*7017fceaSAndreas Gohr 'element' => array( 1171*7017fceaSAndreas Gohr 'name' => 'a', 1172*7017fceaSAndreas Gohr 'text' => $matches[1], 1173*7017fceaSAndreas Gohr 'attributes' => array( 1174*7017fceaSAndreas Gohr 'href' => $url, 1175*7017fceaSAndreas Gohr ), 1176*7017fceaSAndreas Gohr ), 1177*7017fceaSAndreas Gohr ); 1178*7017fceaSAndreas Gohr } 1179*7017fceaSAndreas Gohr } 1180*7017fceaSAndreas Gohr 1181*7017fceaSAndreas Gohr protected function inlineEmphasis($Excerpt) 1182*7017fceaSAndreas Gohr { 1183*7017fceaSAndreas Gohr if ( ! isset($Excerpt['text'][1])) 1184*7017fceaSAndreas Gohr { 1185*7017fceaSAndreas Gohr return; 1186*7017fceaSAndreas Gohr } 1187*7017fceaSAndreas Gohr 1188*7017fceaSAndreas Gohr $marker = $Excerpt['text'][0]; 1189*7017fceaSAndreas Gohr 1190*7017fceaSAndreas Gohr if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches)) 1191*7017fceaSAndreas Gohr { 1192*7017fceaSAndreas Gohr $emphasis = 'strong'; 1193*7017fceaSAndreas Gohr } 1194*7017fceaSAndreas Gohr elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches)) 1195*7017fceaSAndreas Gohr { 1196*7017fceaSAndreas Gohr $emphasis = 'em'; 1197*7017fceaSAndreas Gohr } 1198*7017fceaSAndreas Gohr else 1199*7017fceaSAndreas Gohr { 1200*7017fceaSAndreas Gohr return; 1201*7017fceaSAndreas Gohr } 1202*7017fceaSAndreas Gohr 1203*7017fceaSAndreas Gohr return array( 1204*7017fceaSAndreas Gohr 'extent' => strlen($matches[0]), 1205*7017fceaSAndreas Gohr 'element' => array( 1206*7017fceaSAndreas Gohr 'name' => $emphasis, 1207*7017fceaSAndreas Gohr 'handler' => 'line', 1208*7017fceaSAndreas Gohr 'text' => $matches[1], 1209*7017fceaSAndreas Gohr ), 1210*7017fceaSAndreas Gohr ); 1211*7017fceaSAndreas Gohr } 1212*7017fceaSAndreas Gohr 1213*7017fceaSAndreas Gohr protected function inlineEscapeSequence($Excerpt) 1214*7017fceaSAndreas Gohr { 1215*7017fceaSAndreas Gohr if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters)) 1216*7017fceaSAndreas Gohr { 1217*7017fceaSAndreas Gohr return array( 1218*7017fceaSAndreas Gohr 'markup' => $Excerpt['text'][1], 1219*7017fceaSAndreas Gohr 'extent' => 2, 1220*7017fceaSAndreas Gohr ); 1221*7017fceaSAndreas Gohr } 1222*7017fceaSAndreas Gohr } 1223*7017fceaSAndreas Gohr 1224*7017fceaSAndreas Gohr protected function inlineImage($Excerpt) 1225*7017fceaSAndreas Gohr { 1226*7017fceaSAndreas Gohr if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[') 1227*7017fceaSAndreas Gohr { 1228*7017fceaSAndreas Gohr return; 1229*7017fceaSAndreas Gohr } 1230*7017fceaSAndreas Gohr 1231*7017fceaSAndreas Gohr $Excerpt['text']= substr($Excerpt['text'], 1); 1232*7017fceaSAndreas Gohr 1233*7017fceaSAndreas Gohr $Link = $this->inlineLink($Excerpt); 1234*7017fceaSAndreas Gohr 1235*7017fceaSAndreas Gohr if ($Link === null) 1236*7017fceaSAndreas Gohr { 1237*7017fceaSAndreas Gohr return; 1238*7017fceaSAndreas Gohr } 1239*7017fceaSAndreas Gohr 1240*7017fceaSAndreas Gohr $Inline = array( 1241*7017fceaSAndreas Gohr 'extent' => $Link['extent'] + 1, 1242*7017fceaSAndreas Gohr 'element' => array( 1243*7017fceaSAndreas Gohr 'name' => 'img', 1244*7017fceaSAndreas Gohr 'attributes' => array( 1245*7017fceaSAndreas Gohr 'src' => $Link['element']['attributes']['href'], 1246*7017fceaSAndreas Gohr 'alt' => $Link['element']['text'], 1247*7017fceaSAndreas Gohr ), 1248*7017fceaSAndreas Gohr ), 1249*7017fceaSAndreas Gohr ); 1250*7017fceaSAndreas Gohr 1251*7017fceaSAndreas Gohr $Inline['element']['attributes'] += $Link['element']['attributes']; 1252*7017fceaSAndreas Gohr 1253*7017fceaSAndreas Gohr unset($Inline['element']['attributes']['href']); 1254*7017fceaSAndreas Gohr 1255*7017fceaSAndreas Gohr return $Inline; 1256*7017fceaSAndreas Gohr } 1257*7017fceaSAndreas Gohr 1258*7017fceaSAndreas Gohr protected function inlineLink($Excerpt) 1259*7017fceaSAndreas Gohr { 1260*7017fceaSAndreas Gohr $Element = array( 1261*7017fceaSAndreas Gohr 'name' => 'a', 1262*7017fceaSAndreas Gohr 'handler' => 'line', 1263*7017fceaSAndreas Gohr 'nonNestables' => array('Url', 'Link'), 1264*7017fceaSAndreas Gohr 'text' => null, 1265*7017fceaSAndreas Gohr 'attributes' => array( 1266*7017fceaSAndreas Gohr 'href' => null, 1267*7017fceaSAndreas Gohr 'title' => null, 1268*7017fceaSAndreas Gohr ), 1269*7017fceaSAndreas Gohr ); 1270*7017fceaSAndreas Gohr 1271*7017fceaSAndreas Gohr $extent = 0; 1272*7017fceaSAndreas Gohr 1273*7017fceaSAndreas Gohr $remainder = $Excerpt['text']; 1274*7017fceaSAndreas Gohr 1275*7017fceaSAndreas Gohr if (preg_match('/\[((?:[^][]++|(?R))*+)\]/', $remainder, $matches)) 1276*7017fceaSAndreas Gohr { 1277*7017fceaSAndreas Gohr $Element['text'] = $matches[1]; 1278*7017fceaSAndreas Gohr 1279*7017fceaSAndreas Gohr $extent += strlen($matches[0]); 1280*7017fceaSAndreas Gohr 1281*7017fceaSAndreas Gohr $remainder = substr($remainder, $extent); 1282*7017fceaSAndreas Gohr } 1283*7017fceaSAndreas Gohr else 1284*7017fceaSAndreas Gohr { 1285*7017fceaSAndreas Gohr return; 1286*7017fceaSAndreas Gohr } 1287*7017fceaSAndreas Gohr 1288*7017fceaSAndreas Gohr if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*"|\'[^\']*\'))?\s*[)]/', $remainder, $matches)) 1289*7017fceaSAndreas Gohr { 1290*7017fceaSAndreas Gohr $Element['attributes']['href'] = $matches[1]; 1291*7017fceaSAndreas Gohr 1292*7017fceaSAndreas Gohr if (isset($matches[2])) 1293*7017fceaSAndreas Gohr { 1294*7017fceaSAndreas Gohr $Element['attributes']['title'] = substr($matches[2], 1, - 1); 1295*7017fceaSAndreas Gohr } 1296*7017fceaSAndreas Gohr 1297*7017fceaSAndreas Gohr $extent += strlen($matches[0]); 1298*7017fceaSAndreas Gohr } 1299*7017fceaSAndreas Gohr else 1300*7017fceaSAndreas Gohr { 1301*7017fceaSAndreas Gohr if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches)) 1302*7017fceaSAndreas Gohr { 1303*7017fceaSAndreas Gohr $definition = strlen($matches[1]) ? $matches[1] : $Element['text']; 1304*7017fceaSAndreas Gohr $definition = strtolower($definition); 1305*7017fceaSAndreas Gohr 1306*7017fceaSAndreas Gohr $extent += strlen($matches[0]); 1307*7017fceaSAndreas Gohr } 1308*7017fceaSAndreas Gohr else 1309*7017fceaSAndreas Gohr { 1310*7017fceaSAndreas Gohr $definition = strtolower($Element['text']); 1311*7017fceaSAndreas Gohr } 1312*7017fceaSAndreas Gohr 1313*7017fceaSAndreas Gohr if ( ! isset($this->DefinitionData['Reference'][$definition])) 1314*7017fceaSAndreas Gohr { 1315*7017fceaSAndreas Gohr return; 1316*7017fceaSAndreas Gohr } 1317*7017fceaSAndreas Gohr 1318*7017fceaSAndreas Gohr $Definition = $this->DefinitionData['Reference'][$definition]; 1319*7017fceaSAndreas Gohr 1320*7017fceaSAndreas Gohr $Element['attributes']['href'] = $Definition['url']; 1321*7017fceaSAndreas Gohr $Element['attributes']['title'] = $Definition['title']; 1322*7017fceaSAndreas Gohr } 1323*7017fceaSAndreas Gohr 1324*7017fceaSAndreas Gohr return array( 1325*7017fceaSAndreas Gohr 'extent' => $extent, 1326*7017fceaSAndreas Gohr 'element' => $Element, 1327*7017fceaSAndreas Gohr ); 1328*7017fceaSAndreas Gohr } 1329*7017fceaSAndreas Gohr 1330*7017fceaSAndreas Gohr protected function inlineMarkup($Excerpt) 1331*7017fceaSAndreas Gohr { 1332*7017fceaSAndreas Gohr if ($this->markupEscaped or $this->safeMode or strpos($Excerpt['text'], '>') === false) 1333*7017fceaSAndreas Gohr { 1334*7017fceaSAndreas Gohr return; 1335*7017fceaSAndreas Gohr } 1336*7017fceaSAndreas Gohr 1337*7017fceaSAndreas Gohr if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w[\w-]*[ ]*>/s', $Excerpt['text'], $matches)) 1338*7017fceaSAndreas Gohr { 1339*7017fceaSAndreas Gohr return array( 1340*7017fceaSAndreas Gohr 'markup' => $matches[0], 1341*7017fceaSAndreas Gohr 'extent' => strlen($matches[0]), 1342*7017fceaSAndreas Gohr ); 1343*7017fceaSAndreas Gohr } 1344*7017fceaSAndreas Gohr 1345*7017fceaSAndreas Gohr if ($Excerpt['text'][1] === '!' and preg_match('/^<!---?[^>-](?:-?[^-])*-->/s', $Excerpt['text'], $matches)) 1346*7017fceaSAndreas Gohr { 1347*7017fceaSAndreas Gohr return array( 1348*7017fceaSAndreas Gohr 'markup' => $matches[0], 1349*7017fceaSAndreas Gohr 'extent' => strlen($matches[0]), 1350*7017fceaSAndreas Gohr ); 1351*7017fceaSAndreas Gohr } 1352*7017fceaSAndreas Gohr 1353*7017fceaSAndreas Gohr if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w[\w-]*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches)) 1354*7017fceaSAndreas Gohr { 1355*7017fceaSAndreas Gohr return array( 1356*7017fceaSAndreas Gohr 'markup' => $matches[0], 1357*7017fceaSAndreas Gohr 'extent' => strlen($matches[0]), 1358*7017fceaSAndreas Gohr ); 1359*7017fceaSAndreas Gohr } 1360*7017fceaSAndreas Gohr } 1361*7017fceaSAndreas Gohr 1362*7017fceaSAndreas Gohr protected function inlineSpecialCharacter($Excerpt) 1363*7017fceaSAndreas Gohr { 1364*7017fceaSAndreas Gohr if ($Excerpt['text'][0] === '&' and ! preg_match('/^&#?\w+;/', $Excerpt['text'])) 1365*7017fceaSAndreas Gohr { 1366*7017fceaSAndreas Gohr return array( 1367*7017fceaSAndreas Gohr 'markup' => '&', 1368*7017fceaSAndreas Gohr 'extent' => 1, 1369*7017fceaSAndreas Gohr ); 1370*7017fceaSAndreas Gohr } 1371*7017fceaSAndreas Gohr 1372*7017fceaSAndreas Gohr $SpecialCharacter = array('>' => 'gt', '<' => 'lt', '"' => 'quot'); 1373*7017fceaSAndreas Gohr 1374*7017fceaSAndreas Gohr if (isset($SpecialCharacter[$Excerpt['text'][0]])) 1375*7017fceaSAndreas Gohr { 1376*7017fceaSAndreas Gohr return array( 1377*7017fceaSAndreas Gohr 'markup' => '&'.$SpecialCharacter[$Excerpt['text'][0]].';', 1378*7017fceaSAndreas Gohr 'extent' => 1, 1379*7017fceaSAndreas Gohr ); 1380*7017fceaSAndreas Gohr } 1381*7017fceaSAndreas Gohr } 1382*7017fceaSAndreas Gohr 1383*7017fceaSAndreas Gohr protected function inlineStrikethrough($Excerpt) 1384*7017fceaSAndreas Gohr { 1385*7017fceaSAndreas Gohr if ( ! isset($Excerpt['text'][1])) 1386*7017fceaSAndreas Gohr { 1387*7017fceaSAndreas Gohr return; 1388*7017fceaSAndreas Gohr } 1389*7017fceaSAndreas Gohr 1390*7017fceaSAndreas Gohr if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches)) 1391*7017fceaSAndreas Gohr { 1392*7017fceaSAndreas Gohr return array( 1393*7017fceaSAndreas Gohr 'extent' => strlen($matches[0]), 1394*7017fceaSAndreas Gohr 'element' => array( 1395*7017fceaSAndreas Gohr 'name' => 'del', 1396*7017fceaSAndreas Gohr 'text' => $matches[1], 1397*7017fceaSAndreas Gohr 'handler' => 'line', 1398*7017fceaSAndreas Gohr ), 1399*7017fceaSAndreas Gohr ); 1400*7017fceaSAndreas Gohr } 1401*7017fceaSAndreas Gohr } 1402*7017fceaSAndreas Gohr 1403*7017fceaSAndreas Gohr protected function inlineUrl($Excerpt) 1404*7017fceaSAndreas Gohr { 1405*7017fceaSAndreas Gohr if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/') 1406*7017fceaSAndreas Gohr { 1407*7017fceaSAndreas Gohr return; 1408*7017fceaSAndreas Gohr } 1409*7017fceaSAndreas Gohr 1410*7017fceaSAndreas Gohr if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE)) 1411*7017fceaSAndreas Gohr { 1412*7017fceaSAndreas Gohr $url = $matches[0][0]; 1413*7017fceaSAndreas Gohr 1414*7017fceaSAndreas Gohr $Inline = array( 1415*7017fceaSAndreas Gohr 'extent' => strlen($matches[0][0]), 1416*7017fceaSAndreas Gohr 'position' => $matches[0][1], 1417*7017fceaSAndreas Gohr 'element' => array( 1418*7017fceaSAndreas Gohr 'name' => 'a', 1419*7017fceaSAndreas Gohr 'text' => $url, 1420*7017fceaSAndreas Gohr 'attributes' => array( 1421*7017fceaSAndreas Gohr 'href' => $url, 1422*7017fceaSAndreas Gohr ), 1423*7017fceaSAndreas Gohr ), 1424*7017fceaSAndreas Gohr ); 1425*7017fceaSAndreas Gohr 1426*7017fceaSAndreas Gohr return $Inline; 1427*7017fceaSAndreas Gohr } 1428*7017fceaSAndreas Gohr } 1429*7017fceaSAndreas Gohr 1430*7017fceaSAndreas Gohr protected function inlineUrlTag($Excerpt) 1431*7017fceaSAndreas Gohr { 1432*7017fceaSAndreas Gohr if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches)) 1433*7017fceaSAndreas Gohr { 1434*7017fceaSAndreas Gohr $url = $matches[1]; 1435*7017fceaSAndreas Gohr 1436*7017fceaSAndreas Gohr return array( 1437*7017fceaSAndreas Gohr 'extent' => strlen($matches[0]), 1438*7017fceaSAndreas Gohr 'element' => array( 1439*7017fceaSAndreas Gohr 'name' => 'a', 1440*7017fceaSAndreas Gohr 'text' => $url, 1441*7017fceaSAndreas Gohr 'attributes' => array( 1442*7017fceaSAndreas Gohr 'href' => $url, 1443*7017fceaSAndreas Gohr ), 1444*7017fceaSAndreas Gohr ), 1445*7017fceaSAndreas Gohr ); 1446*7017fceaSAndreas Gohr } 1447*7017fceaSAndreas Gohr } 1448*7017fceaSAndreas Gohr 1449*7017fceaSAndreas Gohr # ~ 1450*7017fceaSAndreas Gohr 1451*7017fceaSAndreas Gohr protected function unmarkedText($text) 1452*7017fceaSAndreas Gohr { 1453*7017fceaSAndreas Gohr if ($this->breaksEnabled) 1454*7017fceaSAndreas Gohr { 1455*7017fceaSAndreas Gohr $text = preg_replace('/[ ]*\n/', "<br />\n", $text); 1456*7017fceaSAndreas Gohr } 1457*7017fceaSAndreas Gohr else 1458*7017fceaSAndreas Gohr { 1459*7017fceaSAndreas Gohr $text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "<br />\n", $text); 1460*7017fceaSAndreas Gohr $text = str_replace(" \n", "\n", $text); 1461*7017fceaSAndreas Gohr } 1462*7017fceaSAndreas Gohr 1463*7017fceaSAndreas Gohr return $text; 1464*7017fceaSAndreas Gohr } 1465*7017fceaSAndreas Gohr 1466*7017fceaSAndreas Gohr # 1467*7017fceaSAndreas Gohr # Handlers 1468*7017fceaSAndreas Gohr # 1469*7017fceaSAndreas Gohr 1470*7017fceaSAndreas Gohr protected function element(array $Element) 1471*7017fceaSAndreas Gohr { 1472*7017fceaSAndreas Gohr if ($this->safeMode) 1473*7017fceaSAndreas Gohr { 1474*7017fceaSAndreas Gohr $Element = $this->sanitiseElement($Element); 1475*7017fceaSAndreas Gohr } 1476*7017fceaSAndreas Gohr 1477*7017fceaSAndreas Gohr $markup = '<'.$Element['name']; 1478*7017fceaSAndreas Gohr 1479*7017fceaSAndreas Gohr if (isset($Element['attributes'])) 1480*7017fceaSAndreas Gohr { 1481*7017fceaSAndreas Gohr foreach ($Element['attributes'] as $name => $value) 1482*7017fceaSAndreas Gohr { 1483*7017fceaSAndreas Gohr if ($value === null) 1484*7017fceaSAndreas Gohr { 1485*7017fceaSAndreas Gohr continue; 1486*7017fceaSAndreas Gohr } 1487*7017fceaSAndreas Gohr 1488*7017fceaSAndreas Gohr $markup .= ' '.$name.'="'.self::escape($value).'"'; 1489*7017fceaSAndreas Gohr } 1490*7017fceaSAndreas Gohr } 1491*7017fceaSAndreas Gohr 1492*7017fceaSAndreas Gohr $permitRawHtml = false; 1493*7017fceaSAndreas Gohr 1494*7017fceaSAndreas Gohr if (isset($Element['text'])) 1495*7017fceaSAndreas Gohr { 1496*7017fceaSAndreas Gohr $text = $Element['text']; 1497*7017fceaSAndreas Gohr } 1498*7017fceaSAndreas Gohr // very strongly consider an alternative if you're writing an 1499*7017fceaSAndreas Gohr // extension 1500*7017fceaSAndreas Gohr elseif (isset($Element['rawHtml'])) 1501*7017fceaSAndreas Gohr { 1502*7017fceaSAndreas Gohr $text = $Element['rawHtml']; 1503*7017fceaSAndreas Gohr $allowRawHtmlInSafeMode = isset($Element['allowRawHtmlInSafeMode']) && $Element['allowRawHtmlInSafeMode']; 1504*7017fceaSAndreas Gohr $permitRawHtml = !$this->safeMode || $allowRawHtmlInSafeMode; 1505*7017fceaSAndreas Gohr } 1506*7017fceaSAndreas Gohr 1507*7017fceaSAndreas Gohr if (isset($text)) 1508*7017fceaSAndreas Gohr { 1509*7017fceaSAndreas Gohr $markup .= '>'; 1510*7017fceaSAndreas Gohr 1511*7017fceaSAndreas Gohr if (!isset($Element['nonNestables'])) 1512*7017fceaSAndreas Gohr { 1513*7017fceaSAndreas Gohr $Element['nonNestables'] = array(); 1514*7017fceaSAndreas Gohr } 1515*7017fceaSAndreas Gohr 1516*7017fceaSAndreas Gohr if (isset($Element['handler'])) 1517*7017fceaSAndreas Gohr { 1518*7017fceaSAndreas Gohr $markup .= $this->{$Element['handler']}($text, $Element['nonNestables']); 1519*7017fceaSAndreas Gohr } 1520*7017fceaSAndreas Gohr elseif (!$permitRawHtml) 1521*7017fceaSAndreas Gohr { 1522*7017fceaSAndreas Gohr $markup .= self::escape($text, true); 1523*7017fceaSAndreas Gohr } 1524*7017fceaSAndreas Gohr else 1525*7017fceaSAndreas Gohr { 1526*7017fceaSAndreas Gohr $markup .= $text; 1527*7017fceaSAndreas Gohr } 1528*7017fceaSAndreas Gohr 1529*7017fceaSAndreas Gohr $markup .= '</'.$Element['name'].'>'; 1530*7017fceaSAndreas Gohr } 1531*7017fceaSAndreas Gohr else 1532*7017fceaSAndreas Gohr { 1533*7017fceaSAndreas Gohr $markup .= ' />'; 1534*7017fceaSAndreas Gohr } 1535*7017fceaSAndreas Gohr 1536*7017fceaSAndreas Gohr return $markup; 1537*7017fceaSAndreas Gohr } 1538*7017fceaSAndreas Gohr 1539*7017fceaSAndreas Gohr protected function elements(array $Elements) 1540*7017fceaSAndreas Gohr { 1541*7017fceaSAndreas Gohr $markup = ''; 1542*7017fceaSAndreas Gohr 1543*7017fceaSAndreas Gohr foreach ($Elements as $Element) 1544*7017fceaSAndreas Gohr { 1545*7017fceaSAndreas Gohr $markup .= "\n" . $this->element($Element); 1546*7017fceaSAndreas Gohr } 1547*7017fceaSAndreas Gohr 1548*7017fceaSAndreas Gohr $markup .= "\n"; 1549*7017fceaSAndreas Gohr 1550*7017fceaSAndreas Gohr return $markup; 1551*7017fceaSAndreas Gohr } 1552*7017fceaSAndreas Gohr 1553*7017fceaSAndreas Gohr # ~ 1554*7017fceaSAndreas Gohr 1555*7017fceaSAndreas Gohr protected function li($lines) 1556*7017fceaSAndreas Gohr { 1557*7017fceaSAndreas Gohr $markup = $this->lines($lines); 1558*7017fceaSAndreas Gohr 1559*7017fceaSAndreas Gohr $trimmedMarkup = trim($markup); 1560*7017fceaSAndreas Gohr 1561*7017fceaSAndreas Gohr if ( ! in_array('', $lines) and substr($trimmedMarkup, 0, 3) === '<p>') 1562*7017fceaSAndreas Gohr { 1563*7017fceaSAndreas Gohr $markup = $trimmedMarkup; 1564*7017fceaSAndreas Gohr $markup = substr($markup, 3); 1565*7017fceaSAndreas Gohr 1566*7017fceaSAndreas Gohr $position = strpos($markup, "</p>"); 1567*7017fceaSAndreas Gohr 1568*7017fceaSAndreas Gohr $markup = substr_replace($markup, '', $position, 4); 1569*7017fceaSAndreas Gohr } 1570*7017fceaSAndreas Gohr 1571*7017fceaSAndreas Gohr return $markup; 1572*7017fceaSAndreas Gohr } 1573*7017fceaSAndreas Gohr 1574*7017fceaSAndreas Gohr # 1575*7017fceaSAndreas Gohr # Deprecated Methods 1576*7017fceaSAndreas Gohr # 1577*7017fceaSAndreas Gohr 1578*7017fceaSAndreas Gohr function parse($text) 1579*7017fceaSAndreas Gohr { 1580*7017fceaSAndreas Gohr $markup = $this->text($text); 1581*7017fceaSAndreas Gohr 1582*7017fceaSAndreas Gohr return $markup; 1583*7017fceaSAndreas Gohr } 1584*7017fceaSAndreas Gohr 1585*7017fceaSAndreas Gohr protected function sanitiseElement(array $Element) 1586*7017fceaSAndreas Gohr { 1587*7017fceaSAndreas Gohr static $goodAttribute = '/^[a-zA-Z0-9][a-zA-Z0-9-_]*+$/'; 1588*7017fceaSAndreas Gohr static $safeUrlNameToAtt = array( 1589*7017fceaSAndreas Gohr 'a' => 'href', 1590*7017fceaSAndreas Gohr 'img' => 'src', 1591*7017fceaSAndreas Gohr ); 1592*7017fceaSAndreas Gohr 1593*7017fceaSAndreas Gohr if (isset($safeUrlNameToAtt[$Element['name']])) 1594*7017fceaSAndreas Gohr { 1595*7017fceaSAndreas Gohr $Element = $this->filterUnsafeUrlInAttribute($Element, $safeUrlNameToAtt[$Element['name']]); 1596*7017fceaSAndreas Gohr } 1597*7017fceaSAndreas Gohr 1598*7017fceaSAndreas Gohr if ( ! empty($Element['attributes'])) 1599*7017fceaSAndreas Gohr { 1600*7017fceaSAndreas Gohr foreach ($Element['attributes'] as $att => $val) 1601*7017fceaSAndreas Gohr { 1602*7017fceaSAndreas Gohr # filter out badly parsed attribute 1603*7017fceaSAndreas Gohr if ( ! preg_match($goodAttribute, $att)) 1604*7017fceaSAndreas Gohr { 1605*7017fceaSAndreas Gohr unset($Element['attributes'][$att]); 1606*7017fceaSAndreas Gohr } 1607*7017fceaSAndreas Gohr # dump onevent attribute 1608*7017fceaSAndreas Gohr elseif (self::striAtStart($att, 'on')) 1609*7017fceaSAndreas Gohr { 1610*7017fceaSAndreas Gohr unset($Element['attributes'][$att]); 1611*7017fceaSAndreas Gohr } 1612*7017fceaSAndreas Gohr } 1613*7017fceaSAndreas Gohr } 1614*7017fceaSAndreas Gohr 1615*7017fceaSAndreas Gohr return $Element; 1616*7017fceaSAndreas Gohr } 1617*7017fceaSAndreas Gohr 1618*7017fceaSAndreas Gohr protected function filterUnsafeUrlInAttribute(array $Element, $attribute) 1619*7017fceaSAndreas Gohr { 1620*7017fceaSAndreas Gohr foreach ($this->safeLinksWhitelist as $scheme) 1621*7017fceaSAndreas Gohr { 1622*7017fceaSAndreas Gohr if (self::striAtStart($Element['attributes'][$attribute], $scheme)) 1623*7017fceaSAndreas Gohr { 1624*7017fceaSAndreas Gohr return $Element; 1625*7017fceaSAndreas Gohr } 1626*7017fceaSAndreas Gohr } 1627*7017fceaSAndreas Gohr 1628*7017fceaSAndreas Gohr $Element['attributes'][$attribute] = str_replace(':', '%3A', $Element['attributes'][$attribute]); 1629*7017fceaSAndreas Gohr 1630*7017fceaSAndreas Gohr return $Element; 1631*7017fceaSAndreas Gohr } 1632*7017fceaSAndreas Gohr 1633*7017fceaSAndreas Gohr # 1634*7017fceaSAndreas Gohr # Static Methods 1635*7017fceaSAndreas Gohr # 1636*7017fceaSAndreas Gohr 1637*7017fceaSAndreas Gohr protected static function escape($text, $allowQuotes = false) 1638*7017fceaSAndreas Gohr { 1639*7017fceaSAndreas Gohr return htmlspecialchars($text, $allowQuotes ? ENT_NOQUOTES : ENT_QUOTES, 'UTF-8'); 1640*7017fceaSAndreas Gohr } 1641*7017fceaSAndreas Gohr 1642*7017fceaSAndreas Gohr protected static function striAtStart($string, $needle) 1643*7017fceaSAndreas Gohr { 1644*7017fceaSAndreas Gohr $len = strlen($needle); 1645*7017fceaSAndreas Gohr 1646*7017fceaSAndreas Gohr if ($len > strlen($string)) 1647*7017fceaSAndreas Gohr { 1648*7017fceaSAndreas Gohr return false; 1649*7017fceaSAndreas Gohr } 1650*7017fceaSAndreas Gohr else 1651*7017fceaSAndreas Gohr { 1652*7017fceaSAndreas Gohr return strtolower(substr($string, 0, $len)) === strtolower($needle); 1653*7017fceaSAndreas Gohr } 1654*7017fceaSAndreas Gohr } 1655*7017fceaSAndreas Gohr 1656*7017fceaSAndreas Gohr static function instance($name = 'default') 1657*7017fceaSAndreas Gohr { 1658*7017fceaSAndreas Gohr if (isset(self::$instances[$name])) 1659*7017fceaSAndreas Gohr { 1660*7017fceaSAndreas Gohr return self::$instances[$name]; 1661*7017fceaSAndreas Gohr } 1662*7017fceaSAndreas Gohr 1663*7017fceaSAndreas Gohr $instance = new static(); 1664*7017fceaSAndreas Gohr 1665*7017fceaSAndreas Gohr self::$instances[$name] = $instance; 1666*7017fceaSAndreas Gohr 1667*7017fceaSAndreas Gohr return $instance; 1668*7017fceaSAndreas Gohr } 1669*7017fceaSAndreas Gohr 1670*7017fceaSAndreas Gohr private static $instances = array(); 1671*7017fceaSAndreas Gohr 1672*7017fceaSAndreas Gohr # 1673*7017fceaSAndreas Gohr # Fields 1674*7017fceaSAndreas Gohr # 1675*7017fceaSAndreas Gohr 1676*7017fceaSAndreas Gohr protected $DefinitionData; 1677*7017fceaSAndreas Gohr 1678*7017fceaSAndreas Gohr # 1679*7017fceaSAndreas Gohr # Read-Only 1680*7017fceaSAndreas Gohr 1681*7017fceaSAndreas Gohr protected $specialCharacters = array( 1682*7017fceaSAndreas Gohr '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|', 1683*7017fceaSAndreas Gohr ); 1684*7017fceaSAndreas Gohr 1685*7017fceaSAndreas Gohr protected $StrongRegex = array( 1686*7017fceaSAndreas Gohr '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s', 1687*7017fceaSAndreas Gohr '_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us', 1688*7017fceaSAndreas Gohr ); 1689*7017fceaSAndreas Gohr 1690*7017fceaSAndreas Gohr protected $EmRegex = array( 1691*7017fceaSAndreas Gohr '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s', 1692*7017fceaSAndreas Gohr '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us', 1693*7017fceaSAndreas Gohr ); 1694*7017fceaSAndreas Gohr 1695*7017fceaSAndreas Gohr protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?'; 1696*7017fceaSAndreas Gohr 1697*7017fceaSAndreas Gohr protected $voidElements = array( 1698*7017fceaSAndreas Gohr 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 1699*7017fceaSAndreas Gohr ); 1700*7017fceaSAndreas Gohr 1701*7017fceaSAndreas Gohr protected $textLevelElements = array( 1702*7017fceaSAndreas Gohr 'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont', 1703*7017fceaSAndreas Gohr 'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing', 1704*7017fceaSAndreas Gohr 'i', 'rp', 'del', 'code', 'strike', 'marquee', 1705*7017fceaSAndreas Gohr 'q', 'rt', 'ins', 'font', 'strong', 1706*7017fceaSAndreas Gohr 's', 'tt', 'kbd', 'mark', 1707*7017fceaSAndreas Gohr 'u', 'xm', 'sub', 'nobr', 1708*7017fceaSAndreas Gohr 'sup', 'ruby', 1709*7017fceaSAndreas Gohr 'var', 'span', 1710*7017fceaSAndreas Gohr 'wbr', 'time', 1711*7017fceaSAndreas Gohr ); 1712*7017fceaSAndreas Gohr} 1713