| 1 | <?php |
|---|
| 2 | |
|---|
| 3 | class FCKeditorParser extends FCKeditorParserWrapper |
|---|
| 4 | { |
|---|
| 5 | public static $fkc_mw_makeImage_options; |
|---|
| 6 | protected $fck_mw_strtr_span; |
|---|
| 7 | protected $fck_mw_strtr_span_counter=1; |
|---|
| 8 | protected $fck_mw_taghook; |
|---|
| 9 | protected $fck_internal_parse_text; |
|---|
| 10 | protected $fck_matches = array(); |
|---|
| 11 | |
|---|
| 12 | private $FCKeditorMagicWords = array( |
|---|
| 13 | '__NOTOC__', |
|---|
| 14 | '__FORCETOC__', |
|---|
| 15 | '__NOEDITSECTION__', |
|---|
| 16 | '__START__', |
|---|
| 17 | '__NOTITLECONVERT__', |
|---|
| 18 | '__NOCONTENTCONVERT__', |
|---|
| 19 | '__END__', |
|---|
| 20 | '__TOC__', |
|---|
| 21 | '__NOTC__', |
|---|
| 22 | '__NOCC__', |
|---|
| 23 | "__FORCETOC__", |
|---|
| 24 | "__NEWSECTIONLINK__", |
|---|
| 25 | "__NOGALLERY__", |
|---|
| 26 | ); |
|---|
| 27 | |
|---|
| 28 | /** |
|---|
| 29 | * Add special string (that would be changed by Parser) to array and return simple unique string |
|---|
| 30 | * that will remain unchanged during whole parsing operation. |
|---|
| 31 | * At the end we'll replace all this unique strings with original content |
|---|
| 32 | * |
|---|
| 33 | * @param string $text |
|---|
| 34 | * @return string |
|---|
| 35 | */ |
|---|
| 36 | private function fck_addToStrtr($text, $replaceLineBreaks = true) { |
|---|
| 37 | $key = 'Fckmw'.$this->fck_mw_strtr_span_counter.'fckmw'; |
|---|
| 38 | $this->fck_mw_strtr_span_counter++; |
|---|
| 39 | if ($replaceLineBreaks) { |
|---|
| 40 | $this->fck_mw_strtr_span[$key] = str_replace(array("\r\n", "\n", "\r"),"fckLR",$text); |
|---|
| 41 | } |
|---|
| 42 | else { |
|---|
| 43 | $this->fck_mw_strtr_span[$key] = $text; |
|---|
| 44 | } |
|---|
| 45 | return $key; |
|---|
| 46 | } |
|---|
| 47 | |
|---|
| 48 | /** |
|---|
| 49 | * Handle link to subpage if necessary |
|---|
| 50 | * @param string $target the source of the link |
|---|
| 51 | * @param string &$text the link text, modified as necessary |
|---|
| 52 | * @return string the full name of the link |
|---|
| 53 | * @private |
|---|
| 54 | */ |
|---|
| 55 | function maybeDoSubpageLink($target, &$text) { |
|---|
| 56 | return $target; |
|---|
| 57 | } |
|---|
| 58 | |
|---|
| 59 | /** |
|---|
| 60 | * DO NOT Replace special strings like "ISBN xxx" and "RFC xxx" with |
|---|
| 61 | * magic external links. |
|---|
| 62 | * |
|---|
| 63 | * DML |
|---|
| 64 | * @private |
|---|
| 65 | */ |
|---|
| 66 | function doMagicLinks( $text ) { |
|---|
| 67 | return $text; |
|---|
| 68 | } |
|---|
| 69 | |
|---|
| 70 | /** |
|---|
| 71 | * Callback function for custom tags: feed, ref, references etc. |
|---|
| 72 | * |
|---|
| 73 | * @param string $str Input |
|---|
| 74 | * @param array $argv Arguments |
|---|
| 75 | * @return string |
|---|
| 76 | */ |
|---|
| 77 | function fck_genericTagHook( $str, $argv, $parser ) { |
|---|
| 78 | if (in_array($this->fck_mw_taghook, array("ref", "math", "references", "source"))) { |
|---|
| 79 | $class = $this->fck_mw_taghook; |
|---|
| 80 | } |
|---|
| 81 | else { |
|---|
| 82 | $class = "special"; |
|---|
| 83 | } |
|---|
| 84 | |
|---|
| 85 | if (empty($argv)) { |
|---|
| 86 | $ret = "<span class=\"fck_mw_".$class."\" _fck_mw_customtag=\"true\" _fck_mw_tagname=\"".$this->fck_mw_taghook."\">"; |
|---|
| 87 | } |
|---|
| 88 | else { |
|---|
| 89 | $ret = "<span class=\"fck_mw_".$class."\" _fck_mw_customtag=\"true\" _fck_mw_tagname=\"".$this->fck_mw_taghook."\""; |
|---|
| 90 | foreach ($argv as $key=>$value) { |
|---|
| 91 | $ret .= " ".$key."=\"".$value."\""; |
|---|
| 92 | } |
|---|
| 93 | $ret .=">"; |
|---|
| 94 | } |
|---|
| 95 | if (is_null($str)) { |
|---|
| 96 | $ret = substr($ret, 0, -1) . " />"; |
|---|
| 97 | } |
|---|
| 98 | else { |
|---|
| 99 | $ret .= htmlspecialchars($str); |
|---|
| 100 | $ret .= "</span>"; |
|---|
| 101 | } |
|---|
| 102 | |
|---|
| 103 | $replacement = $this->fck_addToStrtr($ret); |
|---|
| 104 | return $replacement; |
|---|
| 105 | } |
|---|
| 106 | |
|---|
| 107 | /** |
|---|
| 108 | * Callback function for wiki tags: nowiki, includeonly, noinclude |
|---|
| 109 | * |
|---|
| 110 | * @param string $tagName tag name, eg. nowiki, math |
|---|
| 111 | * @param string $str Input |
|---|
| 112 | * @param array $argv Arguments |
|---|
| 113 | * @return string |
|---|
| 114 | */ |
|---|
| 115 | function fck_wikiTag( $tagName, $str, $argv = array()) { |
|---|
| 116 | if (empty($argv)) { |
|---|
| 117 | $ret = "<span class=\"fck_mw_".$tagName."\" _fck_mw_customtag=\"true\" _fck_mw_tagname=\"".$tagName."\">"; |
|---|
| 118 | } |
|---|
| 119 | else { |
|---|
| 120 | $ret = "<span class=\"fck_mw_".$tagName."\" _fck_mw_customtag=\"true\" _fck_mw_tagname=\"".$tagName."\""; |
|---|
| 121 | foreach ($argv as $key=>$value) { |
|---|
| 122 | $ret .= " ".$key."=\"".$value."\""; |
|---|
| 123 | } |
|---|
| 124 | $ret .=">"; |
|---|
| 125 | } |
|---|
| 126 | if (is_null($str)) { |
|---|
| 127 | $ret = substr($ret, 0, -1) . " />"; |
|---|
| 128 | } |
|---|
| 129 | else { |
|---|
| 130 | $ret .= htmlspecialchars($str); |
|---|
| 131 | $ret .= "</span>"; |
|---|
| 132 | } |
|---|
| 133 | |
|---|
| 134 | $replacement = $this->fck_addToStrtr($ret); |
|---|
| 135 | |
|---|
| 136 | return $replacement; |
|---|
| 137 | } |
|---|
| 138 | |
|---|
| 139 | /** |
|---|
| 140 | * Strips and renders nowiki, pre, math, hiero |
|---|
| 141 | * If $render is set, performs necessary rendering operations on plugins |
|---|
| 142 | * Returns the text, and fills an array with data needed in unstrip() |
|---|
| 143 | * |
|---|
| 144 | * @param StripState $state |
|---|
| 145 | * |
|---|
| 146 | * @param bool $stripcomments when set, HTML comments <!-- like this --> |
|---|
| 147 | * will be stripped in addition to other tags. This is important |
|---|
| 148 | * for section editing, where these comments cause confusion when |
|---|
| 149 | * counting the sections in the wikisource |
|---|
| 150 | * |
|---|
| 151 | * @param array dontstrip contains tags which should not be stripped; |
|---|
| 152 | * used to prevent stipping of <gallery> when saving (fixes bug 2700) |
|---|
| 153 | * |
|---|
| 154 | * @private |
|---|
| 155 | */ |
|---|
| 156 | function strip( $text, $state, $stripcomments = false , $dontstrip = array () ) { |
|---|
| 157 | global $wgContLang, $wgUseTeX, $wgScriptPath, $wgVersion, $wgHooks, $wgExtensionFunctions; |
|---|
| 158 | |
|---|
| 159 | wfProfileIn( __METHOD__ ); |
|---|
| 160 | $render = ($this->mOutputType == OT_HTML); |
|---|
| 161 | |
|---|
| 162 | $uniq_prefix = $this->mUniqPrefix; |
|---|
| 163 | $commentState = new ReplacementArray; |
|---|
| 164 | $nowikiItems = array(); |
|---|
| 165 | $generalItems = array(); |
|---|
| 166 | |
|---|
| 167 | $elements = array_merge( array( 'nowiki', 'gallery', 'math' ), array_keys( $this->mTagHooks ) ); |
|---|
| 168 | if ( (isset($wgHooks['ParserFirstCallInit']) && in_array('efSyntaxHighlight_GeSHiSetup', $wgHooks['ParserFirstCallInit'])) |
|---|
| 169 | || (isset($wgExtensionFunctions) && in_array('efSyntaxHighlight_GeSHiSetup', $wgExtensionFunctions)) ) { |
|---|
| 170 | $elements = array_merge( $elements, array( 'source' ) ); |
|---|
| 171 | } |
|---|
| 172 | if ( (isset($wgHooks['ParserFirstCallInit']) && in_array('wfCite', $wgHooks['ParserFirstCallInit'])) |
|---|
| 173 | || (isset($wgExtensionFunctions) && in_array('wfCite', $wgExtensionFunctions)) ) { |
|---|
| 174 | $elements = array_merge( $elements, array( 'ref', 'references' ) ); |
|---|
| 175 | } |
|---|
| 176 | global $wgRawHtml; |
|---|
| 177 | if( $wgRawHtml ) { |
|---|
| 178 | $elements[] = 'html'; |
|---|
| 179 | } |
|---|
| 180 | |
|---|
| 181 | # Removing $dontstrip tags from $elements list (currently only 'gallery', fixing bug 2700) |
|---|
| 182 | foreach ( $elements AS $k => $v ) { |
|---|
| 183 | if ( !in_array ( $v , $dontstrip ) ) continue; |
|---|
| 184 | unset ( $elements[$k] ); |
|---|
| 185 | } |
|---|
| 186 | |
|---|
| 187 | $elements = array_unique($elements); |
|---|
| 188 | $matches = array(); |
|---|
| 189 | if (version_compare("1.12", $wgVersion, ">")) { |
|---|
| 190 | $text = Parser::extractTagsAndParams( $elements, $text, $matches, $uniq_prefix ); |
|---|
| 191 | } |
|---|
| 192 | else { |
|---|
| 193 | $text = self::extractTagsAndParams( $elements, $text, $matches, $uniq_prefix ); |
|---|
| 194 | } |
|---|
| 195 | |
|---|
| 196 | foreach( $matches as $marker => $data ) { |
|---|
| 197 | list( $element, $content, $params, $tag ) = $data; |
|---|
| 198 | if( $render ) { |
|---|
| 199 | $tagName = strtolower( $element ); |
|---|
| 200 | wfProfileIn( __METHOD__."-render-$tagName" ); |
|---|
| 201 | switch( $tagName ) { |
|---|
| 202 | case '!--': |
|---|
| 203 | // Comment |
|---|
| 204 | if( substr( $tag, -3 ) == '-->' ) { |
|---|
| 205 | $output = $tag; |
|---|
| 206 | } else { |
|---|
| 207 | // Unclosed comment in input. |
|---|
| 208 | // Close it so later stripping can remove it |
|---|
| 209 | $output = "$tag-->"; |
|---|
| 210 | } |
|---|
| 211 | break; |
|---|
| 212 | case 'references': |
|---|
| 213 | $output = $this->fck_wikiTag('references', $content, $params); |
|---|
| 214 | break; |
|---|
| 215 | case 'ref': |
|---|
| 216 | $output = $this->fck_wikiTag('ref', $content, $params); |
|---|
| 217 | break; |
|---|
| 218 | case 'syntaxhighlight': |
|---|
| 219 | case 'source': |
|---|
| 220 | $output = $this->fck_wikiTag('source', $content, $params); |
|---|
| 221 | break; |
|---|
| 222 | case 'html': |
|---|
| 223 | if( $wgRawHtml ) { |
|---|
| 224 | $output = $this->fck_wikiTag('html', $content, $params); |
|---|
| 225 | } |
|---|
| 226 | break; |
|---|
| 227 | case 'nowiki': |
|---|
| 228 | $output = $this->fck_wikiTag('nowiki', $content, $params); //required by FCKeditor |
|---|
| 229 | break; |
|---|
| 230 | case 'math': |
|---|
| 231 | if($wgUseTeX){ //normal render |
|---|
| 232 | $output = $wgContLang->armourMath( MathRenderer::renderMath( $content ) ); |
|---|
| 233 | }else //show fakeimage |
|---|
| 234 | $output = '<img _fckfakelement="true" class="FCK__MWMath" _fck_mw_math="'.$content.'" src="'.$wgScriptPath.'/skins/common/images/button_math.png" />'; |
|---|
| 235 | break; |
|---|
| 236 | case 'gallery': |
|---|
| 237 | $output = $this->fck_wikiTag('gallery', $content, $params); //required by FCKeditor |
|---|
| 238 | //$output = $this->renderImageGallery( $content, $params ); |
|---|
| 239 | break; |
|---|
| 240 | default: |
|---|
| 241 | if( isset( $this->mTagHooks[$tagName] ) ) { |
|---|
| 242 | $this->fck_mw_taghook = $tagName; //required by FCKeditor |
|---|
| 243 | $output = call_user_func_array( $this->mTagHooks[$tagName], |
|---|
| 244 | array( $content, $params, $this ) ); |
|---|
| 245 | } else { |
|---|
| 246 | throw new MWException( "Invalid call hook $element" ); |
|---|
| 247 | } |
|---|
| 248 | } |
|---|
| 249 | wfProfileOut( __METHOD__."-render-$tagName" ); |
|---|
| 250 | } else { |
|---|
| 251 | // Just stripping tags; keep the source |
|---|
| 252 | $output = $tag; |
|---|
| 253 | } |
|---|
| 254 | |
|---|
| 255 | // Unstrip the output, to support recursive strip() calls |
|---|
| 256 | $output = $state->unstripBoth( $output ); |
|---|
| 257 | |
|---|
| 258 | if( !$stripcomments && $element == '!--' ) { |
|---|
| 259 | $commentState->setPair( $marker, $output ); |
|---|
| 260 | } elseif ( $element == 'html' || $element == 'nowiki' ) { |
|---|
| 261 | $nowikiItems[$marker] = $output; |
|---|
| 262 | } else { |
|---|
| 263 | $generalItems[$marker] = $output; |
|---|
| 264 | } |
|---|
| 265 | } |
|---|
| 266 | # Add the new items to the state |
|---|
| 267 | # We do this after the loop instead of during it to avoid slowing |
|---|
| 268 | # down the recursive unstrip |
|---|
| 269 | $state->nowiki->mergeArray( $nowikiItems ); |
|---|
| 270 | $state->general->mergeArray( $generalItems ); |
|---|
| 271 | |
|---|
| 272 | # Unstrip comments unless explicitly told otherwise. |
|---|
| 273 | # (The comments are always stripped prior to this point, so as to |
|---|
| 274 | # not invoke any extension tags / parser hooks contained within |
|---|
| 275 | # a comment.) |
|---|
| 276 | if ( !$stripcomments ) { |
|---|
| 277 | // Put them all back and forget them |
|---|
| 278 | $text = $commentState->replace( $text ); |
|---|
| 279 | } |
|---|
| 280 | |
|---|
| 281 | $this->fck_matches = $matches; |
|---|
| 282 | wfProfileOut( __METHOD__ ); |
|---|
| 283 | return $text; |
|---|
| 284 | } |
|---|
| 285 | |
|---|
| 286 | /** Replace HTML comments with unique text using fck_addToStrtr function |
|---|
| 287 | * |
|---|
| 288 | * @private |
|---|
| 289 | * @param string $text |
|---|
| 290 | * @return string |
|---|
| 291 | */ |
|---|
| 292 | private function fck_replaceHTMLcomments( $text ) { |
|---|
| 293 | wfProfileIn( __METHOD__ ); |
|---|
| 294 | while (($start = strpos($text, '<!--')) !== false) { |
|---|
| 295 | $end = strpos($text, '-->', $start + 4); |
|---|
| 296 | if ($end === false) { |
|---|
| 297 | # Unterminated comment; bail out |
|---|
| 298 | break; |
|---|
| 299 | } |
|---|
| 300 | |
|---|
| 301 | $end += 3; |
|---|
| 302 | |
|---|
| 303 | # Trim space and newline if the comment is both |
|---|
| 304 | # preceded and followed by a newline |
|---|
| 305 | $spaceStart = max($start - 1, 0); |
|---|
| 306 | $spaceLen = $end - $spaceStart; |
|---|
| 307 | while (substr($text, $spaceStart, 1) === ' ' && $spaceStart > 0) { |
|---|
| 308 | $spaceStart--; |
|---|
| 309 | $spaceLen++; |
|---|
| 310 | } |
|---|
| 311 | while (substr($text, $spaceStart + $spaceLen, 1) === ' ') |
|---|
| 312 | $spaceLen++; |
|---|
| 313 | if (substr($text, $spaceStart, 1) === "\n" and substr($text, $spaceStart + $spaceLen, 1) === "\n") { |
|---|
| 314 | # Remove the comment, leading and trailing |
|---|
| 315 | # spaces, and leave only one newline. |
|---|
| 316 | $replacement = $this->fck_addToStrtr(substr($text, $spaceStart, $spaceLen+1), false); |
|---|
| 317 | $text = substr_replace($text, $replacement."\n", $spaceStart, $spaceLen + 1); |
|---|
| 318 | } |
|---|
| 319 | else { |
|---|
| 320 | # Remove just the comment. |
|---|
| 321 | $replacement = $this->fck_addToStrtr(substr($text, $start, $end - $start), false); |
|---|
| 322 | $text = substr_replace($text, $replacement, $start, $end - $start); |
|---|
| 323 | } |
|---|
| 324 | } |
|---|
| 325 | wfProfileOut( __METHOD__ ); |
|---|
| 326 | |
|---|
| 327 | return $text; |
|---|
| 328 | } |
|---|
| 329 | |
|---|
| 330 | function replaceInternalLinks( $text ) { |
|---|
| 331 | $text = preg_replace("/\[\[([^|\[\]]*?)\]\]/", "[[$1|RTENOTITLE]]", $text); //#2223: [[()]] => [[%1|RTENOTITLE]] |
|---|
| 332 | $text = preg_replace("/\[\[:(.*?)\]\]/", "[[RTECOLON$1]]", $text); //change ':' => 'RTECOLON' in links |
|---|
| 333 | $text = parent::replaceInternalLinks($text); |
|---|
| 334 | $text = preg_replace("/\|RTENOTITLE\]\]/", "]]", $text); // remove unused RTENOTITLE |
|---|
| 335 | |
|---|
| 336 | return $text; |
|---|
| 337 | } |
|---|
| 338 | |
|---|
| 339 | function makeImage( $nt, $options ) { |
|---|
| 340 | FCKeditorParser::$fkc_mw_makeImage_options = $options; |
|---|
| 341 | return parent::makeImage( $nt, $options ); |
|---|
| 342 | } |
|---|
| 343 | |
|---|
| 344 | /** |
|---|
| 345 | * Replace templates with unique text to preserve them from parsing |
|---|
| 346 | * |
|---|
| 347 | * @todo if {{template}} is inside string that also must be returned unparsed, |
|---|
| 348 | * e.g. <noinclude>{{template}}</noinclude> |
|---|
| 349 | * {{template}} replaced with Fckmw[n]fckmw which is wrong... |
|---|
| 350 | * |
|---|
| 351 | * @param string $text |
|---|
| 352 | * @return string |
|---|
| 353 | */ |
|---|
| 354 | private function fck_replaceTemplates( $text ) { |
|---|
| 355 | |
|---|
| 356 | $callback = array('{' => |
|---|
| 357 | array( |
|---|
| 358 | 'end'=>'}', |
|---|
| 359 | 'cb' => array( |
|---|
| 360 | 2=>array('FCKeditorParser', 'fck_leaveTemplatesAlone'), |
|---|
| 361 | 3=>array('FCKeditorParser', 'fck_leaveTemplatesAlone'), |
|---|
| 362 | ), |
|---|
| 363 | 'min' =>2, |
|---|
| 364 | 'max' =>3, |
|---|
| 365 | ) |
|---|
| 366 | ); |
|---|
| 367 | |
|---|
| 368 | $text = $this->replace_callback($text, $callback); |
|---|
| 369 | |
|---|
| 370 | $tags = array(); |
|---|
| 371 | $offset=0; |
|---|
| 372 | $textTmp = $text; |
|---|
| 373 | while (false !== ($pos = strpos($textTmp, "<!--FCK_SKIP_START-->"))) |
|---|
| 374 | { |
|---|
| 375 | $tags[abs($pos + $offset)] = 1; |
|---|
| 376 | $textTmp = substr($textTmp, $pos+21); |
|---|
| 377 | $offset += $pos + 21; |
|---|
| 378 | } |
|---|
| 379 | |
|---|
| 380 | $offset=0; |
|---|
| 381 | $textTmp = $text; |
|---|
| 382 | while (false !== ($pos = strpos($textTmp, "<!--FCK_SKIP_END-->"))) |
|---|
| 383 | { |
|---|
| 384 | $tags[abs($pos + $offset)] = -1; |
|---|
| 385 | $textTmp = substr($textTmp, $pos+19); |
|---|
| 386 | $offset += $pos + 19; |
|---|
| 387 | } |
|---|
| 388 | |
|---|
| 389 | if (!empty($tags)) { |
|---|
| 390 | ksort($tags); |
|---|
| 391 | |
|---|
| 392 | $strtr = array("<!--FCK_SKIP_START-->" => "", "<!--FCK_SKIP_END-->" => ""); |
|---|
| 393 | |
|---|
| 394 | $sum=0; |
|---|
| 395 | $lastSum=0; |
|---|
| 396 | $finalString = ""; |
|---|
| 397 | $stringToParse = ""; |
|---|
| 398 | $startingPos = 0; |
|---|
| 399 | $inner = ""; |
|---|
| 400 | $strtr_span = array(); |
|---|
| 401 | foreach ($tags as $pos=>$type) { |
|---|
| 402 | $sum += $type; |
|---|
| 403 | if (!$pos) { |
|---|
| 404 | $opened = 0; |
|---|
| 405 | $closed = 0; |
|---|
| 406 | } |
|---|
| 407 | else { |
|---|
| 408 | $opened = substr_count($text, '[', 0, $pos); //count [ |
|---|
| 409 | $closed = substr_count($text, ']', 0, $pos); //count ] |
|---|
| 410 | } |
|---|
| 411 | if ($sum == 1 && $lastSum == 0) { |
|---|
| 412 | $stringToParse .= strtr(substr($text, $startingPos, $pos - $startingPos), $strtr); |
|---|
| 413 | $startingPos = $pos; |
|---|
| 414 | } |
|---|
| 415 | else if ($sum == 0) { |
|---|
| 416 | $stringToParse .= 'Fckmw'.$this->fck_mw_strtr_span_counter.'fckmw'; |
|---|
| 417 | $inner = htmlspecialchars(strtr(substr($text, $startingPos, $pos - $startingPos + 19), $strtr)); |
|---|
| 418 | $this->fck_mw_strtr_span['href="Fckmw'.$this->fck_mw_strtr_span_counter.'fckmw"'] = 'href="'.$inner.'"'; |
|---|
| 419 | if($opened <= $closed) { // {{template}} is NOT in [] or [[]] |
|---|
| 420 | $this->fck_mw_strtr_span['Fckmw'.$this->fck_mw_strtr_span_counter.'fckmw'] = '<span class="fck_mw_template">'.str_replace(array("\r\n", "\n", "\r"),"fckLR",$inner).'</span>'; |
|---|
| 421 | }else{ |
|---|
| 422 | $this->fck_mw_strtr_span['Fckmw'.$this->fck_mw_strtr_span_counter.'fckmw'] = str_replace(array("\r\n", "\n", "\r"),"fckLR",$inner); |
|---|
| 423 | } |
|---|
| 424 | $startingPos = $pos + 19; |
|---|
| 425 | $this->fck_mw_strtr_span_counter++; |
|---|
| 426 | } |
|---|
| 427 | $lastSum = $sum; |
|---|
| 428 | } |
|---|
| 429 | $stringToParse .= substr($text, $startingPos); |
|---|
| 430 | $text = &$stringToParse; |
|---|
| 431 | } |
|---|
| 432 | |
|---|
| 433 | return $text; |
|---|
| 434 | } |
|---|
| 435 | |
|---|
| 436 | function internalParse ( $text ) { |
|---|
| 437 | |
|---|
| 438 | $this->fck_internal_parse_text =& $text; |
|---|
| 439 | |
|---|
| 440 | //these three tags should remain unchanged |
|---|
| 441 | $text = StringUtils::delimiterReplaceCallback( '<includeonly>', '</includeonly>', array($this, 'fck_includeonly'), $text ); |
|---|
| 442 | $text = StringUtils::delimiterReplaceCallback( '<noinclude>', '</noinclude>', array($this, 'fck_noinclude'), $text ); |
|---|
| 443 | $text = StringUtils::delimiterReplaceCallback( '<onlyinclude>', '</onlyinclude>', array($this, 'fck_onlyinclude'), $text ); |
|---|
| 444 | |
|---|
| 445 | //html comments shouldn't be stripped |
|---|
| 446 | $text = $this->fck_replaceHTMLcomments( $text ); |
|---|
| 447 | //as well as templates |
|---|
| 448 | $text = $this->fck_replaceTemplates( $text ); |
|---|
| 449 | |
|---|
| 450 | $finalString = parent::internalParse($text); |
|---|
| 451 | |
|---|
| 452 | return $finalString; |
|---|
| 453 | } |
|---|
| 454 | function fck_includeonly( $matches ) { |
|---|
| 455 | return $this->fck_wikiTag('includeonly', $matches[1]); |
|---|
| 456 | } |
|---|
| 457 | function fck_noinclude( $matches ) { |
|---|
| 458 | return $this->fck_wikiTag('noinclude', $matches[1]); |
|---|
| 459 | } |
|---|
| 460 | function fck_onlyinclude( $matches ) { |
|---|
| 461 | return $this->fck_wikiTag('onlyinclude', $matches[1]); |
|---|
| 462 | } |
|---|
| 463 | function fck_leaveTemplatesAlone( $matches ) { |
|---|
| 464 | return "<!--FCK_SKIP_START-->".$matches['text']."<!--FCK_SKIP_END-->"; |
|---|
| 465 | } |
|---|
| 466 | function formatHeadings( $text, $isMain=true ) { |
|---|
| 467 | return $text; |
|---|
| 468 | } |
|---|
| 469 | function replaceFreeExternalLinks( $text ) { return $text; } |
|---|
| 470 | function stripNoGallery(&$text) {} |
|---|
| 471 | function stripToc( $text ) { |
|---|
| 472 | //$prefix = '<span class="fck_mw_magic">'; |
|---|
| 473 | //$suffix = '</span>'; |
|---|
| 474 | $prefix = ''; |
|---|
| 475 | $suffix = ''; |
|---|
| 476 | |
|---|
| 477 | $strtr = array(); |
|---|
| 478 | foreach ($this->FCKeditorMagicWords as $word) { |
|---|
| 479 | $strtr[$word] = $prefix . $word . $suffix; |
|---|
| 480 | } |
|---|
| 481 | |
|---|
| 482 | return strtr( $text, $strtr ); |
|---|
| 483 | } |
|---|
| 484 | |
|---|
| 485 | function doDoubleUnderscore( $text ) { |
|---|
| 486 | return $text; |
|---|
| 487 | } |
|---|
| 488 | |
|---|
| 489 | function parse( $text, &$title, $options, $linestart = true, $clearState = true, $revid = null ) { |
|---|
| 490 | $text = preg_replace("/^#REDIRECT/", "<!--FCK_REDIRECT-->", $text); |
|---|
| 491 | $parserOutput = parent::parse($text, $title, $options, $linestart , $clearState , $revid ); |
|---|
| 492 | |
|---|
| 493 | $categories = $parserOutput->getCategories(); |
|---|
| 494 | if ($categories) { |
|---|
| 495 | $appendString = ""; |
|---|
| 496 | foreach ($categories as $cat=>$val) { |
|---|
| 497 | $args = ''; |
|---|
| 498 | if( $val == 'RTENOTITLE' ){ |
|---|
| 499 | $args .= '_fcknotitle="true" '; |
|---|
| 500 | $val = $cat; |
|---|
| 501 | } |
|---|
| 502 | if ($val != $title->mTextform) { |
|---|
| 503 | $appendString .= "<a ".$args."href=\"Category:" . $cat ."\">" . $val ."</a> "; |
|---|
| 504 | } |
|---|
| 505 | else { |
|---|
| 506 | $appendString .= "<a ".$args."href=\"Category:" . $cat ."\">Category:" . $cat ."</a> "; |
|---|
| 507 | } |
|---|
| 508 | } |
|---|
| 509 | $parserOutput->setText($parserOutput->getText() . $appendString); |
|---|
| 510 | } |
|---|
| 511 | |
|---|
| 512 | if (!empty($this->fck_mw_strtr_span)) { |
|---|
| 513 | global $leaveRawTemplates; |
|---|
| 514 | if (!empty($leaveRawTemplates)) { |
|---|
| 515 | foreach ($leaveRawTemplates as $l) { |
|---|
| 516 | $this->fck_mw_strtr_span[$l] = substr($this->fck_mw_strtr_span[$l], 30, -7); |
|---|
| 517 | } |
|---|
| 518 | } |
|---|
| 519 | $text = strtr($parserOutput->getText(), $this->fck_mw_strtr_span); |
|---|
| 520 | $parserOutput->setText(strtr($text, $this->fck_mw_strtr_span)); |
|---|
| 521 | } |
|---|
| 522 | if (!empty($this->fck_matches)) { |
|---|
| 523 | $text = $parserOutput->getText() ; |
|---|
| 524 | foreach ($this->fck_matches as $key => $m) { |
|---|
| 525 | $text = str_replace( $key, $m[3], $text); |
|---|
| 526 | } |
|---|
| 527 | $parserOutput->setText($text); |
|---|
| 528 | } |
|---|
| 529 | |
|---|
| 530 | if (!empty($parserOutput->mLanguageLinks)) { |
|---|
| 531 | foreach ($parserOutput->mLanguageLinks as $l) { |
|---|
| 532 | $parserOutput->setText($parserOutput->getText() . "\n" . "<a href=\"".$l."\">".$l."</a>") ; |
|---|
| 533 | } |
|---|
| 534 | } |
|---|
| 535 | |
|---|
| 536 | $parserOutput->setText(str_replace("<!--FCK_REDIRECT-->", "#REDIRECT", $parserOutput->getText())); |
|---|
| 537 | |
|---|
| 538 | return $parserOutput; |
|---|
| 539 | } |
|---|
| 540 | |
|---|
| 541 | /** |
|---|
| 542 | * Make lists from lines starting with ':', '*', '#', etc. |
|---|
| 543 | * |
|---|
| 544 | * @private |
|---|
| 545 | * @return string the lists rendered as HTML |
|---|
| 546 | */ |
|---|
| 547 | function doBlockLevels( $text, $linestart ) { |
|---|
| 548 | $fname = 'Parser::doBlockLevels'; |
|---|
| 549 | wfProfileIn( $fname ); |
|---|
| 550 | |
|---|
| 551 | # Parsing through the text line by line. The main thing |
|---|
| 552 | # happening here is handling of block-level elements p, pre, |
|---|
| 553 | # and making lists from lines starting with * # : etc. |
|---|
| 554 | # |
|---|
| 555 | $textLines = explode( "\n", $text ); |
|---|
| 556 | |
|---|
| 557 | $lastPrefix = $output = ''; |
|---|
| 558 | $this->mDTopen = $inBlockElem = false; |
|---|
| 559 | $prefixLength = 0; |
|---|
| 560 | $paragraphStack = false; |
|---|
| 561 | |
|---|
| 562 | if ( !$linestart ) { |
|---|
| 563 | $output .= array_shift( $textLines ); |
|---|
| 564 | } |
|---|
| 565 | foreach ( $textLines as $oLine ) { |
|---|
| 566 | $lastPrefixLength = strlen( $lastPrefix ); |
|---|
| 567 | $preCloseMatch = preg_match('/<\\/pre/i', $oLine ); |
|---|
| 568 | $preOpenMatch = preg_match('/<pre/i', $oLine ); |
|---|
| 569 | if ( !$this->mInPre ) { |
|---|
| 570 | # Multiple prefixes may abut each other for nested lists. |
|---|
| 571 | $prefixLength = strspn( $oLine, '*#:;' ); |
|---|
| 572 | $pref = substr( $oLine, 0, $prefixLength ); |
|---|
| 573 | |
|---|
| 574 | # eh? |
|---|
| 575 | $pref2 = str_replace( ';', ':', $pref ); |
|---|
| 576 | $t = substr( $oLine, $prefixLength ); |
|---|
| 577 | $this->mInPre = !empty($preOpenMatch); |
|---|
| 578 | } else { |
|---|
| 579 | # Don't interpret any other prefixes in preformatted text |
|---|
| 580 | $prefixLength = 0; |
|---|
| 581 | $pref = $pref2 = ''; |
|---|
| 582 | $t = $oLine; |
|---|
| 583 | } |
|---|
| 584 | |
|---|
| 585 | # List generation |
|---|
| 586 | if( $prefixLength && 0 == strcmp( $lastPrefix, $pref2 ) ) { |
|---|
| 587 | # Same as the last item, so no need to deal with nesting or opening stuff |
|---|
| 588 | $output .= $this->nextItem( substr( $pref, -1 ) ); |
|---|
| 589 | $paragraphStack = false; |
|---|
| 590 | |
|---|
| 591 | if ( substr( $pref, -1 ) == ';') { |
|---|
| 592 | # The one nasty exception: definition lists work like this: |
|---|
| 593 | # ; title : definition text |
|---|
| 594 | # So we check for : in the remainder text to split up the |
|---|
| 595 | # title and definition, without b0rking links. |
|---|
| 596 | $term = $t2 = ''; |
|---|
| 597 | if ($this->findColonNoLinks($t, $term, $t2) !== false) { |
|---|
| 598 | $t = $t2; |
|---|
| 599 | $output .= $term . $this->nextItem( ':' ); |
|---|
| 600 | } |
|---|
| 601 | } |
|---|
| 602 | } elseif( $prefixLength || $lastPrefixLength ) { |
|---|
| 603 | # Either open or close a level... |
|---|
| 604 | $commonPrefixLength = $this->getCommon( $pref, $lastPrefix ); |
|---|
| 605 | $paragraphStack = false; |
|---|
| 606 | |
|---|
| 607 | while( $commonPrefixLength < $lastPrefixLength ) { |
|---|
| 608 | $output .= $this->closeList( $lastPrefix{$lastPrefixLength-1} ); |
|---|
| 609 | --$lastPrefixLength; |
|---|
| 610 | } |
|---|
| 611 | if ( $prefixLength <= $commonPrefixLength && $commonPrefixLength > 0 ) { |
|---|
| 612 | $output .= $this->nextItem( $pref{$commonPrefixLength-1} ); |
|---|
| 613 | } |
|---|
| 614 | while ( $prefixLength > $commonPrefixLength ) { |
|---|
| 615 | $char = substr( $pref, $commonPrefixLength, 1 ); |
|---|
| 616 | $output .= $this->openList( $char ); |
|---|
| 617 | |
|---|
| 618 | if ( ';' == $char ) { |
|---|
| 619 | # FIXME: This is dupe of code above |
|---|
| 620 | if ($this->findColonNoLinks($t, $term, $t2) !== false) { |
|---|
| 621 | $t = $t2; |
|---|
| 622 | $output .= $term . $this->nextItem( ':' ); |
|---|
| 623 | } |
|---|
| 624 | } |
|---|
| 625 | ++$commonPrefixLength; |
|---|
| 626 | } |
|---|
| 627 | $lastPrefix = $pref2; |
|---|
| 628 | } |
|---|
| 629 | if( 0 == $prefixLength ) { |
|---|
| 630 | wfProfileIn( "$fname-paragraph" ); |
|---|
| 631 | # No prefix (not in list)--go to paragraph mode |
|---|
| 632 | // XXX: use a stack for nestable elements like span, table and div |
|---|
| 633 | $openmatch = preg_match('/(?:<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6|<pre|<tr|<p|<ul|<ol|<li|<\\/tr|<\\/td|<\\/th)/iS', $t ); |
|---|
| 634 | $closematch = preg_match( |
|---|
| 635 | '/(?:<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6|'. |
|---|
| 636 | '<td|<th|<\\/?div|<hr|<\\/pre|<\\/p|'.$this->mUniqPrefix.'-pre|<\\/li|<\\/ul|<\\/ol|<\\/?center)/iS', $t ); |
|---|
| 637 | if ( $openmatch or $closematch ) { |
|---|
| 638 | $paragraphStack = false; |
|---|
| 639 | # TODO bug 5718: paragraph closed |
|---|
| 640 | $output .= $this->closeParagraph(); |
|---|
| 641 | if ( $preOpenMatch and !$preCloseMatch ) { |
|---|
| 642 | $this->mInPre = true; |
|---|
| 643 | } |
|---|
| 644 | if ( $closematch ) { |
|---|
| 645 | $inBlockElem = false; |
|---|
| 646 | } else { |
|---|
| 647 | $inBlockElem = true; |
|---|
| 648 | } |
|---|
| 649 | } else if ( !$inBlockElem && !$this->mInPre ) { |
|---|
| 650 | if ( ' ' == $t{0} and ( $this->mLastSection == 'pre' or trim($t) != '' ) ) { |
|---|
| 651 | // pre |
|---|
| 652 | if ($this->mLastSection != 'pre') { |
|---|
| 653 | $paragraphStack = false; |
|---|
| 654 | $output .= $this->closeParagraph().'<pre class="_fck_mw_lspace">'; |
|---|
| 655 | $this->mLastSection = 'pre'; |
|---|
| 656 | } |
|---|
| 657 | $t = substr( $t, 1 ); |
|---|
| 658 | } else { |
|---|
| 659 | // paragraph |
|---|
| 660 | if ( '' == trim($t) ) { |
|---|
| 661 | if ( $paragraphStack ) { |
|---|
| 662 | $output .= $paragraphStack.'<br />'; |
|---|
| 663 | $paragraphStack = false; |
|---|
| 664 | $this->mLastSection = 'p'; |
|---|
| 665 | } else { |
|---|
| 666 | if ($this->mLastSection != 'p' ) { |
|---|
| 667 | $output .= $this->closeParagraph(); |
|---|
| 668 | $this->mLastSection = ''; |
|---|
| 669 | $paragraphStack = '<p>'; |
|---|
| 670 | } else { |
|---|
| 671 | $paragraphStack = '</p><p>'; |
|---|
| 672 | } |
|---|
| 673 | } |
|---|
| 674 | } else { |
|---|
| 675 | if ( $paragraphStack ) { |
|---|
| 676 | $output .= $paragraphStack; |
|---|
| 677 | $paragraphStack = false; |
|---|
| 678 | $this->mLastSection = 'p'; |
|---|
| 679 | } else if ($this->mLastSection != 'p') { |
|---|
| 680 | $output .= $this->closeParagraph().'<p>'; |
|---|
| 681 | $this->mLastSection = 'p'; |
|---|
| 682 | } |
|---|
| 683 | } |
|---|
| 684 | } |
|---|
| 685 | } |
|---|
| 686 | wfProfileOut( "$fname-paragraph" ); |
|---|
| 687 | } |
|---|
| 688 | // somewhere above we forget to get out of pre block (bug 785) |
|---|
| 689 | if($preCloseMatch && $this->mInPre) { |
|---|
| 690 | $this->mInPre = false; |
|---|
| 691 | } |
|---|
| 692 | if ($paragraphStack === false) { |
|---|
| 693 | $output .= $t."\n"; |
|---|
| 694 | } |
|---|
| 695 | } |
|---|
| 696 | while ( $prefixLength ) { |
|---|
| 697 | $output .= $this->closeList( $pref2{$prefixLength-1} ); |
|---|
| 698 | --$prefixLength; |
|---|
| 699 | } |
|---|
| 700 | if ( '' != $this->mLastSection ) { |
|---|
| 701 | $output .= '</' . $this->mLastSection . '>'; |
|---|
| 702 | $this->mLastSection = ''; |
|---|
| 703 | } |
|---|
| 704 | |
|---|
| 705 | wfProfileOut( $fname ); |
|---|
| 706 | return $output; |
|---|
| 707 | } |
|---|
| 708 | } |
|---|