516793a300a104803a3f9f18be93a259882108a8
[lhc/web/wiklou.git] / languages / LanguageConverter.php
1 <?php
2 /**
3 * @package MediaWiki
4 * @subpackage Language
5 *
6 * @author Zhengzhu Feng <zhengzhu@gmail.com>
7 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
8 */
9
10 class LanguageConverter {
11 var $mPreferredVariant='';
12 var $mMainLanguageCode;
13 var $mVariants, $mVariantFallbacks;
14 var $mTablesLoaded = false;
15 var $mTables;
16 var $mTitleDisplay='';
17 var $mDoTitleConvert=true, $mDoContentConvert=true;
18 var $mTitleFromFlag = false;
19 var $mCacheKey;
20 var $mLangObj;
21 var $mMarkup;
22 var $mFlags;
23 var $mUcfirst = false;
24 /**
25 * Constructor
26 *
27 * @param string $maincode the main language code of this language
28 * @param array $variants the supported variants of this language
29 * @param array $variantfallback the fallback language of each variant
30 * @param array $markup array defining the markup used for manual conversion
31 * @param array $flags array defining the custom strings that maps to the flags
32 * @access public
33 */
34 function __construct($langobj, $maincode,
35 $variants=array(),
36 $variantfallbacks=array(),
37 $markup=array(),
38 $flags = array()) {
39 $this->mLangObj = $langobj;
40 $this->mMainLanguageCode = $maincode;
41 $this->mVariants = $variants;
42 $this->mVariantFallbacks = $variantfallbacks;
43 $this->mCacheKey = wfMemcKey( 'conversiontables', $maincode );
44 $m = array('begin'=>'-{', 'flagsep'=>'|', 'codesep'=>':',
45 'varsep'=>';', 'end'=>'}-');
46 $this->mMarkup = array_merge($m, $markup);
47 $f = array('A'=>'A', 'T'=>'T', 'R' => 'R');
48 $this->mFlags = array_merge($f, $flags);
49 }
50
51 /**
52 * @access public
53 */
54 function getVariants() {
55 return $this->mVariants;
56 }
57
58 /**
59 * in case some variant is not defined in the markup, we need
60 * to have some fallback. for example, in zh, normally people
61 * will define zh-cn and zh-tw, but less so for zh-sg or zh-hk.
62 * when zh-sg is preferred but not defined, we will pick zh-cn
63 * in this case. right now this is only used by zh.
64 *
65 * @param string $v the language code of the variant
66 * @return string the code of the fallback language or false if there is no fallback
67 * @private
68 */
69 function getVariantFallback($v) {
70 return $this->mVariantFallbacks[$v];
71 }
72
73
74 /**
75 * get preferred language variants.
76 * @param boolean $fromUser Get it from $wgUser's preferences
77 * @return string the preferred language code
78 * @access public
79 */
80 function getPreferredVariant( $fromUser = true ) {
81 global $wgUser, $wgRequest, $wgVariantArticlePath, $wgDefaultLanguageVariant;
82
83 if($this->mPreferredVariant)
84 return $this->mPreferredVariant;
85
86 // see if the preference is set in the request
87 $req = $wgRequest->getText( 'variant' );
88 if( in_array( $req, $this->mVariants ) ) {
89 $this->mPreferredVariant = $req;
90 return $req;
91 }
92
93 // check the syntax /code/ArticleTitle
94 if($wgVariantArticlePath!=false && isset($_SERVER['SCRIPT_NAME'])){
95 // Note: SCRIPT_NAME probably won't hold the correct value if PHP is run as CGI
96 // (it will hold path to php.cgi binary), and might not exist on some very old PHP installations
97 $scriptBase = basename( $_SERVER['SCRIPT_NAME'] );
98 if(in_array($scriptBase,$this->mVariants)){
99 $this->mPreferredVariant = $scriptBase;
100 return $this->mPreferredVariant;
101 }
102 }
103
104 // get language variant preference from logged in users
105 // Don't call this on stub objects because that causes infinite
106 // recursion during initialisation
107 if( $fromUser && $wgUser->isLoggedIn() ) {
108 $this->mPreferredVariant = $wgUser->getOption('variant');
109 return $this->mPreferredVariant;
110 }
111
112 // see if default variant is globaly set
113 if($wgDefaultLanguageVariant != false && in_array( $wgDefaultLanguageVariant, $this->mVariants )){
114 $this->mPreferredVariant = $wgDefaultLanguageVariant;
115 return $this->mPreferredVariant;
116 }
117
118 # FIXME rewrite code for parsing http header. The current code
119 # is written specific for detecting zh- variants
120 if( !$this->mPreferredVariant ) {
121 // see if some supported language variant is set in the
122 // http header, but we don't set the mPreferredVariant
123 // variable in case this is called before the user's
124 // preference is loaded
125 $pv=$this->mMainLanguageCode;
126 if(array_key_exists('HTTP_ACCEPT_LANGUAGE', $_SERVER)) {
127 $header = str_replace( '_', '-', strtolower($_SERVER["HTTP_ACCEPT_LANGUAGE"]));
128 $zh = strstr($header, $pv.'-');
129 if($zh) {
130 $pv = substr($zh,0,5);
131 }
132 }
133 // don't try to return bad variant
134 if(in_array( $pv, $this->mVariants ))
135 return $pv;
136 }
137
138 return $this->mMainLanguageCode;
139
140 }
141
142 /**
143 * dictionary-based conversion
144 *
145 * @param string $text the text to be converted
146 * @param string $toVariant the target language code
147 * @return string the converted text
148 * @private
149 */
150 function autoConvert($text, $toVariant=false) {
151 $fname="LanguageConverter::autoConvert";
152
153 wfProfileIn( $fname );
154
155 if(!$this->mTablesLoaded)
156 $this->loadTables();
157
158 if(!$toVariant)
159 $toVariant = $this->getPreferredVariant();
160 if(!in_array($toVariant, $this->mVariants))
161 return $text;
162
163 /* we convert everything except:
164 1. html markups (anything between < and >)
165 2. html entities
166 3. place holders created by the parser
167 */
168 global $wgParser;
169 if (isset($wgParser))
170 $marker = '|' . $wgParser->UniqPrefix() . '[\-a-zA-Z0-9]+';
171 else
172 $marker = "";
173
174 // this one is needed when the text is inside an html markup
175 $htmlfix = '|<[^>]+$|^[^<>]*>';
176
177 // disable convert to variants between <code></code> tags
178 $codefix = '<code>.+?<\/code>|';
179 // disable convertsion of <script type="text/javascript"> ... </script>
180 $scriptfix = '<script.*?>.*?<\/script>|';
181
182 $reg = '/'.$codefix . $scriptfix . '<[^>]+>|&[a-zA-Z#][a-z0-9]+;' . $marker . $htmlfix . '/s';
183
184 $matches = preg_split($reg, $text, -1, PREG_SPLIT_OFFSET_CAPTURE);
185
186 $m = array_shift($matches);
187
188 $ret = $this->translate($m[0], $toVariant);
189 $mstart = $m[1]+strlen($m[0]);
190 foreach($matches as $m) {
191 $ret .= substr($text, $mstart, $m[1]-$mstart);
192 $ret .= $this->translate($m[0], $toVariant);
193 $mstart = $m[1] + strlen($m[0]);
194 }
195 wfProfileOut( $fname );
196 return $ret;
197 }
198
199 /**
200 * Translate a string to a variant
201 * Doesn't process markup or do any of that other stuff, for that use convert()
202 *
203 * @param string $text Text to convert
204 * @param string $variant Variant language code
205 * @return string Translated text
206 */
207 function translate( $text, $variant ) {
208 wfProfileIn( __METHOD__ );
209 if( !$this->mTablesLoaded )
210 $this->loadTables();
211 $text = $this->mTables[$variant]->replace( $text );
212 wfProfileOut( __METHOD__ );
213 return $text;
214 }
215
216 /**
217 * convert text to all supported variants
218 *
219 * @param string $text the text to be converted
220 * @return array of string
221 * @public
222 */
223 function autoConvertToAllVariants($text) {
224 $fname="LanguageConverter::autoConvertToAllVariants";
225 wfProfileIn( $fname );
226 if( !$this->mTablesLoaded )
227 $this->loadTables();
228
229 $ret = array();
230 foreach($this->mVariants as $variant) {
231 $ret[$variant] = $this->translate($text, $variant);
232 }
233
234 wfProfileOut( $fname );
235 return $ret;
236 }
237
238 /**
239 * convert link text to all supported variants
240 *
241 * @param string $text the text to be converted
242 * @return array of string
243 * @public
244 */
245 function convertLinkToAllVariants($text) {
246 if( !$this->mTablesLoaded )
247 $this->loadTables();
248
249 $ret = array();
250 $tarray = explode($this->mMarkup['begin'], $text);
251 $tfirst = array_shift($tarray);
252
253 foreach($this->mVariants as $variant)
254 $ret[$variant] = $this->translate($tfirst,$variant);
255
256 foreach($tarray as $txt) {
257 $marked = explode($this->mMarkup['end'], $txt, 2);
258
259 foreach($this->mVariants as $variant){
260 $ret[$variant] .= $this->mMarkup['begin'].$marked[0].$this->mMarkup['end'];
261 if(array_key_exists(1, $marked))
262 $ret[$variant] .= $this->translate($marked[1],$variant);
263 }
264
265 }
266
267 return $ret;
268 }
269
270
271 /**
272 * Convert text using a parser object for context
273 */
274 function parserConvert( $text, &$parser ) {
275 global $wgDisableLangConversion;
276 /* don't do anything if this is the conversion table */
277 if ( $parser->getTitle()->getNamespace() == NS_MEDIAWIKI &&
278 strpos($parser->mTitle->getText(), "Conversiontable") !== false )
279 {
280 return $text;
281 }
282
283 if($wgDisableLangConversion)
284 return $text;
285
286 $text = $this->convert( $text );
287 $parser->mOutput->setTitleText( $this->mTitleDisplay );
288 return $text;
289 }
290
291 /**
292 * Parse flags with syntax -{FLAG| ... }-
293 *
294 */
295 function parseFlags($marked){
296 $flags = array();
297
298 // process flag only if the flag is valid
299 if(strlen($marked) < 2 || !(in_array($marked[0],$this->mFlags) && $marked[1]=='|' ) )
300 return array($marked,array());
301
302 $tt = explode($this->mMarkup['flagsep'], $marked, 2);
303
304 if(sizeof($tt) == 2) {
305 $f = explode($this->mMarkup['varsep'], $tt[0]);
306 foreach($f as $ff) {
307 $ff = trim($ff);
308 if(array_key_exists($ff, $this->mFlags) &&
309 !array_key_exists($this->mFlags[$ff], $flags))
310 $flags[] = $this->mFlags[$ff];
311 }
312 $rules = $tt[1];
313 }
314 else
315 $rules = $marked;
316
317 if( !in_array('R',$flags) ){
318 //FIXME: may cause trouble here...
319 //strip &nbsp; since it interferes with the parsing, plus,
320 //all spaces should be stripped in this tag anyway.
321 $rules = str_replace('&nbsp;', '', $rules);
322 }
323
324 return array($rules,$flags);
325 }
326
327 /**
328 * convert text to different variants of a language. the automatic
329 * conversion is done in autoConvert(). here we parse the text
330 * marked with -{}-, which specifies special conversions of the
331 * text that can not be accomplished in autoConvert()
332 *
333 * syntax of the markup:
334 * -{code1:text1;code2:text2;...}- or
335 * -{text}- in which case no conversion should take place for text
336 *
337 * @param string $text text to be converted
338 * @param bool $isTitle whether this conversion is for the article title
339 * @return string converted text
340 * @access public
341 */
342 function convert( $text , $isTitle=false) {
343 $mw =& MagicWord::get( 'notitleconvert' );
344 if( $mw->matchAndRemove( $text ) )
345 $this->mDoTitleConvert = false;
346
347 $mw =& MagicWord::get( 'nocontentconvert' );
348 if( $mw->matchAndRemove( $text ) ) {
349 $this->mDoContentConvert = false;
350 }
351
352 // no conversion if redirecting
353 $mw =& MagicWord::get( 'redirect' );
354 if( $mw->matchStart( $text ))
355 return $text;
356
357 if( $isTitle ) {
358
359 // use the title from the T flag if any
360 if($this->mTitleFromFlag){
361 $this->mTitleFromFlag = false;
362 return $this->mTitleDisplay;
363 }
364
365 // check for __NOTC__ tag
366 if( !$this->mDoTitleConvert ) {
367 $this->mTitleDisplay = $text;
368 return $text;
369 }
370
371 global $wgRequest;
372 $isredir = $wgRequest->getText( 'redirect', 'yes' );
373 $action = $wgRequest->getText( 'action' );
374 if ( $isredir == 'no' || $action == 'edit' ) {
375 return $text;
376 }
377 else {
378 $this->mTitleDisplay = $this->convert($text);
379 return $this->mTitleDisplay;
380 }
381 }
382
383 $plang = $this->getPreferredVariant();
384 if( isset( $this->mVariantFallbacks[$plang] ) ) {
385 $fallback = $this->mVariantFallbacks[$plang];
386 } else {
387 $fallback = $this->mMainLanguageCode;
388 }
389
390 $tarray = explode($this->mMarkup['begin'], $text);
391 $tfirst = array_shift($tarray);
392 if($this->mDoContentConvert)
393 $text = $this->autoConvert($tfirst);
394 else
395 $text = $tfirst;
396 foreach($tarray as $txt) {
397 $marked = explode($this->mMarkup['end'], $txt, 2);
398
399 // strip the flags from syntax like -{T| ... }-
400 list($rules,$flags) = $this->parseFlags($marked[0]);
401
402 // proces R flag: output raw content of -{ ... }-
403 if( in_array('R',$flags) ){
404 $disp = $rules;
405 } else if( $this->mDoContentConvert){
406 // parse the contents -{ ... }-
407 $carray = $this->parseManualRule($rules, $flags);
408
409 $disp = '';
410 if(array_key_exists($plang, $carray)) {
411 $disp = $carray[$plang];
412 } else if(array_key_exists($fallback, $carray)) {
413 $disp = $carray[$fallback];
414 }
415 } else{
416 // if we don't do content convert, still strip the -{}- tags
417 $disp = $rules;
418 $flags = array();
419 }
420
421 if($disp) {
422 // use syntax -{T|zh:TitleZh;zh-tw:TitleTw}- for custom conversion in title
423 if(in_array('T', $flags)){
424 $this->mTitleFromFlag = true;
425 $this->mTitleDisplay = $disp;
426 }
427 else
428 $text .= $disp;
429
430 // use syntax -{A|zh:WordZh;zh-tw:WordTw}- to introduce a custom mapping between
431 // words WordZh and WordTw in the whole text
432 if(in_array('A', $flags)) {
433
434 /* fill in the missing variants, if any,
435 with fallbacks */
436 foreach($this->mVariants as $v) {
437 if(!array_key_exists($v, $carray)) {
438 $vf = $this->getVariantFallback($v);
439 if(array_key_exists($vf, $carray))
440 $carray[$v] = $carray[$vf];
441 }
442 }
443
444 foreach($this->mVariants as $vfrom) {
445 if(!array_key_exists($vfrom, $carray))
446 continue;
447 foreach($this->mVariants as $vto) {
448 if($vfrom == $vto)
449 continue;
450 if(!array_key_exists($vto, $carray))
451 continue;
452 $this->mTables[$vto]->setPair($carray[$vfrom], $carray[$vto]);
453 }
454 }
455 }
456 }
457 else {
458 $text .= $marked[0];
459 }
460 if(array_key_exists(1, $marked)){
461 if( $this->mDoContentConvert )
462 $text .= $this->autoConvert($marked[1]);
463 else
464 $text .= $marked[1];
465 }
466 }
467
468 return $text;
469 }
470
471 /**
472 * parse the manually marked conversion rule
473 * @param string $rule the text of the rule
474 * @return array of the translation in each variant
475 * @private
476 */
477 function parseManualRule($rules, $flags=array()) {
478
479 $choice = explode($this->mMarkup['varsep'], $rules);
480 $carray = array();
481 if(sizeof($choice) == 1) {
482 /* a single choice */
483 foreach($this->mVariants as $v)
484 $carray[$v] = $choice[0];
485 }
486 else {
487 foreach($choice as $c) {
488 $v = explode($this->mMarkup['codesep'], $c);
489 if(sizeof($v) != 2) // syntax error, skip
490 continue;
491 $carray[trim($v[0])] = trim($v[1]);
492 }
493 }
494 return $carray;
495 }
496
497 /**
498 * if a language supports multiple variants, it is
499 * possible that non-existing link in one variant
500 * actually exists in another variant. this function
501 * tries to find it. See e.g. LanguageZh.php
502 *
503 * @param string $link the name of the link
504 * @param mixed $nt the title object of the link
505 * @return null the input parameters may be modified upon return
506 * @access public
507 */
508 function findVariantLink( &$link, &$nt ) {
509 global $wgDisableLangConversion;
510 $linkBatch = new LinkBatch();
511
512 $ns=NS_MAIN;
513
514 if(is_object($nt))
515 $ns = $nt->getNamespace();
516
517 $variants = $this->autoConvertToAllVariants($link);
518 if($variants == false) //give up
519 return;
520
521 $titles = array();
522
523 foreach( $variants as $v ) {
524 if($v != $link){
525 $varnt = Title::newFromText( $v, $ns );
526 if(!is_null($varnt)){
527 $linkBatch->addObj($varnt);
528 $titles[]=$varnt;
529 }
530 }
531 }
532
533 // fetch all variants in single query
534 $linkBatch->execute();
535
536 foreach( $titles as $varnt ) {
537 if( $varnt->getArticleID() > 0 ) {
538 $nt = $varnt;
539 if( !$wgDisableLangConversion )
540 $link = $v;
541 break;
542 }
543 }
544 }
545
546 /**
547 * returns language specific hash options
548 *
549 * @access public
550 */
551 function getExtraHashOptions() {
552 $variant = $this->getPreferredVariant();
553 return '!' . $variant ;
554 }
555
556 /**
557 * get title text as defined in the body of the article text
558 *
559 * @access public
560 */
561 function getParsedTitle() {
562 return $this->mTitleDisplay;
563 }
564
565 /**
566 * a write lock to the cache
567 *
568 * @private
569 */
570 function lockCache() {
571 global $wgMemc;
572 $success = false;
573 for($i=0; $i<30; $i++) {
574 if($success = $wgMemc->add($this->mCacheKey . "lock", 1, 10))
575 break;
576 sleep(1);
577 }
578 return $success;
579 }
580
581 /**
582 * unlock cache
583 *
584 * @private
585 */
586 function unlockCache() {
587 global $wgMemc;
588 $wgMemc->delete($this->mCacheKey . "lock");
589 }
590
591
592 /**
593 * Load default conversion tables
594 * This method must be implemented in derived class
595 *
596 * @private
597 */
598 function loadDefaultTables() {
599 $name = get_class($this);
600 wfDie("Must implement loadDefaultTables() method in class $name");
601 }
602
603 /**
604 * load conversion tables either from the cache or the disk
605 * @private
606 */
607 function loadTables($fromcache=true) {
608 global $wgMemc;
609 if( $this->mTablesLoaded )
610 return;
611 wfProfileIn( __METHOD__ );
612 $this->mTablesLoaded = true;
613 $this->mTables = false;
614 if($fromcache) {
615 wfProfileIn( __METHOD__.'-cache' );
616 $this->mTables = $wgMemc->get( $this->mCacheKey );
617 wfProfileOut( __METHOD__.'-cache' );
618 }
619 if ( !$this->mTables || !isset( $this->mTables['VERSION 2'] ) ) {
620 wfProfileIn( __METHOD__.'-recache' );
621 // not in cache, or we need a fresh reload.
622 // we will first load the default tables
623 // then update them using things in MediaWiki:Zhconversiontable/*
624 $this->loadDefaultTables();
625 foreach($this->mVariants as $var) {
626 $cached = $this->parseCachedTable($var);
627 $this->mTables[$var]->mergeArray($cached);
628 }
629
630 $this->postLoadTables();
631 $this->mTables['VERSION 2'] = true;
632
633 if($this->lockCache()) {
634 $wgMemc->set($this->mCacheKey, $this->mTables, 43200);
635 $this->unlockCache();
636 }
637 wfProfileOut( __METHOD__.'-recache' );
638 }
639 wfProfileOut( __METHOD__ );
640 }
641
642 /**
643 * Hook for post processig after conversion tables are loaded
644 *
645 */
646 function postLoadTables() {}
647
648 /**
649 * Reload the conversion tables
650 *
651 * @private
652 */
653 function reloadTables() {
654 if($this->mTables)
655 unset($this->mTables);
656 $this->mTablesLoaded = false;
657 $this->loadTables(false);
658 }
659
660
661 /**
662 * parse the conversion table stored in the cache
663 *
664 * the tables should be in blocks of the following form:
665
666 * -{
667 * word => word ;
668 * word => word ;
669 * ...
670 * }-
671 *
672 * to make the tables more manageable, subpages are allowed
673 * and will be parsed recursively if $recursive=true
674 *
675 * @private
676 */
677 function parseCachedTable($code, $subpage='', $recursive=true) {
678 global $wgMessageCache;
679 static $parsed = array();
680
681 if(!is_object($wgMessageCache))
682 return array();
683
684 $key = 'Conversiontable/'.$code;
685 if($subpage)
686 $key .= '/' . $subpage;
687
688 if(array_key_exists($key, $parsed))
689 return array();
690
691
692 $txt = $wgMessageCache->get( $key, true, true, true );
693
694 // get all subpage links of the form
695 // [[MediaWiki:conversiontable/zh-xx/...|...]]
696 $linkhead = $this->mLangObj->getNsText(NS_MEDIAWIKI) . ':Conversiontable';
697 $subs = explode('[[', $txt);
698 $sublinks = array();
699 foreach( $subs as $sub ) {
700 $link = explode(']]', $sub, 2);
701 if(count($link) != 2)
702 continue;
703 $b = explode('|', $link[0]);
704 $b = explode('/', trim($b[0]), 3);
705 if(count($b)==3)
706 $sublink = $b[2];
707 else
708 $sublink = '';
709
710 if($b[0] == $linkhead && $b[1] == $code) {
711 $sublinks[] = $sublink;
712 }
713 }
714
715
716 // parse the mappings in this page
717 $blocks = explode($this->mMarkup['begin'], $txt);
718 array_shift($blocks);
719 $ret = array();
720 foreach($blocks as $block) {
721 $mappings = explode($this->mMarkup['end'], $block, 2);
722 $stripped = str_replace(array("'", '"', '*','#'), '', $mappings[0]);
723 $table = explode( ';', $stripped );
724 foreach( $table as $t ) {
725 $m = explode( '=>', $t );
726 if( count( $m ) != 2)
727 continue;
728 // trim any trailling comments starting with '//'
729 $tt = explode('//', $m[1], 2);
730 $ret[trim($m[0])] = trim($tt[0]);
731 }
732 }
733 $parsed[$key] = true;
734
735
736 // recursively parse the subpages
737 if($recursive) {
738 foreach($sublinks as $link) {
739 $s = $this->parseCachedTable($code, $link, $recursive);
740 $ret = array_merge($ret, $s);
741 }
742 }
743
744 if ($this->mUcfirst) {
745 foreach ($ret as $k => $v) {
746 $ret[Language::ucfirst($k)] = Language::ucfirst($v);
747 }
748 }
749 return $ret;
750 }
751
752 /**
753 * Enclose a string with the "no conversion" tag. This is used by
754 * various functions in the Parser
755 *
756 * @param string $text text to be tagged for no conversion
757 * @return string the tagged text
758 */
759 function markNoConversion($text, $noParse=false) {
760 # don't mark if already marked
761 if(strpos($text, $this->mMarkup['begin']) ||
762 strpos($text, $this->mMarkup['end']))
763 return $text;
764
765 $ret = $this->mMarkup['begin'] . $text . $this->mMarkup['end'];
766 return $ret;
767 }
768
769 /**
770 * convert the sorting key for category links. this should make different
771 * keys that are variants of each other map to the same key
772 */
773 function convertCategoryKey( $key ) {
774 return $key;
775 }
776 /**
777 * hook to refresh the cache of conversion tables when
778 * MediaWiki:conversiontable* is updated
779 * @private
780 */
781 function OnArticleSaveComplete($article, $user, $text, $summary, $isminor, $iswatch, $section) {
782 $titleobj = $article->getTitle();
783 if($titleobj->getNamespace() == NS_MEDIAWIKI) {
784 /*
785 global $wgContLang; // should be an LanguageZh.
786 if(get_class($wgContLang) != 'languagezh')
787 return true;
788 */
789 $title = $titleobj->getDBkey();
790 $t = explode('/', $title, 3);
791 $c = count($t);
792 if( $c > 1 && $t[0] == 'Conversiontable' ) {
793 if(in_array($t[1], $this->mVariants)) {
794 $this->reloadTables();
795 }
796 }
797 }
798 return true;
799 }
800
801 /**
802 * Armour rendered math against conversion
803 * Wrap math into rawoutput -{R| math }- syntax
804 */
805 function armourMath($text){
806 $ret = $this->mMarkup['begin'] . 'R|' . $text . $this->mMarkup['end'];
807 return $ret;
808 }
809
810
811 }
812
813 ?>