Try to output editsection markers instead of rendered editsection links and defer...
[lhc/web/wiklou.git] / includes / parser / Parser.php
index 298aa3d..b723bea 100644 (file)
  * (which in turn the browser understands, and can display).
  *
  * <pre>
- * There are six main entry points into the Parser class:
+ * There are five main entry points into the Parser class:
  * parse()
- *   produces HTML output
+ *     produces HTML output
  * preSaveTransform().
- *   produces altered wiki markup.
+ *     produces altered wiki markup.
  * preprocess()
- *   removes HTML comments and expands templates
- * cleanSig()
- *   Cleans a signature before saving it to preferences
- * extractSections()
- *   Extracts sections from an article for section editing
- * getTransclusionText()
- *   Extracts the text of a template with only <includeonly>, etc., parsed
+ *     removes HTML comments and expands templates
+ * cleanSig() / cleanSigInSig()
+ *     Cleans a signature before saving it to preferences
+ * getSection()
+ *     Return the content of a section from an article for section editing
+ * replaceSection()
+ *     Replaces a section by number inside an article
+ * getPreloadText()
+ *     Removes <noinclude> sections, and <includeonly> tags.
  *
  * Globals used:
  *    objects:   $wgLang, $wgContLang
@@ -45,8 +47,7 @@
  *
  * @ingroup Parser
  */
-class Parser
-{
+class Parser {
        /**
         * Update this version number when the ParserOutput format
         * changes in an incompatible way, so the parser cache
@@ -65,7 +66,7 @@ class Parser
        const EXT_IMAGE_REGEX = '/^(http:\/\/|https:\/\/)([^][<>"\\x00-\\x20\\x7F]+)
                \\/([A-Za-z0-9_.,~%\\-+&;#*?!=()@\\x80-\\xFF]+)\\.((?i)gif|png|jpg|jpeg)$/Sx';
 
-       // State constants for the definition list colon extraction
+       # State constants for the definition list colon extraction
        const COLON_STATE_TEXT = 0;
        const COLON_STATE_TAG = 1;
        const COLON_STATE_TAGSTART = 2;
@@ -75,28 +76,35 @@ class Parser
        const COLON_STATE_COMMENTDASH = 6;
        const COLON_STATE_COMMENTDASHDASH = 7;
 
-       // Flags for preprocessToDom
+       # Flags for preprocessToDom
        const PTD_FOR_INCLUSION = 1;
 
-       // Allowed values for $this->mOutputType
-       // Parameter to startExternalParse().
-       const OT_HTML = 1;
-       const OT_WIKI = 2;
-       const OT_PREPROCESS = 3;
+       # Allowed values for $this->mOutputType
+       # Parameter to startExternalParse().
+       const OT_HTML = 1; # like parse()
+       const OT_WIKI = 2; # like preSaveTransform()
+       const OT_PREPROCESS = 3; # like preprocess()
        const OT_MSG = 3;
-       const OT_INCLUDES = 4;
+       const OT_PLAIN = 4; # like extractSections() - portions of the original are returned unchanged.
 
-       // Marker Suffix needs to be accessible staticly.
+       # Marker Suffix needs to be accessible staticly.
        const MARKER_SUFFIX = "-QINU\x7f";
 
-       /**#@+
-        * @private
-        */
        # Persistent:
-       var $mTagHooks, $mTransparentTagHooks, $mFunctionHooks, $mFunctionSynonyms, $mVariables,
-               $mSubstWords, $mImageParams, $mImageParamsMagicArray, $mStripList, $mMarkerIndex,
-               $mPreprocessor, $mExtLinkBracketedRegex, $mUrlProtocols, $mDefaultStripList,
-               $mVarCache, $mConf, $mFunctionTagHooks;
+       var $mTagHooks = array();
+       var $mTransparentTagHooks = array();
+       var $mFunctionHooks = array();
+       var $mFunctionSynonyms = array( 0 => array(), 1 => array() );
+       var $mFunctionTagHooks = array();
+       var $mStripList  = array();
+       var $mDefaultStripList  = array();
+       var $mVarCache = array();
+       var $mImageParams = array();
+       var $mImageParamsMagicArray = array();
+       var $mMarkerIndex = 0;
+       var $mFirstCall = true;
+       var $mVariables, $mSubstWords; # Initialised by initialiseVariables()
+       var $mConf, $mPreprocessor, $mExtLinkBracketedRegex, $mUrlProtocols; # Initialised in constructor
 
 
        # Cleared with clearState():
@@ -104,21 +112,22 @@ class Parser
        var $mIncludeCount, $mArgStack, $mLastSection, $mInPre;
        var $mLinkHolders, $mLinkID;
        var $mIncludeSizes, $mPPNodeCount, $mDefaultSort;
-       var $mTplExpandCache; // empty-frame expansion cache
+       var $mTplExpandCache; # empty-frame expansion cache
        var $mTplRedirCache, $mTplDomCache, $mHeadings, $mDoubleUnderscores;
-       var $mExpensiveFunctionCount; // number of expensive parser function calls
+       var $mExpensiveFunctionCount; # number of expensive parser function calls
+       var $mUser; # User object; only used when doing pre-save transform
 
        # Temporary
        # These are variables reset at least once per parse regardless of $clearState
-       var $mOptions,      // ParserOptions object
-               $mTitle,        // Title context, used for self-link rendering and similar things
-               $mOutputType,   // Output type, one of the OT_xxx constants
-               $ot,            // Shortcut alias, see setOutputType()
-               $mRevisionId,   // ID to display in {{REVISIONID}} tags
-               $mRevisionTimestamp, // The timestamp of the specified revision ID
-               $mRevIdForTs;   // The revision ID which was used to fetch the timestamp
-
-       /**#@-*/
+       var $mOptions;      # ParserOptions object
+       var $mTitle;        # Title context, used for self-link rendering and similar things
+       var $mOutputType;   # Output type, one of the OT_xxx constants
+       var $ot;            # Shortcut alias, see setOutputType()
+       var $mRevisionObject; # The revision object of the specified revision ID
+       var $mRevisionId;   # ID to display in {{REVISIONID}} tags
+       var $mRevisionTimestamp; # The timestamp of the specified revision ID
+       var $mRevisionUser; # Userto display in {{REVISIONUSER}} tag
+       var $mRevIdForTs;   # The revision ID which was used to fetch the timestamp
 
        /**
         * Constructor
@@ -127,20 +136,13 @@ class Parser
         */
        function __construct( $conf = array() ) {
                $this->mConf = $conf;
-               $this->mTagHooks = array();
-               $this->mTransparentTagHooks = array();
-               $this->mFunctionHooks = array();
-               $this->mFunctionTagHooks = array();
-               $this->mFunctionSynonyms = array( 0 => array(), 1 => array() );
-               $this->mDefaultStripList = $this->mStripList = array();
                $this->mUrlProtocols = wfUrlProtocols();
                $this->mExtLinkBracketedRegex = '/\[(\b(' . wfUrlProtocols() . ')'.
-                       '[^][<>"\\x00-\\x20\\x7F]+) *([^\]\\x0a\\x0d]*?)\]/S';
-               $this->mVarCache = array();
+                       '[^][<>"\\x00-\\x20\\x7F]+) *([^\]\\x00-\\x08\\x0a-\\x1F]*?)\]/S';
                if ( isset( $conf['preprocessorClass'] ) ) {
                        $this->mPreprocessorClass = $conf['preprocessorClass'];
                } elseif ( extension_loaded( 'domxml' ) ) {
-                       // PECL extension that conflicts with the core DOM extension (bug 13770)
+                       # PECL extension that conflicts with the core DOM extension (bug 13770)
                        wfDebug( "Warning: you have the obsolete domxml extension for PHP. Please remove it!\n" );
                        $this->mPreprocessorClass = 'Preprocessor_Hash';
                } elseif ( extension_loaded( 'dom' ) ) {
@@ -148,8 +150,6 @@ class Parser
                } else {
                        $this->mPreprocessorClass = 'Preprocessor_Hash';
                }
-               $this->mMarkerIndex = 0;
-               $this->mFirstCall = true;
        }
 
        /**
@@ -194,6 +194,7 @@ class Parser
                        $this->firstCallInit();
                }
                $this->mOutput = new ParserOutput;
+               $this->mOptions->registerWatcher( array( $this->mOutput, 'recordOption' ) );
                $this->mAutonumber = 0;
                $this->mLastSection = '';
                $this->mDTopen = false;
@@ -203,8 +204,10 @@ class Parser
                $this->mInPre = false;
                $this->mLinkHolders = new LinkHolderArray( $this );
                $this->mLinkID = 0;
-               $this->mRevisionTimestamp = $this->mRevisionId = null;
+               $this->mRevisionObject = $this->mRevisionTimestamp =
+                       $this->mRevisionId = $this->mRevisionUser = null;
                $this->mVarCache = array();
+               $this->mUser = null;
 
                /**
                 * Prefix for temporary replacement strings for the multipass parser.
@@ -216,7 +219,7 @@ class Parser
                 * Must not consist of all title characters, or else it will change
                 * the behaviour of <nowiki> in a link.
                 */
-               #$this->mUniqPrefix = "\x07UNIQ" . Parser::getRandomString();
+               # $this->mUniqPrefix = "\x07UNIQ" . Parser::getRandomString();
                # Changed to \x7f to allow XML double-parsing -- TS
                $this->mUniqPrefix = "\x7fUNIQ" . self::getRandomString();
 
@@ -245,51 +248,6 @@ class Parser
                wfProfileOut( __METHOD__ );
        }
 
-       function setOutputType( $ot ) {
-               $this->mOutputType = $ot;
-               // Shortcut alias
-               $this->ot = array(
-                       'html' => $ot == self::OT_HTML,
-                       'wiki' => $ot == self::OT_WIKI,
-                       'pre' => $ot == self::OT_PREPROCESS,
-               );
-       }
-
-       /**
-        * Set the context title
-        */
-       function setTitle( $t ) {
-               if ( !$t || $t instanceof FakeTitle ) {
-                       $t = Title::newFromText( 'NO TITLE' );
-               }
-
-               if ( strval( $t->getFragment() ) !== '' ) {
-                       # Strip the fragment to avoid various odd effects
-                       $this->mTitle = clone $t;
-                       $this->mTitle->setFragment( '' );
-               } else {
-                       $this->mTitle = $t;
-               }
-       }
-
-       /**
-        * Accessor for mUniqPrefix.
-        *
-        * @public
-        */
-       function uniqPrefix() {
-               if( !isset( $this->mUniqPrefix ) ) {
-                       // @todo Fixme: this is probably *horribly wrong*
-                       // LanguageConverter seems to want $wgParser's uniqPrefix, however
-                       // if this is called for a parser cache hit, the parser may not
-                       // have ever been initialized in the first place.
-                       // Not really sure what the heck is supposed to be going on here.
-                       return '';
-                       //throw new MWException( "Accessing uninitialized mUniqPrefix" );
-               }
-               return $this->mUniqPrefix;
-       }
-
        /**
         * Convert wikitext to HTML
         * Do not call this function recursively.
@@ -308,23 +266,27 @@ class Parser
                 * to internalParse() which does all the real work.
                 */
 
-               global $wgUseTidy, $wgAlwaysUseTidy, $wgContLang, $wgDisableLangConversion;
+               global $wgUseTidy, $wgAlwaysUseTidy, $wgContLang, $wgDisableLangConversion, $wgDisableTitleConversion;
                $fname = __METHOD__.'-' . wfGetCaller();
                wfProfileIn( __METHOD__ );
                wfProfileIn( $fname );
 
+               $this->mOptions = $options;
                if ( $clearState ) {
                        $this->clearState();
                }
 
-               $this->mOptions = $options;
-               $this->setTitle( $title ); // Page title has to be set for the pre-processor
+               $this->setTitle( $title ); # Page title has to be set for the pre-processor
 
                $oldRevisionId = $this->mRevisionId;
+               $oldRevisionObject = $this->mRevisionObject;
                $oldRevisionTimestamp = $this->mRevisionTimestamp;
-               if( $revid !== null ) {
+               $oldRevisionUser = $this->mRevisionUser;
+               if ( $revid !== null ) {
                        $this->mRevisionId = $revid;
+                       $this->mRevisionObject = null;
                        $this->mRevisionTimestamp = null;
+                       $this->mRevisionUser = null;
                }
                $this->setOutputType( self::OT_HTML );
                wfRunHooks( 'ParserBeforeStrip', array( &$this, &$text, &$this->mStripState ) );
@@ -338,67 +300,89 @@ class Parser
                $fixtags = array(
                        # french spaces, last one Guillemet-left
                        # only if there is something before the space
-                       '/(.) (?=\\?|:|;|!|%|\\302\\273)/' => '\\1&nbsp;\\2',
+                       '/(.) (?=\\?|:|;|!|%|\\302\\273)/' => '\\1&#160;\\2',
                        # french spaces, Guillemet-right
-                       '/(\\302\\253) /' => '\\1&nbsp;',
-                       '/&nbsp;(!\s*important)/' => ' \\1', #Beware of CSS magic word !important, bug #11874.
+                       '/(\\302\\253) /' => '\\1&#160;',
+                       '/&#160;(!\s*important)/' => ' \\1', # Beware of CSS magic word !important, bug #11874.
                );
-               $text = preg_replace( array_keys($fixtags), array_values($fixtags), $text );
+               $text = preg_replace( array_keys( $fixtags ), array_values( $fixtags ), $text );
 
                $text = $this->doBlockLevels( $text, $linestart );
 
                $this->replaceLinkHolders( $text );
 
-               // The position of the convert() call should not be changed. it
-               // assumes that the links are all replaced and the only thing left
-               // is the <nowiki> mark.
+               /**
+                * The page doesn't get language converted if
+                * a) It's disabled
+                * b) Content isn't converted
+                * c) It's a conversion table
+                */
                if ( !( $wgDisableLangConversion
                                || isset( $this->mDoubleUnderscores['nocontentconvert'] )
-                               || $this->mTitle->isTalkPage()
                                || $this->mTitle->isConversionTable() ) ) {
+
+                       # The position of the convert() call should not be changed. it
+                       # assumes that the links are all replaced and the only thing left
+                       # is the <nowiki> mark.
+
                        $text = $wgContLang->convert( $text );
                }
 
-               // A title may have been set in a conversion rule.
-               // Note that if a user tries to set a title in a conversion
-               // rule but content conversion was not done, then the parser
-               // won't pick it up.  This is probably expected behavior.
-               if ( $wgContLang->getConvRuleTitle() ) {
-                       $this->mOutput->setTitleText( $wgContLang->getConvRuleTitle() );
+               /**
+                * A page get its title converted except:
+                * a) Language conversion is globally disabled
+                * b) Title convert is globally disabled
+                * c) The page is a redirect page
+                * d) User request with a "linkconvert" set to "no"
+                * e) A "nocontentconvert" magic word has been set
+                * f) A "notitleconvert" magic word has been set
+                * g) User sets "noconvertlink" in his/her preference
+                *
+                * Note that if a user tries to set a title in a conversion
+                * rule but content conversion was not done, then the parser
+                * won't pick it up.  This is probably expected behavior.
+                */
+               if ( !( $wgDisableLangConversion
+                               || $wgDisableTitleConversion
+                               || isset( $this->mDoubleUnderscores['nocontentconvert'] )
+                               || isset( $this->mDoubleUnderscores['notitleconvert'] )
+                               || $this->mOutput->getDisplayTitle() !== false ) )
+               {
+                       $convruletitle = $wgContLang->getConvRuleTitle();
+                       if ( $convruletitle ) {
+                               $this->mOutput->setTitleText( $convruletitle );
+                       } else {
+                               $titleText = $wgContLang->convertTitle( $title );
+                               $this->mOutput->setTitleText( $titleText );
+                       }
                }
 
                $text = $this->mStripState->unstripNoWiki( $text );
 
                wfRunHooks( 'ParserBeforeTidy', array( &$this, &$text ) );
 
-               if ( $this->mTransparentTagHooks ) {
-                       //!JF Move to its own function
-                       $uniq_prefix = $this->mUniqPrefix;
-                       $matches = array();
-                       $elements = array_keys( $this->mTransparentTagHooks );
-                       $text = self::extractTagsAndParams( $elements, $text, $matches, $uniq_prefix );
-
-                       foreach( $matches as $marker => $data ) {
-                               list( $element, $content, $params, $tag ) = $data;
-                               $tagName = strtolower( $element );
-                               if( isset( $this->mTransparentTagHooks[$tagName] ) ) {
-                                       $output = call_user_func_array( $this->mTransparentTagHooks[$tagName],
-                                               array( $content, $params, $this ) );
-                               } else {
-                                       $output = $tag;
-                               }
-                               $this->mStripState->general->setPair( $marker, $output );
+//!JF Move to its own function
+
+               $uniq_prefix = $this->mUniqPrefix;
+               $matches = array();
+               $elements = array_keys( $this->mTransparentTagHooks );
+               $text = $this->extractTagsAndParams( $elements, $text, $matches, $uniq_prefix );
+
+               foreach ( $matches as $marker => $data ) {
+                       list( $element, $content, $params, $tag ) = $data;
+                       $tagName = strtolower( $element );
+                       if ( isset( $this->mTransparentTagHooks[$tagName] ) ) {
+                               $output = call_user_func_array( $this->mTransparentTagHooks[$tagName], array( $content, $params, $this ) );
+                       } else {
+                               $output = $tag;
                        }
+                       $this->mStripState->general->setPair( $marker, $output );
                }
-
-               # This was originally inserted for transparent tag hooks (now deprecated)
-               # but some extensions (notably <poem>) rely on the extra unstripGeneral()
-               # after unstripNoWiki() so they can modify the contents of <nowiki> tags.
                $text = $this->mStripState->unstripGeneral( $text );
 
                $text = Sanitizer::normalizeCharReferences( $text );
 
-               if ( ( $wgUseTidy && $this->mOptions->mTidy ) || $wgAlwaysUseTidy ) {
+               if ( ( $wgUseTidy && $this->mOptions->getTidy() ) || $wgAlwaysUseTidy ) {
                        $text = MWTidy::tidy( $text );
                } else {
                        # attempt to sanitize at least some nesting problems
@@ -436,12 +420,11 @@ class Parser
 
                # Information on include size limits, for the benefit of users who try to skirt them
                if ( $this->mOptions->getEnableLimitReport() ) {
-                       global $wgExpensiveParserFunctionLimit;
                        $max = $this->mOptions->getMaxIncludeSize();
                        $PFreport = "Expensive parser function count: {$this->mExpensiveFunctionCount}/$wgExpensiveParserFunctionLimit\n";
                        $limitReport =
                                "NewPP limit report\n" .
-                               "Preprocessor node count: {$this->mPPNodeCount}/{$this->mOptions->mMaxPPNodeCount}\n" .
+                               "Preprocessor node count: {$this->mPPNodeCount}/{$this->mOptions->getMaxPPNodeCount()}\n" .
                                "Post-expand include size: {$this->mIncludeSizes['post-expand']}/$max bytes\n" .
                                "Template argument size: {$this->mIncludeSizes['arg']}/$max bytes\n".
                                $PFreport;
@@ -451,7 +434,9 @@ class Parser
                $this->mOutput->setText( $text );
 
                $this->mRevisionId = $oldRevisionId;
+               $this->mRevisionObject = $oldRevisionObject;
                $this->mRevisionTimestamp = $oldRevisionTimestamp;
+               $this->mRevisionUser = $oldRevisionUser;
                wfProfileOut( $fname );
                wfProfileOut( __METHOD__ );
 
@@ -465,7 +450,7 @@ class Parser
         * If $frame is not provided, then template variables (e.g., {{{1}}}) within $text are not expanded
         *
         * @param $text String: text extension wants to have parsed
-        * @param PPFrame $frame: The frame to use for expanding any template variables
+        * @param $frame PPFrame: The frame to use for expanding any template variables
         */
        function recursiveTagParse( $text, $frame=false ) {
                wfProfileIn( __METHOD__ );
@@ -482,11 +467,11 @@ class Parser
         */
        function preprocess( $text, $title, $options, $revid = null ) {
                wfProfileIn( __METHOD__ );
+               $this->mOptions = $options;
                $this->clearState();
                $this->setOutputType( self::OT_PREPROCESS );
-               $this->mOptions = $options;
                $this->setTitle( $title );
-               if( $revid !== null ) {
+               if ( $revid !== null ) {
                        $this->mRevisionId = $revid;
                }
                wfRunHooks( 'ParserBeforeStrip', array( &$this, &$text, &$this->mStripState ) );
@@ -498,23 +483,21 @@ class Parser
        }
 
        /**
-        * Get the wikitext of a page as though it was transcluded.
+        * Process the wikitext for the ?preload= feature. (bug 5210)
         *
-        * Specifically <includeonly> etc. are parsed, redirects are followed, comments
-        * are removed, but templates arguments and parser functions are untouched.
-        *
-        * This is not called by the parser itself, see braceSubstitution for its transclusion. 
+        * <noinclude>, <includeonly> etc. are parsed as for template transclusion,
+        * comments, templates, arguments, tags hooks and parser functions are untouched.
         */
-       public function getTransclusionText( $title, $options ) {
-               // Must initialize first
-               $this->clearState();
-               $this->setOutputType( self::OT_INCLUDES );
+       public function getPreloadText( $text, $title, $options ) {
+               # Parser (re)initialisation
                $this->mOptions = $options;
-               $this->setTitle( new FakeTitle ); 
+               $this->clearState();
+               $this->setOutputType( self::OT_PLAIN );
+               $this->setTitle( $title );
 
-               list( $text, $title ) = $this->getTemplateDom( $title );
                $flags = PPFrame::NO_ARGS | PPFrame::NO_TEMPLATES;
-               return $this->getPreprocessor()->newFrame()->expand( $text, $flags );
+               $dom = $this->preprocessToDom( $text, self::PTD_FOR_INCLUSION );
+               return $this->getPreprocessor()->newFrame()->expand( $dom, $flags );
        }
 
        /**
@@ -523,15 +506,131 @@ class Parser
         * @private
         * @static
         */
-       function getRandomString() {
-               return dechex(mt_rand(0, 0x7fffffff)) . dechex(mt_rand(0, 0x7fffffff));
+       static private function getRandomString() {
+               return dechex( mt_rand( 0, 0x7fffffff ) ) . dechex( mt_rand( 0, 0x7fffffff ) );
+       }
+
+       /**
+        * Set the current user.
+        * Should only be used when doing pre-save transform.
+        *
+        * @param $user Mixed: User object or null (to reset)
+        */
+       function setUser( $user ) {
+               $this->mUser = $user;
+       }
+
+       /**
+        * Accessor for mUniqPrefix.
+        *
+        * @return String
+        */
+       public function uniqPrefix() {
+               if ( !isset( $this->mUniqPrefix ) ) {
+                       # @todo Fixme: this is probably *horribly wrong*
+                       # LanguageConverter seems to want $wgParser's uniqPrefix, however
+                       # if this is called for a parser cache hit, the parser may not
+                       # have ever been initialized in the first place.
+                       # Not really sure what the heck is supposed to be going on here.
+                       return '';
+                       # throw new MWException( "Accessing uninitialized mUniqPrefix" );
+               }
+               return $this->mUniqPrefix;
        }
 
-       function &getTitle() { return $this->mTitle; }
-       function getOptions() { return $this->mOptions; }
-       function getRevisionId() { return $this->mRevisionId; }
-       function getOutput() { return $this->mOutput; }
-       function nextLinkID() { return $this->mLinkID++; }
+       /**
+        * Set the context title
+        */
+       function setTitle( $t ) {
+               if ( !$t || $t instanceof FakeTitle ) {
+                       $t = Title::newFromText( 'NO TITLE' );
+               }
+
+               if ( strval( $t->getFragment() ) !== '' ) {
+                       # Strip the fragment to avoid various odd effects
+                       $this->mTitle = clone $t;
+                       $this->mTitle->setFragment( '' );
+               } else {
+                       $this->mTitle = $t;
+               }
+       }
+
+       /**
+        * Accessor for the Title object
+        *
+        * @return Title object
+        */
+       function getTitle() {
+               return $this->mTitle;
+       }
+
+       /**
+        * Accessor/mutator for the Title object
+        *
+        * @param $x New Title object or null to just get the current one
+        * @return Title object
+        */
+       function Title( $x = null ) {
+               return wfSetVar( $this->mTitle, $x );
+       }
+
+       /**
+        * Set the output type
+        *
+        * @param $ot Integer: new value
+        */
+       function setOutputType( $ot ) {
+               $this->mOutputType = $ot;
+               # Shortcut alias
+               $this->ot = array(
+                       'html' => $ot == self::OT_HTML,
+                       'wiki' => $ot == self::OT_WIKI,
+                       'pre' => $ot == self::OT_PREPROCESS,
+                       'plain' => $ot == self::OT_PLAIN,
+               );
+       }
+
+       /**
+        * Accessor/mutator for the output type
+        *
+        * @param $x New value or null to just get the current one
+        * @return Integer
+        */
+       function OutputType( $x = null ) {
+               return wfSetVar( $this->mOutputType, $x );
+       }
+
+       /**
+        * Get the ParserOutput object
+        *
+        * @return ParserOutput object
+        */
+       function getOutput() {
+               return $this->mOutput;
+       }
+
+       /**
+        * Get the ParserOptions object
+        *
+        * @return ParserOptions object
+        */
+       function getOptions() {
+               return $this->mOptions;
+       }
+
+       /**
+        * Accessor/mutator for the ParserOptions object
+        *
+        * @param $x New value or null to just get the current one
+        * @return Current ParserOptions object
+        */
+       function Options( $x = null ) {
+               return wfSetVar( $this->mOptions, $x );
+       }
+
+       function nextLinkID() {
+               return $this->mLinkID++;
+       }
 
        function getFunctionLang() {
                global $wgLang, $wgContLang;
@@ -544,8 +643,23 @@ class Parser
                }
        }
 
+       /**
+        * Get a User object either from $this->mUser, if set, or from the
+        * ParserOptions object otherwise
+        *
+        * @return User object
+        */
+       function getUser() {
+               if ( !is_null( $this->mUser ) ) {
+                       return $this->mUser;
+               }
+               return $this->mOptions->getUser();
+       }
+
        /**
         * Get a preprocessor object
+        *
+        * @return Preprocessor instance
         */
        function getPreprocessor() {
                if ( !isset( $this->mPreprocessor ) ) {
@@ -568,12 +682,13 @@ class Parser
         *
         * @param $elements list of element names. Comments are always extracted.
         * @param $text Source text string.
+        * @param $matches Out parameter, Array: extracted tags
         * @param $uniq_prefix
+        * @return String: stripped text
         *
-        * @public
         * @static
         */
-       function extractTagsAndParams($elements, $text, &$matches, $uniq_prefix = ''){
+       public function extractTagsAndParams( $elements, $text, &$matches, $uniq_prefix = '' ) {
                static $n = 1;
                $stripped = '';
                $matches = array();
@@ -584,40 +699,40 @@ class Parser
                while ( $text != '' ) {
                        $p = preg_split( $start, $text, 2, PREG_SPLIT_DELIM_CAPTURE );
                        $stripped .= $p[0];
-                       if( count( $p ) < 5 ) {
+                       if ( count( $p ) < 5 ) {
                                break;
                        }
-                       if( count( $p ) > 5 ) {
-                               // comment
+                       if ( count( $p ) > 5 ) {
+                               # comment
                                $element    = $p[4];
                                $attributes = '';
                                $close      = '';
                                $inside     = $p[5];
                        } else {
-                               // tag
+                               # tag
                                $element    = $p[1];
                                $attributes = $p[2];
                                $close      = $p[3];
                                $inside     = $p[4];
                        }
 
-                       $marker = "$uniq_prefix-$element-" . sprintf('%08X', $n++) . self::MARKER_SUFFIX;
+                       $marker = "$uniq_prefix-$element-" . sprintf( '%08X', $n++ ) . self::MARKER_SUFFIX;
                        $stripped .= $marker;
 
                        if ( $close === '/>' ) {
-                               // Empty element tag, <tag />
+                               # Empty element tag, <tag />
                                $content = null;
                                $text = $inside;
                                $tail = null;
                        } else {
-                               if( $element === '!--' ) {
+                               if ( $element === '!--' ) {
                                        $end = '/(-->)/';
                                } else {
                                        $end = "/(<\\/$element\\s*>)/i";
                                }
                                $q = preg_split( $end, $inside, 2, PREG_SPLIT_DELIM_CAPTURE );
                                $content = $q[0];
-                               if( count( $q ) < 3 ) {
+                               if ( count( $q ) < 3 ) {
                                        # No end tag -- let it run out to the end of the text.
                                        $tail = '';
                                        $text = '';
@@ -645,7 +760,7 @@ class Parser
        /**
         * @deprecated use replaceVariables
         */
-       function strip( $text, $state, $stripcomments = false , $dontstrip = array () ) {
+       function strip( $text, $state, $stripcomments = false , $dontstrip = array() ) {
                return $text;
        }
 
@@ -705,34 +820,35 @@ class Parser
         *
         * @private
         */
-       function doTableStuff ( $text ) {
+       function doTableStuff( $text ) {
                wfProfileIn( __METHOD__ );
 
                $lines = StringUtils::explode( "\n", $text );
                $out = '';
-               $td_history = array (); // Is currently a td tag open?
-               $last_tag_history = array (); // Save history of last lag activated (td, th or caption)
-               $tr_history = array (); // Is currently a tr tag open?
-               $tr_attributes = array (); // history of tr attributes
-               $has_opened_tr = array(); // Did this table open a <tr> element?
-               $indent_level = 0; // indent level of the table
+               $td_history = array(); # Is currently a td tag open?
+               $last_tag_history = array(); # Save history of last lag activated (td, th or caption)
+               $tr_history = array(); # Is currently a tr tag open?
+               $tr_attributes = array(); # history of tr attributes
+               $has_opened_tr = array(); # Did this table open a <tr> element?
+               $indent_level = 0; # indent level of the table
 
                foreach ( $lines as $outLine ) {
                        $line = trim( $outLine );
 
-                       if( $line == '' ) { // empty line, go to next line
+                       if ( $line === '' ) { # empty line, go to next line
                                $out .= $outLine."\n";
                                continue;
                        }
+
                        $first_character = $line[0];
                        $matches = array();
 
                        if ( preg_match( '/^(:*)\{\|(.*)$/', $line , $matches ) ) {
-                               // First check if we are starting a new table
+                               # First check if we are starting a new table
                                $indent_level = strlen( $matches[1] );
 
                                $attributes = $this->mStripState->unstripBoth( $matches[2] );
-                               $attributes = Sanitizer::fixTagAttributes ( $attributes , 'table' );
+                               $attributes = Sanitizer::fixTagAttributes( $attributes , 'table' );
 
                                $outLine = str_repeat( '<dl><dd>' , $indent_level ) . "<table{$attributes}>";
                                array_push( $td_history , false );
@@ -740,156 +856,152 @@ class Parser
                                array_push( $tr_history , false );
                                array_push( $tr_attributes , '' );
                                array_push( $has_opened_tr , false );
-                       } else if ( count ( $td_history ) == 0 ) {
-                               // Don't do any of the following
+                       } elseif ( count( $td_history ) == 0 ) {
+                               # Don't do any of the following
                                $out .= $outLine."\n";
                                continue;
-                       } else if ( substr ( $line , 0 , 2 ) === '|}' ) {
-                               // We are ending a table
-                               $line = '</table>' . substr ( $line , 2 );
-                               $last_tag = array_pop ( $last_tag_history );
+                       } elseif ( substr( $line , 0 , 2 ) === '|}' ) {
+                               # We are ending a table
+                               $line = '</table>' . substr( $line , 2 );
+                               $last_tag = array_pop( $last_tag_history );
 
-                               if ( !array_pop ( $has_opened_tr ) ) {
+                               if ( !array_pop( $has_opened_tr ) ) {
                                        $line = "<tr><td></td></tr>{$line}";
                                }
 
-                               if ( array_pop ( $tr_history ) ) {
+                               if ( array_pop( $tr_history ) ) {
                                        $line = "</tr>{$line}";
                                }
 
-                               if ( array_pop ( $td_history ) ) {
+                               if ( array_pop( $td_history ) ) {
                                        $line = "</{$last_tag}>{$line}";
                                }
-                               array_pop ( $tr_attributes );
+                               array_pop( $tr_attributes );
                                $outLine = $line . str_repeat( '</dd></dl>' , $indent_level );
-                       } else if ( substr ( $line , 0 , 2 ) === '|-' ) {
-                               // Now we have a table row
+                       } elseif ( substr( $line , 0 , 2 ) === '|-' ) {
+                               # Now we have a table row
                                $line = preg_replace( '#^\|-+#', '', $line );
 
-                               // Whats after the tag is now only attributes
+                               # Whats after the tag is now only attributes
                                $attributes = $this->mStripState->unstripBoth( $line );
                                $attributes = Sanitizer::fixTagAttributes( $attributes, 'tr' );
                                array_pop( $tr_attributes );
                                array_push( $tr_attributes, $attributes );
 
                                $line = '';
-                               $last_tag = array_pop ( $last_tag_history );
-                               array_pop ( $has_opened_tr );
-                               array_push ( $has_opened_tr , true );
+                               $last_tag = array_pop( $last_tag_history );
+                               array_pop( $has_opened_tr );
+                               array_push( $has_opened_tr , true );
 
-                               if ( array_pop ( $tr_history ) ) {
+                               if ( array_pop( $tr_history ) ) {
                                        $line = '</tr>';
                                }
 
-                               if ( array_pop ( $td_history ) ) {
+                               if ( array_pop( $td_history ) ) {
                                        $line = "</{$last_tag}>{$line}";
                                }
 
                                $outLine = $line;
-                               array_push ( $tr_history , false );
-                               array_push ( $td_history , false );
-                               array_push ( $last_tag_history , '' );
-                       }
-                       else if ( $first_character === '|' || $first_character === '!' || substr ( $line , 0 , 2 )  === '|+' ) {
-                               // This might be cell elements, td, th or captions
-                               if ( substr ( $line , 0 , 2 ) === '|+' ) {
+                               array_push( $tr_history , false );
+                               array_push( $td_history , false );
+                               array_push( $last_tag_history , '' );
+                       } elseif ( $first_character === '|' || $first_character === '!' || substr( $line , 0 , 2 )  === '|+' ) {
+                               # This might be cell elements, td, th or captions
+                               if ( substr( $line , 0 , 2 ) === '|+' ) {
                                        $first_character = '+';
-                                       $line = substr ( $line , 1 );
+                                       $line = substr( $line , 1 );
                                }
 
-                               $line = substr ( $line , 1 );
+                               $line = substr( $line , 1 );
 
                                if ( $first_character === '!' ) {
-                                       $line = str_replace ( '!!' , '||' , $line );
+                                       $line = str_replace( '!!' , '||' , $line );
                                }
 
-                               // Split up multiple cells on the same line.
-                               // FIXME : This can result in improper nesting of tags processed
-                               // by earlier parser steps, but should avoid splitting up eg
-                               // attribute values containing literal "||".
+                               # Split up multiple cells on the same line.
+                               # FIXME : This can result in improper nesting of tags processed
+                               # by earlier parser steps, but should avoid splitting up eg
+                               # attribute values containing literal "||".
                                $cells = StringUtils::explodeMarkup( '||' , $line );
 
                                $outLine = '';
 
-                               // Loop through each table cell
-                               foreach ( $cells as $cell )
-                               {
+                               # Loop through each table cell
+                               foreach ( $cells as $cell ) {
                                        $previous = '';
-                                       if ( $first_character !== '+' )
-                                       {
-                                               $tr_after = array_pop ( $tr_attributes );
-                                               if ( !array_pop ( $tr_history ) ) {
+                                       if ( $first_character !== '+' ) {
+                                               $tr_after = array_pop( $tr_attributes );
+                                               if ( !array_pop( $tr_history ) ) {
                                                        $previous = "<tr{$tr_after}>\n";
                                                }
-                                               array_push ( $tr_history , true );
-                                               array_push ( $tr_attributes , '' );
-                                               array_pop ( $has_opened_tr );
-                                               array_push ( $has_opened_tr , true );
+                                               array_push( $tr_history , true );
+                                               array_push( $tr_attributes , '' );
+                                               array_pop( $has_opened_tr );
+                                               array_push( $has_opened_tr , true );
                                        }
 
-                                       $last_tag = array_pop ( $last_tag_history );
+                                       $last_tag = array_pop( $last_tag_history );
 
-                                       if ( array_pop ( $td_history ) ) {
-                                               $previous = "</{$last_tag}>{$previous}";
+                                       if ( array_pop( $td_history ) ) {
+                                               $previous = "</{$last_tag}>\n{$previous}";
                                        }
 
                                        if ( $first_character === '|' ) {
                                                $last_tag = 'td';
-                                       } else if ( $first_character === '!' ) {
+                                       } elseif ( $first_character === '!' ) {
                                                $last_tag = 'th';
-                                       } else if ( $first_character === '+' ) {
+                                       } elseif ( $first_character === '+' ) {
                                                $last_tag = 'caption';
                                        } else {
                                                $last_tag = '';
                                        }
 
-                                       array_push ( $last_tag_history , $last_tag );
+                                       array_push( $last_tag_history , $last_tag );
 
-                                       // A cell could contain both parameters and data
-                                       $cell_data = explode ( '|' , $cell , 2 );
+                                       # A cell could contain both parameters and data
+                                       $cell_data = explode( '|' , $cell , 2 );
 
-                                       // Bug 553: Note that a '|' inside an invalid link should not
-                                       // be mistaken as delimiting cell parameters
+                                       # Bug 553: Note that a '|' inside an invalid link should not
+                                       # be mistaken as delimiting cell parameters
                                        if ( strpos( $cell_data[0], '[[' ) !== false ) {
                                                $cell = "{$previous}<{$last_tag}>{$cell}";
-                                       } else if ( count ( $cell_data ) == 1 )
+                                       } elseif ( count( $cell_data ) == 1 ) {
                                                $cell = "{$previous}<{$last_tag}>{$cell_data[0]}";
-                                       else {
+                                       else {
                                                $attributes = $this->mStripState->unstripBoth( $cell_data[0] );
                                                $attributes = Sanitizer::fixTagAttributes( $attributes , $last_tag );
                                                $cell = "{$previous}<{$last_tag}{$attributes}>{$cell_data[1]}";
                                        }
 
                                        $outLine .= $cell;
-                                       array_push ( $td_history , true );
+                                       array_push( $td_history , true );
                                }
                        }
                        $out .= $outLine . "\n";
                }
 
-               // Closing open td, tr && table
-               while ( count ( $td_history ) > 0 )
-               {
-                       if ( array_pop ( $td_history ) ) {
+               # Closing open td, tr && table
+               while ( count( $td_history ) > 0 ) {
+                       if ( array_pop( $td_history ) ) {
                                $out .= "</td>\n";
                        }
-                       if ( array_pop ( $tr_history ) ) {
+                       if ( array_pop( $tr_history ) ) {
                                $out .= "</tr>\n";
                        }
-                       if ( !array_pop ( $has_opened_tr ) ) {
+                       if ( !array_pop( $has_opened_tr ) ) {
                                $out .= "<tr><td></td></tr>\n" ;
                        }
 
                        $out .= "</table>\n";
                }
 
-               // Remove trailing line-ending (b/c)
+               # Remove trailing line-ending (b/c)
                if ( substr( $out, -1 ) === "\n" ) {
                        $out = substr( $out, 0, -1 );
                }
 
-               // special case: don't return empty table
-               if( $out === "<table>\n<tr><td></td></tr>\n</table>" ) {
+               # special case: don't return empty table
+               if ( $out === "<table>\n<tr><td></td></tr>\n</table>" ) {
                        $out = '';
                }
 
@@ -915,29 +1027,29 @@ class Parser
                        return $text ;
                }
 
-               // if $frame is provided, then use $frame for replacing any variables
-               if ($frame) {
-                       // use frame depth to infer how include/noinclude tags should be handled
-                       // depth=0 means this is the top-level document; otherwise it's an included document
-                       if( !$frame->depth )
+               # if $frame is provided, then use $frame for replacing any variables
+               if ( $frame ) {
+                       # use frame depth to infer how include/noinclude tags should be handled
+                       # depth=0 means this is the top-level document; otherwise it's an included document
+                       if ( !$frame->depth ) {
                                $flag = 0;
-                       else
+                       } else {
                                $flag = Parser::PTD_FOR_INCLUSION;
+                       }
                        $dom = $this->preprocessToDom( $text, $flag );
                        $text = $frame->expand( $dom );
-               }
-               // if $frame is not provided, then use old-style replaceVariables
-               else {
+               } else {
+                       # if $frame is not provided, then use old-style replaceVariables
                        $text = $this->replaceVariables( $text );
                }
 
                $text = Sanitizer::removeHTMLtags( $text, array( &$this, 'attributeStripCallback' ), false, array_keys( $this->mTransparentTagHooks ) );
                wfRunHooks( 'InternalParseBeforeLinks', array( &$this, &$text, &$this->mStripState ) );
 
-               // Tables need to come after variable replacement for things to work
-               // properly; putting them before other transformations should keep
-               // exciting things like link expansions from showing up in surprising
-               // places.
+               # Tables need to come after variable replacement for things to work
+               # properly; putting them before other transformations should keep
+               # exciting things like link expansions from showing up in surprising
+               # places.
                $text = $this->doTableStuff( $text );
 
                $text = preg_replace( '/(^|\n)-----*/', '\\1<hr />', $text );
@@ -945,17 +1057,17 @@ class Parser
                $text = $this->doDoubleUnderscore( $text );
 
                $text = $this->doHeadings( $text );
-               if( $this->mOptions->getUseDynamicDates() ) {
+               if ( $this->mOptions->getUseDynamicDates() ) {
                        $df = DateFormatter::getInstance();
                        $text = $df->reformat( $this->mOptions->getDateFormat(), $text );
                }
-               $text = $this->doAllQuotes( $text );
                $text = $this->replaceInternalLinks( $text );
+               $text = $this->doAllQuotes( $text );
                $text = $this->replaceExternalLinks( $text );
 
                # replaceInternalLinks may sometimes leave behind
                # absolute URLs, which have to be masked to hide them from replaceExternalLinks
-               $text = str_replace($this->mUniqPrefix.'NOPARSE', '', $text);
+               $text = str_replace( $this->mUniqPrefix.'NOPARSE', '', $text );
 
                $text = $this->doMagicLinks( $text );
                $text = $this->formatHeadings( $text, $origText, $isMain );
@@ -977,15 +1089,15 @@ class Parser
                $urlChar = self::EXT_LINK_URL_CLASS;
                $text = preg_replace_callback(
                        '!(?:                           # Start cases
-                               (<a.*?</a>) |               # m[1]: Skip link text
+                               (<a[ \t\r\n>].*?</a>) |     # m[1]: Skip link text
                                (<.*?>) |                   # m[2]: Skip stuff inside HTML elements' . "
                                (\\b(?:$prots)$urlChar+) |  # m[3]: Free external links" . '
                                (?:RFC|PMID)\s+([0-9]+) |   # m[4]: RFC or PMID, capture number
                                ISBN\s+(\b                  # m[5]: ISBN, capture number
-                                   (?: 97[89] [\ \-]? )?   # optional 13-digit ISBN prefix
-                                   (?: [0-9]  [\ \-]? ){9} # 9 digits with opt. delimiters
-                                   [0-9Xx]                 # check digit
-                                   \b)
+                                       (?: 97[89] [\ \-]? )?   # optional 13-digit ISBN prefix
+                                       (?: [0-9]  [\ \-]? ){9} # 9 digits with opt. delimiters
+                                       [0-9Xx]                 # check digit
+                                       \b)
                        )!x', array( &$this, 'magicLinkCallback' ), $text );
                wfProfileOut( __METHOD__ );
                return $text;
@@ -1003,7 +1115,6 @@ class Parser
                        return $this->makeFreeExternalLink( $m[0] );
                } elseif ( isset( $m[4] ) && $m[4] !== '' ) {
                        # RFC or PMID
-                       $CssClass = '';
                        if ( substr( $m[0], 0, 3 ) === 'RFC' ) {
                                $keyword = 'RFC';
                                $urlmsg = 'rfcurl';
@@ -1016,10 +1127,10 @@ class Parser
                                $id = $m[4];
                        } else {
                                throw new MWException( __METHOD__.': unrecognised match type "' .
-                                       substr($m[0], 0, 20 ) . '"' );
+                                       substr( $m[0], 0, 20 ) . '"' );
                        }
-                       $url = wfMsg( $urlmsg, $id);
-                       $sk = $this->mOptions->getSkin();
+                       $url = wfMsgForContent( $urlmsg, $id);
+                       $sk = $this->mOptions->getSkin( $this->mTitle );
                        $la = $sk->getExternalLinkAttributes( "external $CssClass" );
                        return "<a href=\"{$url}\"{$la}>{$keyword} {$id}</a>";
                } elseif ( isset( $m[5] ) && $m[5] !== '' ) {
@@ -1048,16 +1159,16 @@ class Parser
                global $wgContLang;
                wfProfileIn( __METHOD__ );
 
-               $sk = $this->mOptions->getSkin();
+               $sk = $this->mOptions->getSkin( $this->mTitle );
                $trail = '';
 
                # The characters '<' and '>' (which were escaped by
                # removeHTMLtags()) should not be included in
                # URLs, per RFC 2396.
                $m2 = array();
-               if (preg_match('/&(lt|gt);/', $url, $m2, PREG_OFFSET_CAPTURE)) {
-                       $trail = substr($url, $m2[0][1]) . $trail;
-                       $url = substr($url, 0, $m2[0][1]);
+               if ( preg_match( '/&(lt|gt);/', $url, $m2, PREG_OFFSET_CAPTURE ) ) {
+                       $trail = substr( $url, $m2[0][1] ) . $trail;
+                       $url = substr( $url, 0, $m2[0][1] );
                }
 
                # Move trailing punctuation to $trail
@@ -1119,7 +1230,7 @@ class Parser
                foreach ( $lines as $line ) {
                        $outtext .= $this->doQuotes( $line ) . "\n";
                }
-               $outtext = substr($outtext, 0,-1);
+               $outtext = substr( $outtext, 0,-1 );
                wfProfileOut( __METHOD__ );
                return $outtext;
        }
@@ -1129,89 +1240,84 @@ class Parser
         */
        public function doQuotes( $text ) {
                $arr = preg_split( "/(''+)/", $text, -1, PREG_SPLIT_DELIM_CAPTURE );
-               if ( count( $arr ) == 1 )
+               if ( count( $arr ) == 1 ) {
                        return $text;
-               else
-               {
+               } else {
                        # First, do some preliminary work. This may shift some apostrophes from
                        # being mark-up to being text. It also counts the number of occurrences
                        # of bold and italics mark-ups.
-                       $i = 0;
                        $numbold = 0;
                        $numitalics = 0;
-                       foreach ( $arr as $r )
-                       {
-                               if ( ( $i % 2 ) == 1 )
-                               {
+                       for ( $i = 0; $i < count( $arr ); $i++ ) {
+                               if ( ( $i % 2 ) == 1 ) {
                                        # If there are ever four apostrophes, assume the first is supposed to
                                        # be text, and the remaining three constitute mark-up for bold text.
-                                       if ( strlen( $arr[$i] ) == 4 )
-                                       {
+                                       if ( strlen( $arr[$i] ) == 4 ) {
                                                $arr[$i-1] .= "'";
                                                $arr[$i] = "'''";
-                                       }
-                                       # If there are more than 5 apostrophes in a row, assume they're all
-                                       # text except for the last 5.
-                                       else if ( strlen( $arr[$i] ) > 5 )
-                                       {
+                                       } elseif ( strlen( $arr[$i] ) > 5 ) {
+                                               # If there are more than 5 apostrophes in a row, assume they're all
+                                               # text except for the last 5.
                                                $arr[$i-1] .= str_repeat( "'", strlen( $arr[$i] ) - 5 );
                                                $arr[$i] = "'''''";
                                        }
                                        # Count the number of occurrences of bold and italics mark-ups.
                                        # We are not counting sequences of five apostrophes.
-                                       if ( strlen( $arr[$i] ) == 2 )      { $numitalics++;             }
-                                       else if ( strlen( $arr[$i] ) == 3 ) { $numbold++;                }
-                                       else if ( strlen( $arr[$i] ) == 5 ) { $numitalics++; $numbold++; }
+                                       if ( strlen( $arr[$i] ) == 2 ) {
+                                               $numitalics++;
+                                       } elseif ( strlen( $arr[$i] ) == 3 ) {
+                                               $numbold++;
+                                       } elseif ( strlen( $arr[$i] ) == 5 ) {
+                                               $numitalics++;
+                                               $numbold++;
+                                       }
                                }
-                               $i++;
                        }
 
                        # If there is an odd number of both bold and italics, it is likely
                        # that one of the bold ones was meant to be an apostrophe followed
                        # by italics. Which one we cannot know for certain, but it is more
                        # likely to be one that has a single-letter word before it.
-                       if ( ( $numbold % 2 == 1 ) && ( $numitalics % 2 == 1 ) )
-                       {
+                       if ( ( $numbold % 2 == 1 ) && ( $numitalics % 2 == 1 ) ) {
                                $i = 0;
                                $firstsingleletterword = -1;
                                $firstmultiletterword = -1;
                                $firstspace = -1;
-                               foreach ( $arr as $r )
-                               {
-                                       if ( ( $i % 2 == 1 ) and ( strlen( $r ) == 3 ) )
-                                       {
-                                               $x1 = substr ($arr[$i-1], -1);
-                                               $x2 = substr ($arr[$i-1], -2, 1);
-                                               if ($x1 === ' ') {
-                                                       if ($firstspace == -1) $firstspace = $i;
-                                               } else if ($x2 === ' ') {
-                                                       if ($firstsingleletterword == -1) $firstsingleletterword = $i;
+                               foreach ( $arr as $r ) {
+                                       if ( ( $i % 2 == 1 ) and ( strlen( $r ) == 3 ) ) {
+                                               $x1 = substr( $arr[$i-1], -1 );
+                                               $x2 = substr( $arr[$i-1], -2, 1 );
+                                               if ( $x1 === ' ' ) {
+                                                       if ( $firstspace == -1 ) {
+                                                               $firstspace = $i;
+                                                       }
+                                               } elseif ( $x2 === ' ') {
+                                                       if ( $firstsingleletterword == -1 ) {
+                                                               $firstsingleletterword = $i;
+                                                       }
                                                } else {
-                                                       if ($firstmultiletterword == -1) $firstmultiletterword = $i;
+                                                       if ( $firstmultiletterword == -1 ) {
+                                                               $firstmultiletterword = $i;
+                                                       }
                                                }
                                        }
                                        $i++;
                                }
 
                                # If there is a single-letter word, use it!
-                               if ($firstsingleletterword > -1)
-                               {
-                                       $arr [ $firstsingleletterword ] = "''";
-                                       $arr [ $firstsingleletterword-1 ] .= "'";
-                               }
-                               # If not, but there's a multi-letter word, use that one.
-                               else if ($firstmultiletterword > -1)
-                               {
-                                       $arr [ $firstmultiletterword ] = "''";
-                                       $arr [ $firstmultiletterword-1 ] .= "'";
-                               }
-                               # ... otherwise use the first one that has neither.
-                               # (notice that it is possible for all three to be -1 if, for example,
-                               # there is only one pentuple-apostrophe in the line)
-                               else if ($firstspace > -1)
-                               {
-                                       $arr [ $firstspace ] = "''";
-                                       $arr [ $firstspace-1 ] .= "'";
+                               if ( $firstsingleletterword > -1 ) {
+                                       $arr[$firstsingleletterword] = "''";
+                                       $arr[$firstsingleletterword-1] .= "'";
+                               } elseif ( $firstmultiletterword > -1 ) {
+                                       # If not, but there's a multi-letter word, use that one.
+                                       $arr[$firstmultiletterword] = "''";
+                                       $arr[$firstmultiletterword-1] .= "'";
+                               } elseif ( $firstspace > -1 ) {
+                                       # ... otherwise use the first one that has neither.
+                                       # (notice that it is possible for all three to be -1 if, for example,
+                                       # there is only one pentuple-apostrophe in the line)
+                                       $arr[$firstspace] = "''";
+                                       $arr[$firstspace-1] .= "'";
                                }
                        }
 
@@ -1220,71 +1326,70 @@ class Parser
                        $buffer = '';
                        $state = '';
                        $i = 0;
-                       foreach ($arr as $r)
-                       {
-                               if (($i % 2) == 0)
-                               {
-                                       if ($state === 'both')
+                       foreach ( $arr as $r ) {
+                               if ( ( $i % 2 ) == 0 ) {
+                                       if ( $state === 'both' ) {
                                                $buffer .= $r;
-                                       else
+                                       } else {
                                                $output .= $r;
-                               }
-                               else
-                               {
-                                       if (strlen ($r) == 2)
-                                       {
-                                               if ($state === 'i')
-                                               { $output .= '</i>'; $state = ''; }
-                                               else if ($state === 'bi')
-                                               { $output .= '</i>'; $state = 'b'; }
-                                               else if ($state === 'ib')
-                                               { $output .= '</b></i><b>'; $state = 'b'; }
-                                               else if ($state === 'both')
-                                               { $output .= '<b><i>'.$buffer.'</i>'; $state = 'b'; }
-                                               else # $state can be 'b' or ''
-                                               { $output .= '<i>'; $state .= 'i'; }
-                                       }
-                                       else if (strlen ($r) == 3)
-                                       {
-                                               if ($state === 'b')
-                                               { $output .= '</b>'; $state = ''; }
-                                               else if ($state === 'bi')
-                                               { $output .= '</i></b><i>'; $state = 'i'; }
-                                               else if ($state === 'ib')
-                                               { $output .= '</b>'; $state = 'i'; }
-                                               else if ($state === 'both')
-                                               { $output .= '<i><b>'.$buffer.'</b>'; $state = 'i'; }
-                                               else # $state can be 'i' or ''
-                                               { $output .= '<b>'; $state .= 'b'; }
                                        }
-                                       else if (strlen ($r) == 5)
-                                       {
-                                               if ($state === 'b')
-                                               { $output .= '</b><i>'; $state = 'i'; }
-                                               else if ($state === 'i')
-                                               { $output .= '</i><b>'; $state = 'b'; }
-                                               else if ($state === 'bi')
-                                               { $output .= '</i></b>'; $state = ''; }
-                                               else if ($state === 'ib')
-                                               { $output .= '</b></i>'; $state = ''; }
-                                               else if ($state === 'both')
-                                               { $output .= '<i><b>'.$buffer.'</b></i>'; $state = ''; }
-                                               else # ($state == '')
-                                               { $buffer = ''; $state = 'both'; }
+                               } else {
+                                       if ( strlen( $r ) == 2 ) {
+                                               if ( $state === 'i' ) {
+                                                       $output .= '</i>'; $state = '';
+                                               } elseif ( $state === 'bi' ) {
+                                                       $output .= '</i>'; $state = 'b';
+                                               } elseif ( $state === 'ib' ) {
+                                                       $output .= '</b></i><b>'; $state = 'b';
+                                               } elseif ( $state === 'both' ) {
+                                                       $output .= '<b><i>'.$buffer.'</i>'; $state = 'b';
+                                               } else { # $state can be 'b' or ''
+                                                       $output .= '<i>'; $state .= 'i';
+                                               }
+                                       } elseif ( strlen( $r ) == 3 ) {
+                                               if ( $state === 'b' ) {
+                                                       $output .= '</b>'; $state = '';
+                                               } elseif ( $state === 'bi' ) {
+                                                       $output .= '</i></b><i>'; $state = 'i';
+                                               } elseif ( $state === 'ib' ) {
+                                                       $output .= '</b>'; $state = 'i';
+                                               } elseif ( $state === 'both' ) {
+                                                       $output .= '<i><b>'.$buffer.'</b>'; $state = 'i';
+                                               } else { # $state can be 'i' or ''
+                                                       $output .= '<b>'; $state .= 'b';
+                                               }
+                                       } elseif ( strlen( $r ) == 5 ) {
+                                               if ( $state === 'b' ) {
+                                                       $output .= '</b><i>'; $state = 'i';
+                                               } elseif ( $state === 'i' ) {
+                                                       $output .= '</i><b>'; $state = 'b';
+                                               } elseif ( $state === 'bi' ) {
+                                                       $output .= '</i></b>'; $state = '';
+                                               } elseif ( $state === 'ib' ) {
+                                                       $output .= '</b></i>'; $state = '';
+                                               } elseif ( $state === 'both' ) {
+                                                       $output .= '<i><b>'.$buffer.'</b></i>'; $state = '';
+                                               } else { # ($state == '')
+                                                       $buffer = ''; $state = 'both';
+                                               }
                                        }
                                }
                                $i++;
                        }
                        # Now close all remaining tags.  Notice that the order is important.
-                       if ($state === 'b' || $state === 'ib')
+                       if ( $state === 'b' || $state === 'ib' ) {
                                $output .= '</b>';
-                       if ($state === 'i' || $state === 'bi' || $state === 'ib')
+                       }
+                       if ( $state === 'i' || $state === 'bi' || $state === 'ib' ) {
                                $output .= '</i>';
-                       if ($state === 'bi')
+                       }
+                       if ( $state === 'bi' ) {
                                $output .= '</b>';
+                       }
                        # There might be lonely ''''', so make sure we have a buffer
-                       if ($state === 'both' && $buffer)
+                       if ( $state === 'both' && $buffer ) {
                                $output .= '<b><i>'.$buffer.'</i></b>';
+                       }
                        return $output;
                }
        }
@@ -1292,7 +1397,7 @@ class Parser
        /**
         * Replace external links (REL)
         *
-        * Note: this is all very hackish and the order of execution matters a lot.
+        * Note: this is all very hackish and the order of execution matters a lot.
         * Make sure to run maintenance/parserTests.php if you change this code.
         *
         * @private
@@ -1301,7 +1406,7 @@ class Parser
                global $wgContLang;
                wfProfileIn( __METHOD__ );
 
-               $sk = $this->mOptions->getSkin();
+               $sk = $this->mOptions->getSkin( $this->mTitle );
 
                $bits = preg_split( $this->mExtLinkBracketedRegex, $text, -1, PREG_SPLIT_DELIM_CAPTURE );
                $s = array_shift( $bits );
@@ -1317,9 +1422,9 @@ class Parser
                        # removeHTMLtags()) should not be included in
                        # URLs, per RFC 2396.
                        $m2 = array();
-                       if (preg_match('/&(lt|gt);/', $url, $m2, PREG_OFFSET_CAPTURE)) {
-                               $text = substr($url, $m2[0][1]) . ' ' . $text;
-                               $url = substr($url, 0, $m2[0][1]);
+                       if ( preg_match( '/&(lt|gt);/', $url, $m2, PREG_OFFSET_CAPTURE ) ) {
+                               $text = substr( $url, $m2[0][1] ) . ' ' . $text;
+                               $url = substr( $url, 0, $m2[0][1] );
                        }
 
                        # If the link text is an image URL, replace it with an <img> tag
@@ -1332,12 +1437,12 @@ class Parser
                        $dtrail = '';
 
                        # Set linktype for CSS - if URL==text, link is essentially free
-                       $linktype = ($text === $url) ? 'free' : 'text';
+                       $linktype = ( $text === $url ) ? 'free' : 'text';
 
                        # No link text, e.g. [http://domain.tld/some.link]
                        if ( $text == '' ) {
                                # Autonumber if allowed. See bug #5918
-                               if ( strpos( wfUrlProtocols(), substr($protocol, 0, strpos($protocol, ':')) ) !== false ) {
+                               if ( strpos( wfUrlProtocols(), substr( $protocol, 0, strpos( $protocol, ':' ) ) ) !== false ) {
                                        $langObj = $this->getFunctionLang();
                                        $text = '[' . $langObj->formatNum( ++$this->mAutonumber ) . ']';
                                        $linktype = 'autonumber';
@@ -1352,13 +1457,13 @@ class Parser
                                list( $dtrail, $trail ) = Linker::splitTrail( $trail );
                        }
 
-                       $text = $wgContLang->markNoConversion($text);
+                       $text = $wgContLang->markNoConversion( $text );
 
                        $url = Sanitizer::cleanUrl( $url );
 
                        # Use the encoded URL
                        # This means that users can paste URLs directly into the text
-                       # Funny characters like &ouml; aren't valid in URLs anyway
+                       # Funny characters like Ã¶ aren't valid in URLs anyway
                        # This was changed in August 2004
                        $s .= $sk->makeExternalLink( $url, $text, false, $linktype,
                                $this->getExternalLinkAttribs( $url ) ) . $dtrail . $trail;
@@ -1380,15 +1485,15 @@ class Parser
         * (depending on configuration, namespace, and the URL's domain) and/or a
         * target attribute (depending on configuration).
         *
-        * @param string $url Optional URL, to extract the domain from for rel =>
+        * @param $url String: optional URL, to extract the domain from for rel =>
         *   nofollow if appropriate
-        * @return array Associative array of HTML attributes
+        * @return Array: associative array of HTML attributes
         */
        function getExternalLinkAttribs( $url = false ) {
                $attribs = array();
                global $wgNoFollowLinks, $wgNoFollowNsExceptions;
                $ns = $this->mTitle->getNamespace();
-               if( $wgNoFollowLinks && !in_array($ns, $wgNoFollowNsExceptions) ) {
+               if ( $wgNoFollowLinks && !in_array( $ns, $wgNoFollowNsExceptions ) ) {
                        $attribs['rel'] = 'nofollow';
 
                        global $wgNoFollowDomainExceptions;
@@ -1396,8 +1501,7 @@ class Parser
                                $bits = wfParseUrl( $url );
                                if ( is_array( $bits ) && isset( $bits['host'] ) ) {
                                        foreach ( $wgNoFollowDomainExceptions as $domain ) {
-                                               if( substr( $bits['host'], -strlen( $domain ) )
-                                               == $domain ) {
+                                               if ( substr( $bits['host'], -strlen( $domain ) ) == $domain ) {
                                                        unset( $attribs['rel'] );
                                                        break;
                                                }
@@ -1414,9 +1518,10 @@ class Parser
 
        /**
         * Replace unusual URL escape codes with their equivalent characters
-        * @param string
-        * @return string
-        * @static
+        *
+        * @param $url String
+        * @return String
+        *
         * @todo  This can merge genuinely required bits in the path or query string,
         *        breaking legit URLs. A proper fix would treat the various parts of
         *        the URL differently; as a workaround, just use the output for
@@ -1430,18 +1535,16 @@ class Parser
        /**
         * Callback function used in replaceUnusualEscapes().
         * Replaces unusual URL escape codes with their equivalent character
-        * @static
-        * @private
         */
        private static function replaceUnusualEscapesCallback( $matches ) {
                $char = urldecode( $matches[0] );
                $ord = ord( $char );
-               // Is it an unsafe or HTTP reserved character according to RFC 1738?
+               # Is it an unsafe or HTTP reserved character according to RFC 1738?
                if ( $ord > 32 && $ord < 127 && strpos( '<>"#{}|\^~[]`;/?', $char ) === false ) {
-                       // No, shouldn't be escaped
+                       # No, shouldn't be escaped
                        return $char;
                } else {
-                       // Yes, leave it escaped
+                       # Yes, leave it escaped
                        return $matches[0];
                }
        }
@@ -1452,39 +1555,40 @@ class Parser
         * @private
         */
        function maybeMakeExternalImage( $url ) {
-               $sk = $this->mOptions->getSkin();
+               $sk = $this->mOptions->getSkin( $this->mTitle );
                $imagesfrom = $this->mOptions->getAllowExternalImagesFrom();
-               $imagesexception = !empty($imagesfrom);
+               $imagesexception = !empty( $imagesfrom );
                $text = false;
                # $imagesfrom could be either a single string or an array of strings, parse out the latter
-               if( $imagesexception && is_array( $imagesfrom ) ) {
+               if ( $imagesexception && is_array( $imagesfrom ) ) {
                        $imagematch = false;
-                       foreach( $imagesfrom as $match ) {
-                               if( strpos( $url, $match ) === 0 ) {
+                       foreach ( $imagesfrom as $match ) {
+                               if ( strpos( $url, $match ) === 0 ) {
                                        $imagematch = true;
                                        break;
                                }
                        }
-               } elseif( $imagesexception ) {
-                       $imagematch = (strpos( $url, $imagesfrom ) === 0);
+               } elseif ( $imagesexception ) {
+                       $imagematch = ( strpos( $url, $imagesfrom ) === 0 );
                } else {
                        $imagematch = false;
                }
                if ( $this->mOptions->getAllowExternalImages()
-                    || ( $imagesexception && $imagematch ) ) {
+                        || ( $imagesexception && $imagematch ) ) {
                        if ( preg_match( self::EXT_IMAGE_REGEX, $url ) ) {
                                # Image found
                                $text = $sk->makeExternalImage( $url );
                        }
                }
-               if( !$text && $this->mOptions->getEnableImageWhitelist()
+               if ( !$text && $this->mOptions->getEnableImageWhitelist()
                         && preg_match( self::EXT_IMAGE_REGEX, $url ) ) {
                        $whitelist = explode( "\n", wfMsgForContent( 'external_image_whitelist' ) );
-                       foreach( $whitelist as $entry ) {
+                       foreach ( $whitelist as $entry ) {
                                # Sanitize the regex fragment, make it case-insensitive, ignore blank entries/comments
-                               if( strpos( $entry, '#' ) === 0 || $entry === '' )
+                               if ( strpos( $entry, '#' ) === 0 || $entry === '' ) {
                                        continue;
-                               if( preg_match( '/' . str_replace( '/', '\\/', $entry ) . '/i', $url ) ) {
+                               }
+                               if ( preg_match( '/' . str_replace( '/', '\\/', $entry ) . '/i', $url ) ) {
                                        # Image matches a whitelist entry
                                        $text = $sk->makeExternalImage( $url );
                                        break;
@@ -1496,7 +1600,7 @@ class Parser
 
        /**
         * Process [[ ]] wikilinks
-        * @return processed text
+        * @return String: processed text
         *
         * @private
         */
@@ -1522,17 +1626,17 @@ class Parser
                if ( !$tc ) {
                        $tc = Title::legalChars() . '#%';
                        # Match a link having the form [[namespace:link|alternate]]trail
-                       $e1 = "/^([{$tc}]*)(\\|.*?)?]](.*)\$/sD";
+                       $e1 = "/^([{$tc}]+)(?:\\|(.+?))?]](.*)\$/sD";
                        # Match cases where there is no "]]", which might still be images
                        $e1_img = "/^([{$tc}]+)\\|(.*)\$/sD";
                }
 
-               $sk = $this->mOptions->getSkin();
+               $sk = $this->mOptions->getSkin( $this->mTitle );
                $holders = new LinkHolderArray( $this );
 
-               #split the entire text string on occurences of [[
+               # split the entire text string on occurences of [[
                $a = StringUtils::explode( '[[', ' ' . $s );
-               #get the first element (all text up to first [[), and remove the space we added
+               # get the first element (all text up to first [[), and remove the space we added
                $s = $a->current();
                $a->next();
                $line = $a->current(); # Workaround for broken ArrayIterator::next() that returns "void"
@@ -1546,7 +1650,7 @@ class Parser
                        $e2 = wfMsgForContent( 'linkprefix' );
                }
 
-               if( is_null( $this->mTitle ) ) {
+               if ( is_null( $this->mTitle ) ) {
                        wfProfileOut( __METHOD__.'-setup' );
                        wfProfileOut( __METHOD__ );
                        throw new MWException( __METHOD__.": \$this->mTitle is null\n" );
@@ -1564,10 +1668,10 @@ class Parser
                        $prefix = '';
                }
 
-               if($wgContLang->hasVariants()) {
-                       $selflink = $wgContLang->convertLinkToAllVariants($this->mTitle->getPrefixedText());
+               if ( $wgContLang->hasVariants() ) {
+                       $selflink = $wgContLang->autoConvertToAllVariants( $this->mTitle->getPrefixedText() );
                } else {
-                       $selflink = array($this->mTitle->getPrefixedText());
+                       $selflink = array( $this->mTitle->getPrefixedText() );
                }
                $useSubpages = $this->areSubpagesAllowed();
                wfProfileOut( __METHOD__.'-setup' );
@@ -1591,7 +1695,7 @@ class Parser
                                        $prefix='';
                                }
                                # first link
-                               if($first_prefix) {
+                               if ( $first_prefix ) {
                                        $prefix = $first_prefix;
                                        $first_prefix = false;
                                }
@@ -1602,15 +1706,7 @@ class Parser
 
                        wfProfileIn( __METHOD__."-e1" );
                        if ( preg_match( $e1, $line, $m ) ) { # page with normal text or alt
-
-                               if( $m[2] === '' ) {
-                                       $text = '';
-                               } elseif( $m[2] === '|' ) { 
-                                       $text = $this->getPipeTrickText( $m[1] );
-                               } else {
-                                       $text = substr( $m[2], 1 );
-                               }
-
+                               $text = $m[2];
                                # If we get a ] at the beginning of $m[3] that means we have a link that's something like:
                                # [[Image:Foo.jpg|[http://example.com desc]]] <- having three ] in a row fucks up,
                                # the real problem is with the $e1 regex
@@ -1619,28 +1715,26 @@ class Parser
                                # Still some problems for cases where the ] is meant to be outside punctuation,
                                # and no image is in sight. See bug 2095.
                                #
-                               if( $text !== '' &&
+                               if ( $text !== '' &&
                                        substr( $m[3], 0, 1 ) === ']' &&
-                                       strpos($text, '[') !== false
+                                       strpos( $text, '[' ) !== false
                                )
                                {
                                        $text .= ']'; # so that replaceExternalLinks($text) works later
                                        $m[3] = substr( $m[3], 1 );
                                }
-
-                               # Handle pipe-trick for [[|<blah>]]
-                               $lnk = $m[1] === '' ? $this->getPipeTrickLink( $text ) : $m[1];
                                # fix up urlencoded title texts
-                               if( strpos( $lnk, '%' ) !== false ) {
+                               if ( strpos( $m[1], '%' ) !== false ) {
                                        # Should anchors '#' also be rejected?
-                                       $lnk = str_replace( array('<', '>'), array('&lt;', '&gt;'), urldecode($lnk) );
+                                       $m[1] = str_replace( array('<', '>'), array('&lt;', '&gt;'), rawurldecode( $m[1] ) );
                                }
-
                                $trail = $m[3];
-                       } elseif( preg_match($e1_img, $line, $m) ) { # Invalid, but might be an image with a link in its caption
+                       } elseif ( preg_match( $e1_img, $line, $m ) ) { # Invalid, but might be an image with a link in its caption
                                $might_be_img = true;
                                $text = $m[2];
-                               $lnk = strpos( $m[1], '%' ) === false ? $m[1] : urldecode( $m[1] );
+                               if ( strpos( $m[1], '%' ) !== false ) {
+                                       $m[1] = rawurldecode( $m[1] );
+                               }
                                $trail = "";
                        } else { # Invalid form; output directly
                                $s .= $prefix . '[[' . $line ;
@@ -1653,7 +1747,7 @@ class Parser
                        # Don't allow internal links to pages containing
                        # PROTO: where PROTO is a valid URL protocol; these
                        # should be external links.
-                       if ( preg_match( '/^\b(?:' . wfUrlProtocols() . ')/', $lnk ) ) {
+                       if ( preg_match( '/^\b(?:' . wfUrlProtocols() . ')/', $m[1] ) ) {
                                $s .= $prefix . '[[' . $line ;
                                wfProfileOut( __METHOD__."-misc" );
                                continue;
@@ -1661,13 +1755,13 @@ class Parser
 
                        # Make subpage if necessary
                        if ( $useSubpages ) {
-                               $link = $this->maybeDoSubpageLink( $lnk, $text );
+                               $link = $this->maybeDoSubpageLink( $m[1], $text );
                        } else {
-                               $link = $lnk;
+                               $link = $m[1];
                        }
 
-                       $noforce = (substr( $lnk, 0, 1 ) !== ':');
-                       if (!$noforce) {
+                       $noforce = ( substr( $m[1], 0, 1 ) !== ':' );
+                       if ( !$noforce ) {
                                # Strip off leading ':'
                                $link = substr( $link, 1 );
                        }
@@ -1687,10 +1781,10 @@ class Parser
 
                        if ( $might_be_img ) { # if this is actually an invalid link
                                wfProfileIn( __METHOD__."-might_be_img" );
-                               if ( $ns == NS_FILE && $noforce ) { #but might be an image
+                               if ( $ns == NS_FILE && $noforce ) { # but might be an image
                                        $found = false;
                                        while ( true ) {
-                                               #look at the next 'line' to see if we can close it there
+                                               # look at the next 'line' to see if we can close it there
                                                $a->next();
                                                $next_line = $a->current();
                                                if ( $next_line === false || $next_line === null ) {
@@ -1704,24 +1798,24 @@ class Parser
                                                        $trail = $m[2];
                                                        break;
                                                } elseif ( count( $m ) == 2 ) {
-                                                       #if there's exactly one ]] that's fine, we'll keep looking
+                                                       # if there's exactly one ]] that's fine, we'll keep looking
                                                        $text .= "[[{$m[0]}]]{$m[1]}";
                                                } else {
-                                                       #if $next_line is invalid too, we need look no further
+                                                       # if $next_line is invalid too, we need look no further
                                                        $text .= '[[' . $next_line;
                                                        break;
                                                }
                                        }
                                        if ( !$found ) {
                                                # we couldn't find the end of this imageLink, so output it raw
-                                               #but don't ignore what might be perfectly normal links in the text we've examined
+                                               # but don't ignore what might be perfectly normal links in the text we've examined
                                                $holders->merge( $this->replaceInternalLinks2( $text ) );
                                                $s .= "{$prefix}[[$link|$text";
                                                # note: no $trail, because without an end, there *is* no trail
                                                wfProfileOut( __METHOD__."-might_be_img" );
                                                continue;
                                        }
-                               } else { #it's not an image, so output it raw
+                               } else { # it's not an image, so output it raw
                                        $s .= "{$prefix}[[$link|$text";
                                        # note: no $trail, because without an end, there *is* no trail
                                        wfProfileOut( __METHOD__."-might_be_img" );
@@ -1731,7 +1825,15 @@ class Parser
                        }
 
                        $wasblank = ( $text  == '' );
-                       if ( $wasblank ) $text = $link;
+                       if ( $wasblank ) {
+                               $text = $link;
+                       } else {
+                               # Bug 4598 madness. Handle the quotes only if they come from the alternate part
+                               # [[Lista d''e paise d''o munno]] -> <a href="...">Lista d''e paise d''o munno</a>
+                               # [[Criticism of Harry Potter|Criticism of ''Harry Potter'']]
+                               #    -> <a href="Criticism of Harry Potter">Criticism of <i>Harry Potter</i></a>
+                               $text = $this->doQuotes( $text );
+                       }
 
                        # Link not escaped by : , create the various objects
                        if ( $noforce ) {
@@ -1740,8 +1842,8 @@ class Parser
                                wfProfileIn( __METHOD__."-interwiki" );
                                if ( $iw && $this->mOptions->getInterwikiMagic() && $nottalk && $wgContLang->getLanguageName( $iw ) ) {
                                        $this->mOutput->addLanguageLink( $nt->getFullText() );
-                                       $s = rtrim($s . $prefix);
-                                       $s .= trim($trail, "\n") == '' ? '': $prefix . $trail;
+                                       $s = rtrim( $s . $prefix );
+                                       $s .= trim( $trail, "\n" ) == '' ? '': $prefix . $trail;
                                        wfProfileOut( __METHOD__."-interwiki" );
                                        continue;
                                }
@@ -1760,7 +1862,7 @@ class Parser
                                                        # recursively parse links inside the image caption
                                                        # actually, this will parse them in any other parameters, too,
                                                        # but it might be hard to fix that, and it doesn't matter ATM
-                                                       $text = $this->replaceExternalLinks($text);
+                                                       $text = $this->replaceExternalLinks( $text );
                                                        $holders->merge( $this->replaceInternalLinks2( $text ) );
                                                }
                                                # cloak any absolute URLs inside the image markup, so replaceExternalLinks() won't touch them
@@ -1776,7 +1878,7 @@ class Parser
 
                                if ( $ns == NS_CATEGORY ) {
                                        wfProfileIn( __METHOD__."-category" );
-                                       $s = rtrim($s . "\n"); # bug 87
+                                       $s = rtrim( $s . "\n" ); # bug 87
 
                                        if ( $wasblank ) {
                                                $sortkey = $this->getDefaultSort();
@@ -1792,7 +1894,7 @@ class Parser
                                         * Strip the whitespace Category links produce, see bug 87
                                         * @todo We might want to use trim($tmp, "\n") here.
                                         */
-                                       $s .= trim($prefix . $trail, "\n") == '' ? '': $prefix . $trail;
+                                       $s .= trim( $prefix . $trail, "\n" ) == '' ? '' : $prefix . $trail;
 
                                        wfProfileOut( __METHOD__."-category" );
                                        continue;
@@ -1800,8 +1902,8 @@ class Parser
                        }
 
                        # Self-link checking
-                       if( $nt->getFragment() === '' && $ns != NS_SPECIAL ) {
-                               if( in_array( $nt->getPrefixedText(), $selflink, true ) ) {
+                       if ( $nt->getFragment() === '' && $ns != NS_SPECIAL ) {
+                               if ( in_array( $nt->getPrefixedText(), $selflink, true ) ) {
                                        $s .= $prefix . $sk->makeSelfLinkObj( $nt, $text, '', $trail );
                                        continue;
                                }
@@ -1809,7 +1911,7 @@ class Parser
 
                        # NS_MEDIA is a pseudo-namespace for linking directly to a file
                        # FIXME: Should do batch file existence checks, see comment below
-                       if( $ns == NS_MEDIA ) {
+                       if ( $ns == NS_MEDIA ) {
                                wfProfileIn( __METHOD__."-media" );
                                # Give extensions a chance to select the file revision for us
                                $skip = $time = false;
@@ -1832,7 +1934,7 @@ class Parser
                        #
                        # FIXME: isAlwaysKnown() can be expensive for file links; we should really do
                        # batch file existence checks for NS_FILE and NS_MEDIA
-                       if( $iw == '' && $nt->isAlwaysKnown() ) {
+                       if ( $iw == '' && $nt->isAlwaysKnown() ) {
                                $this->mOutput->addLink( $nt );
                                $s .= $this->makeKnownLinkHolder( $nt, $text, '', $trail, $prefix );
                        } else {
@@ -1864,17 +1966,17 @@ class Parser
         * breaking URLs in the following text without breaking trails on the
         * wiki links, it's been made into a horrible function.
         *
-        * @param Title $nt
-        * @param string $text
-        * @param string $query
-        * @param string $trail
-        * @param string $prefix
-        * @return string HTML-wikitext mix oh yuck
+        * @param $nt Title
+        * @param $text String
+        * @param $query String
+        * @param $trail String
+        * @param $prefix String
+        * @return String: HTML-wikitext mix oh yuck
         */
        function makeKnownLinkHolder( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
                list( $inside, $trail ) = Linker::splitTrail( $trail );
-               $sk = $this->mOptions->getSkin();
-               // FIXME: use link() instead of deprecated makeKnownLinkObj()
+               $sk = $this->mOptions->getSkin( $this->mTitle );
+               # FIXME: use link() instead of deprecated makeKnownLinkObj()
                $link = $sk->makeKnownLinkObj( $nt, $text, $query, $inside, $prefix );
                return $this->armorLinks( $link ) . $trail;
        }
@@ -1886,8 +1988,8 @@ class Parser
         * Not needed quite as much as it used to be since free links are a bit
         * more sensible these days. But bracketed links are still an issue.
         *
-        * @param string more-or-less HTML
-        * @return string less-or-more HTML with NOPARSE bits
+        * @param $text String: more-or-less HTML
+        * @return String: less-or-more HTML with NOPARSE bits
         */
        function armorLinks( $text ) {
                return preg_replace( '/\b(' . wfUrlProtocols() . ')/',
@@ -1896,7 +1998,7 @@ class Parser
 
        /**
         * Return true if subpage links should be expanded on this page.
-        * @return bool
+        * @return Boolean
         */
        function areSubpagesAllowed() {
                # Some namespaces don't allow subpages
@@ -1905,39 +2007,21 @@ class Parser
 
        /**
         * Handle link to subpage if necessary
-        * @param string $target the source of the link
-        * @param string &$text the link text, modified as necessary
+        *
+        * @param $target String: the source of the link
+        * @param &$text String: the link text, modified as necessary
         * @return string the full name of the link
         * @private
         */
-       function maybeDoSubpageLink($target, &$text) {
+       function maybeDoSubpageLink( $target, &$text ) {
                return Linker::normalizeSubpageLink( $this->mTitle, $target, $text );
        }
 
-       /**
-        * From the [[title|]] return link-text as though the used typed [[title|link-text]]
-        * @param string $link from [[$link|]]
-        * @return string $text for [[$link|$text]]
-        */
-       function getPipeTrickText( $link ) {
-               return Linker::getPipeTrickText( $link );
-       }
-
-       /**
-        * From the [[|link-text]] return the title as though the user typed [[title|link-text]]
-        * @param string $text from [[|$text]]
-        * @param Title $title to resolve the link against
-        * @return string $link for [[$link|$text]]
-        */
-       function getPipeTrickLink( $text ) {
-               return Linker::getPipeTrickLink( $text, $this->mTitle );
-       }
-
        /**#@+
         * Used by doBlockLevels()
         * @private
         */
-       /* private */ function closeParagraph() {
+       function closeParagraph() {
                $result = '';
                if ( $this->mLastSection != '' ) {
                        $result = '</' . $this->mLastSection  . ">\n";
@@ -1946,42 +2030,64 @@ class Parser
                $this->mLastSection = '';
                return $result;
        }
-       # getCommon() returns the length of the longest common substring
-       # of both arguments, starting at the beginning of both.
-       #
-       /* private */ function getCommon( $st1, $st2 ) {
+
+       /**
+        * getCommon() returns the length of the longest common substring
+        * of both arguments, starting at the beginning of both.
+        * @private
+        */
+       function getCommon( $st1, $st2 ) {
                $fl = strlen( $st1 );
                $shorter = strlen( $st2 );
-               if ( $fl < $shorter ) { $shorter = $fl; }
+               if ( $fl < $shorter ) {
+                       $shorter = $fl;
+               }
 
                for ( $i = 0; $i < $shorter; ++$i ) {
-                       if ( $st1{$i} != $st2{$i} ) { break; }
+                       if ( $st1{$i} != $st2{$i} ) {
+                               break;
+                       }
                }
                return $i;
        }
-       # These next three functions open, continue, and close the list
-       # element appropriate to the prefix character passed into them.
-       #
-       /* private */ function openList( $char ) {
+
+       /**
+        * These next three functions open, continue, and close the list
+        * element appropriate to the prefix character passed into them.
+        * @private
+        */
+       function openList( $char ) {
                $result = $this->closeParagraph();
 
-               if ( '*' === $char ) { $result .= '<ul><li>'; }
-               elseif ( '#' === $char ) { $result .= '<ol><li>'; }
-               elseif ( ':' === $char ) { $result .= '<dl><dd>'; }
-               elseif ( ';' === $char ) {
+               if ( '*' === $char ) {
+                       $result .= '<ul><li>';
+               } elseif ( '#' === $char ) {
+                       $result .= '<ol><li>';
+               } elseif ( ':' === $char ) {
+                       $result .= '<dl><dd>';
+               } elseif ( ';' === $char ) {
                        $result .= '<dl><dt>';
                        $this->mDTopen = true;
+               } else {
+                       $result = '<!-- ERR 1 -->';
                }
-               else { $result = '<!-- ERR 1 -->'; }
 
                return $result;
        }
 
-       /* private */ function nextItem( $char ) {
-               if ( '*' === $char || '#' === $char ) { return '</li><li>'; }
-               elseif ( ':' === $char || ';' === $char ) {
+       /**
+        * TODO: document
+        * @param $char String
+        * @private
+        */
+       function nextItem( $char ) {
+               if ( '*' === $char || '#' === $char ) {
+                       return '</li><li>';
+               } elseif ( ':' === $char || ';' === $char ) {
                        $close = '</dd>';
-                       if ( $this->mDTopen ) { $close = '</dt>'; }
+                       if ( $this->mDTopen ) {
+                               $close = '</dt>';
+                       }
                        if ( ';' === $char ) {
                                $this->mDTopen = true;
                                return $close . '<dt>';
@@ -1993,18 +2099,26 @@ class Parser
                return '<!-- ERR 2 -->';
        }
 
-       /* private */ function closeList( $char ) {
-               if ( '*' === $char ) { $text = '</li></ul>'; }
-               elseif ( '#' === $char ) { $text = '</li></ol>'; }
-               elseif ( ':' === $char ) {
+       /**
+        * TODO: document
+        * @param $char String
+        * @private
+        */
+       function closeList( $char ) {
+               if ( '*' === $char ) {
+                       $text = '</li></ul>';
+               } elseif ( '#' === $char ) {
+                       $text = '</li></ol>';
+               } elseif ( ':' === $char ) {
                        if ( $this->mDTopen ) {
                                $this->mDTopen = false;
                                $text = '</dt></dl>';
                        } else {
                                $text = '</dd></dl>';
                        }
+               } else {
+                       return '<!-- ERR 3 -->';
                }
-               else {  return '<!-- ERR 3 -->'; }
                return $text."\n";
        }
        /**#@-*/
@@ -2012,7 +2126,8 @@ class Parser
        /**
         * Make lists from lines starting with ':', '*', '#', etc. (DBL)
         *
-        * @param $linestart bool whether or not this is at the start of a line.
+        * @param $text String
+        * @param $linestart Boolean: whether or not this is at the start of a line.
         * @private
         * @return string the lists rendered as HTML
         */
@@ -2037,24 +2152,24 @@ class Parser
                                $linestart = true;
                                continue;
                        }
-                       // * = ul
-                       // # = ol
-                       // ; = dt
-                       // : = dd
+                       # * = ul
+                       # # = ol
+                       # ; = dt
+                       # : = dd
 
                        $lastPrefixLength = strlen( $lastPrefix );
-                       $preCloseMatch = preg_match('/<\\/pre/i', $oLine );
-                       $preOpenMatch = preg_match('/<pre/i', $oLine );
-                       // If not in a <pre> element, scan for and figure out what prefixes are there.
+                       $preCloseMatch = preg_match( '/<\\/pre/i', $oLine );
+                       $preOpenMatch = preg_match( '/<pre/i', $oLine );
+                       # If not in a <pre> element, scan for and figure out what prefixes are there.
                        if ( !$this->mInPre ) {
                                # Multiple prefixes may abut each other for nested lists.
                                $prefixLength = strspn( $oLine, '*#:;' );
                                $prefix = substr( $oLine, 0, $prefixLength );
 
                                # eh?
-                               // ; and : are both from definition-lists, so they're equivalent
-                               //  for the purposes of determining whether or not we need to open/close
-                               //  elements.
+                               # ; and : are both from definition-lists, so they're equivalent
+                               #  for the purposes of determining whether or not we need to open/close
+                               #  elements.
                                $prefix2 = str_replace( ';', ':', $prefix );
                                $t = substr( $oLine, $prefixLength );
                                $this->mInPre = (bool)$preOpenMatch;
@@ -2066,7 +2181,7 @@ class Parser
                        }
 
                        # List generation
-                       if( $prefixLength && $lastPrefix === $prefix2 ) {
+                       if ( $prefixLength && $lastPrefix === $prefix2 ) {
                                # Same as the last item, so no need to deal with nesting or opening stuff
                                $output .= $this->nextItem( substr( $prefix, -1 ) );
                                $paragraphStack = false;
@@ -2077,37 +2192,37 @@ class Parser
                                        # So we check for : in the remainder text to split up the
                                        # title and definition, without b0rking links.
                                        $term = $t2 = '';
-                                       if ($this->findColonNoLinks($t, $term, $t2) !== false) {
+                                       if ( $this->findColonNoLinks( $t, $term, $t2 ) !== false ) {
                                                $t = $t2;
                                                $output .= $term . $this->nextItem( ':' );
                                        }
                                }
-                       } elseif( $prefixLength || $lastPrefixLength ) {
-                               // We need to open or close prefixes, or both.
+                       } elseif ( $prefixLength || $lastPrefixLength ) {
+                               # We need to open or close prefixes, or both.
 
                                # Either open or close a level...
                                $commonPrefixLength = $this->getCommon( $prefix, $lastPrefix );
                                $paragraphStack = false;
 
-                               // Close all the prefixes which aren't shared.
-                               while( $commonPrefixLength < $lastPrefixLength ) {
+                               # Close all the prefixes which aren't shared.
+                               while ( $commonPrefixLength < $lastPrefixLength ) {
                                        $output .= $this->closeList( $lastPrefix[$lastPrefixLength-1] );
                                        --$lastPrefixLength;
                                }
 
-                               // Continue the current prefix if appropriate.
+                               # Continue the current prefix if appropriate.
                                if ( $prefixLength <= $commonPrefixLength && $commonPrefixLength > 0 ) {
                                        $output .= $this->nextItem( $prefix[$commonPrefixLength-1] );
                                }
 
-                               // Open prefixes where appropriate.
+                               # Open prefixes where appropriate.
                                while ( $prefixLength > $commonPrefixLength ) {
                                        $char = substr( $prefix, $commonPrefixLength, 1 );
                                        $output .= $this->openList( $char );
 
                                        if ( ';' === $char ) {
                                                # FIXME: This is dupe of code above
-                                               if ($this->findColonNoLinks($t, $term, $t2) !== false) {
+                                               if ( $this->findColonNoLinks( $t, $term, $t2 ) !== false ) {
                                                        $t = $t2;
                                                        $output .= $term . $this->nextItem( ':' );
                                                }
@@ -2117,11 +2232,11 @@ class Parser
                                $lastPrefix = $prefix2;
                        }
 
-                       // If we have no prefixes, go to paragraph mode.
-                       if( 0 == $prefixLength ) {
+                       # If we have no prefixes, go to paragraph mode.
+                       if ( 0 == $prefixLength ) {
                                wfProfileIn( __METHOD__."-paragraph" );
                                # No prefix (not in list)--go to paragraph mode
-                               // XXX: use a stack for nestable elements like span, table and div
+                               # XXX: use a stack for nestable elements like span, table and div
                                $openmatch = preg_match('/(?:<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6|<pre|<tr|<p|<ul|<ol|<li|<\\/tr|<\\/td|<\\/th)/iS', $t );
                                $closematch = preg_match(
                                        '/(?:<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6|'.
@@ -2133,29 +2248,25 @@ class Parser
                                        if ( $preOpenMatch and !$preCloseMatch ) {
                                                $this->mInPre = true;
                                        }
-                                       if ( $closematch ) {
-                                               $inBlockElem = false;
-                                       } else {
-                                               $inBlockElem = true;
-                                       }
-                               } else if ( !$inBlockElem && !$this->mInPre ) {
-                                       if ( ' ' == substr( $t, 0, 1 ) and ( $this->mLastSection === 'pre' or trim($t) != '' ) ) {
-                                               // pre
-                                               if ($this->mLastSection !== 'pre') {
+                                       $inBlockElem = !$closematch;
+                               } elseif ( !$inBlockElem && !$this->mInPre ) {
+                                       if ( ' ' == substr( $t, 0, 1 ) and ( $this->mLastSection === 'pre' || trim( $t ) != '' ) ) {
+                                               # pre
+                                               if ( $this->mLastSection !== 'pre' ) {
                                                        $paragraphStack = false;
                                                        $output .= $this->closeParagraph().'<pre>';
                                                        $this->mLastSection = 'pre';
                                                }
                                                $t = substr( $t, 1 );
                                        } else {
-                                               // paragraph
-                                               if ( trim($t) == '' ) {
+                                               # paragraph
+                                               if ( trim( $t ) === '' ) {
                                                        if ( $paragraphStack ) {
                                                                $output .= $paragraphStack.'<br />';
                                                                $paragraphStack = false;
                                                                $this->mLastSection = 'p';
                                                        } else {
-                                                               if ($this->mLastSection !== 'p' ) {
+                                                               if ( $this->mLastSection !== 'p' ) {
                                                                        $output .= $this->closeParagraph();
                                                                        $this->mLastSection = '';
                                                                        $paragraphStack = '<p>';
@@ -2168,7 +2279,7 @@ class Parser
                                                                $output .= $paragraphStack;
                                                                $paragraphStack = false;
                                                                $this->mLastSection = 'p';
-                                                       } else if ($this->mLastSection !== 'p') {
+                                                       } elseif ( $this->mLastSection !== 'p' ) {
                                                                $output .= $this->closeParagraph().'<p>';
                                                                $this->mLastSection = 'p';
                                                        }
@@ -2177,11 +2288,11 @@ class Parser
                                }
                                wfProfileOut( __METHOD__."-paragraph" );
                        }
-                       // somewhere above we forget to get out of pre block (bug 785)
-                       if($preCloseMatch && $this->mInPre) {
+                       # somewhere above we forget to get out of pre block (bug 785)
+                       if ( $preCloseMatch && $this->mInPre ) {
                                $this->mInPre = false;
                        }
-                       if ($paragraphStack === false) {
+                       if ( $paragraphStack === false ) {
                                $output .= $t."\n";
                        }
                }
@@ -2201,31 +2312,32 @@ class Parser
        /**
         * Split up a string on ':', ignoring any occurences inside tags
         * to prevent illegal overlapping.
-        * @param string $str the string to split
-        * @param string &$before set to everything before the ':'
-        * @param string &$after set to everything after the ':'
-        * return string the position of the ':', or false if none found
+        *
+        * @param $str String: the string to split
+        * @param &$before String: set to everything before the ':'
+        * @param &$after String: set to everything after the ':'
+        * return String: the position of the ':', or false if none found
         */
-       function findColonNoLinks($str, &$before, &$after) {
+       function findColonNoLinks( $str, &$before, &$after ) {
                wfProfileIn( __METHOD__ );
 
                $pos = strpos( $str, ':' );
-               if( $pos === false ) {
-                       // Nothing to find!
+               if ( $pos === false ) {
+                       # Nothing to find!
                        wfProfileOut( __METHOD__ );
                        return false;
                }
 
                $lt = strpos( $str, '<' );
-               if( $lt === false || $lt > $pos ) {
-                       // Easy; no tag nesting to worry about
+               if ( $lt === false || $lt > $pos ) {
+                       # Easy; no tag nesting to worry about
                        $before = substr( $str, 0, $pos );
                        $after = substr( $str, $pos+1 );
                        wfProfileOut( __METHOD__ );
                        return $pos;
                }
 
-               // Ugly state machine to walk through avoiding tags.
+               # Ugly state machine to walk through avoiding tags.
                $state = self::COLON_STATE_TEXT;
                $stack = 0;
                $len = strlen( $str );
@@ -2233,67 +2345,67 @@ class Parser
                        $c = $str{$i};
 
                        switch( $state ) {
-                       // (Using the number is a performance hack for common cases)
-                       case 0: // self::COLON_STATE_TEXT:
+                       # (Using the number is a performance hack for common cases)
+                       case 0: # self::COLON_STATE_TEXT:
                                switch( $c ) {
                                case "<":
-                                       // Could be either a <start> tag or an </end> tag
+                                       # Could be either a <start> tag or an </end> tag
                                        $state = self::COLON_STATE_TAGSTART;
                                        break;
                                case ":":
-                                       if( $stack == 0 ) {
-                                               // We found it!
+                                       if ( $stack == 0 ) {
+                                               # We found it!
                                                $before = substr( $str, 0, $i );
                                                $after = substr( $str, $i + 1 );
                                                wfProfileOut( __METHOD__ );
                                                return $i;
                                        }
-                                       // Embedded in a tag; don't break it.
+                                       # Embedded in a tag; don't break it.
                                        break;
                                default:
-                                       // Skip ahead looking for something interesting
+                                       # Skip ahead looking for something interesting
                                        $colon = strpos( $str, ':', $i );
-                                       if( $colon === false ) {
-                                               // Nothing else interesting
+                                       if ( $colon === false ) {
+                                               # Nothing else interesting
                                                wfProfileOut( __METHOD__ );
                                                return false;
                                        }
                                        $lt = strpos( $str, '<', $i );
-                                       if( $stack === 0 ) {
-                                               if( $lt === false || $colon < $lt ) {
-                                                       // We found it!
+                                       if ( $stack === 0 ) {
+                                               if ( $lt === false || $colon < $lt ) {
+                                                       # We found it!
                                                        $before = substr( $str, 0, $colon );
                                                        $after = substr( $str, $colon + 1 );
                                                        wfProfileOut( __METHOD__ );
                                                        return $i;
                                                }
                                        }
-                                       if( $lt === false ) {
-                                               // Nothing else interesting to find; abort!
-                                               // We're nested, but there's no close tags left. Abort!
+                                       if ( $lt === false ) {
+                                               # Nothing else interesting to find; abort!
+                                               # We're nested, but there's no close tags left. Abort!
                                                break 2;
                                        }
-                                       // Skip ahead to next tag start
+                                       # Skip ahead to next tag start
                                        $i = $lt;
                                        $state = self::COLON_STATE_TAGSTART;
                                }
                                break;
-                       case 1: // self::COLON_STATE_TAG:
-                               // In a <tag>
+                       case 1: # self::COLON_STATE_TAG:
+                               # In a <tag>
                                switch( $c ) {
                                case ">":
                                        $stack++;
                                        $state = self::COLON_STATE_TEXT;
                                        break;
                                case "/":
-                                       // Slash may be followed by >?
+                                       # Slash may be followed by >?
                                        $state = self::COLON_STATE_TAGSLASH;
                                        break;
                                default:
-                                       // ignore
+                                       # ignore
                                }
                                break;
-                       case 2: // self::COLON_STATE_TAGSTART:
+                       case 2: # self::COLON_STATE_TAGSTART:
                                switch( $c ) {
                                case "/":
                                        $state = self::COLON_STATE_CLOSETAG;
@@ -2302,18 +2414,18 @@ class Parser
                                        $state = self::COLON_STATE_COMMENT;
                                        break;
                                case ">":
-                                       // Illegal early close? This shouldn't happen D:
+                                       # Illegal early close? This shouldn't happen D:
                                        $state = self::COLON_STATE_TEXT;
                                        break;
                                default:
                                        $state = self::COLON_STATE_TAG;
                                }
                                break;
-                       case 3: // self::COLON_STATE_CLOSETAG:
-                               // In a </tag>
-                               if( $c === ">" ) {
+                       case 3: # self::COLON_STATE_CLOSETAG:
+                               # In a </tag>
+                               if ( $c === ">" ) {
                                        $stack--;
-                                       if( $stack < 0 ) {
+                                       if ( $stack < 0 ) {
                                                wfDebug( __METHOD__.": Invalid input; too many close tags\n" );
                                                wfProfileOut( __METHOD__ );
                                                return false;
@@ -2322,28 +2434,28 @@ class Parser
                                }
                                break;
                        case self::COLON_STATE_TAGSLASH:
-                               if( $c === ">" ) {
-                                       // Yes, a self-closed tag <blah/>
+                               if ( $c === ">" ) {
+                                       # Yes, a self-closed tag <blah/>
                                        $state = self::COLON_STATE_TEXT;
                                } else {
-                                       // Probably we're jumping the gun, and this is an attribute
+                                       # Probably we're jumping the gun, and this is an attribute
                                        $state = self::COLON_STATE_TAG;
                                }
                                break;
-                       case 5: // self::COLON_STATE_COMMENT:
-                               if( $c === "-" ) {
+                       case 5: # self::COLON_STATE_COMMENT:
+                               if ( $c === "-" ) {
                                        $state = self::COLON_STATE_COMMENTDASH;
                                }
                                break;
                        case self::COLON_STATE_COMMENTDASH:
-                               if( $c === "-" ) {
+                               if ( $c === "-" ) {
                                        $state = self::COLON_STATE_COMMENTDASHDASH;
                                } else {
                                        $state = self::COLON_STATE_COMMENT;
                                }
                                break;
                        case self::COLON_STATE_COMMENTDASHDASH:
-                               if( $c === ">" ) {
+                               if ( $c === ">" ) {
                                        $state = self::COLON_STATE_TEXT;
                                } else {
                                        $state = self::COLON_STATE_COMMENT;
@@ -2353,7 +2465,7 @@ class Parser
                                throw new MWException( "State machine error in " . __METHOD__ );
                        }
                }
-               if( $stack > 0 ) {
+               if ( $stack > 0 ) {
                        wfDebug( __METHOD__.": Invalid input; not enough close tags (stack $stack, state $state)\n" );
                        return false;
                }
@@ -2367,8 +2479,8 @@ class Parser
         * @private
         */
        function getVariableValue( $index, $frame=false ) {
-               global $wgContLang, $wgSitename, $wgServer, $wgServerName;
-               global $wgScriptPath, $wgStylePath;
+               global $wgContLang, $wgSitename, $wgServer;
+               global $wgArticlePath, $wgScriptPath, $wgStylePath;
 
                /**
                 * Some of these require message or data lookups and can be
@@ -2472,7 +2584,7 @@ class Parser
                                $value = wfUrlEncode( str_replace( ' ', '_', $this->mTitle->getBaseText() ) );
                                break;
                        case 'talkpagename':
-                               if( $this->mTitle->canTalk() ) {
+                               if ( $this->mTitle->canTalk() ) {
                                        $talkPage = $this->mTitle->getTalkPage();
                                        $value = wfEscapeWikiText( $talkPage->getPrefixedText() );
                                } else {
@@ -2480,7 +2592,7 @@ class Parser
                                }
                                break;
                        case 'talkpagenamee':
-                               if( $this->mTitle->canTalk() ) {
+                               if ( $this->mTitle->canTalk() ) {
                                        $talkPage = $this->mTitle->getTalkPage();
                                        $value = $talkPage->getPrefixedUrl();
                                } else {
@@ -2495,71 +2607,70 @@ class Parser
                                $subjPage = $this->mTitle->getSubjectPage();
                                $value = $subjPage->getPrefixedUrl();
                                break;
-                       case 'pipetrick':
-                               $text = $this->mTitle->getText();
-                               $value = $this->getPipeTrickText( $text );
-                               break;
-                       case 'pipetricke':
-                               $text = $this->mTitle->getText();
-                               $value = wfUrlEncode( str_replace( ' ', '_', $this->getPipeTrickText( $text ) ) );
-                               break;
                        case 'revisionid':
-                               // Let the edit saving system know we should parse the page
-                               // *after* a revision ID has been assigned.
+                               # Let the edit saving system know we should parse the page
+                               # *after* a revision ID has been assigned.
                                $this->mOutput->setFlag( 'vary-revision' );
                                wfDebug( __METHOD__ . ": {{REVISIONID}} used, setting vary-revision...\n" );
                                $value = $this->mRevisionId;
                                break;
                        case 'revisionday':
-                               // Let the edit saving system know we should parse the page
-                               // *after* a revision ID has been assigned. This is for null edits.
+                               # Let the edit saving system know we should parse the page
+                               # *after* a revision ID has been assigned. This is for null edits.
                                $this->mOutput->setFlag( 'vary-revision' );
                                wfDebug( __METHOD__ . ": {{REVISIONDAY}} used, setting vary-revision...\n" );
                                $value = intval( substr( $this->getRevisionTimestamp(), 6, 2 ) );
                                break;
                        case 'revisionday2':
-                               // Let the edit saving system know we should parse the page
-                               // *after* a revision ID has been assigned. This is for null edits.
+                               # Let the edit saving system know we should parse the page
+                               # *after* a revision ID has been assigned. This is for null edits.
                                $this->mOutput->setFlag( 'vary-revision' );
                                wfDebug( __METHOD__ . ": {{REVISIONDAY2}} used, setting vary-revision...\n" );
                                $value = substr( $this->getRevisionTimestamp(), 6, 2 );
                                break;
                        case 'revisionmonth':
-                               // Let the edit saving system know we should parse the page
-                               // *after* a revision ID has been assigned. This is for null edits.
+                               # Let the edit saving system know we should parse the page
+                               # *after* a revision ID has been assigned. This is for null edits.
                                $this->mOutput->setFlag( 'vary-revision' );
                                wfDebug( __METHOD__ . ": {{REVISIONMONTH}} used, setting vary-revision...\n" );
+                               $value = substr( $this->getRevisionTimestamp(), 4, 2 );
+                               break;
+                       case 'revisionmonth1':
+                               # Let the edit saving system know we should parse the page
+                               # *after* a revision ID has been assigned. This is for null edits.
+                               $this->mOutput->setFlag( 'vary-revision' );
+                               wfDebug( __METHOD__ . ": {{REVISIONMONTH1}} used, setting vary-revision...\n" );
                                $value = intval( substr( $this->getRevisionTimestamp(), 4, 2 ) );
                                break;
                        case 'revisionyear':
-                               // Let the edit saving system know we should parse the page
-                               // *after* a revision ID has been assigned. This is for null edits.
+                               # Let the edit saving system know we should parse the page
+                               # *after* a revision ID has been assigned. This is for null edits.
                                $this->mOutput->setFlag( 'vary-revision' );
                                wfDebug( __METHOD__ . ": {{REVISIONYEAR}} used, setting vary-revision...\n" );
                                $value = substr( $this->getRevisionTimestamp(), 0, 4 );
                                break;
                        case 'revisiontimestamp':
-                               // Let the edit saving system know we should parse the page
-                               // *after* a revision ID has been assigned. This is for null edits.
+                               # Let the edit saving system know we should parse the page
+                               # *after* a revision ID has been assigned. This is for null edits.
                                $this->mOutput->setFlag( 'vary-revision' );
                                wfDebug( __METHOD__ . ": {{REVISIONTIMESTAMP}} used, setting vary-revision...\n" );
                                $value = $this->getRevisionTimestamp();
                                break;
                        case 'revisionuser':
-                                // Let the edit saving system know we should parse the page
-                                // *after* a revision ID has been assigned. This is for null edits.
+                               # Let the edit saving system know we should parse the page
+                               # *after* a revision ID has been assigned. This is for null edits.
                                $this->mOutput->setFlag( 'vary-revision' );
                                wfDebug( __METHOD__ . ": {{REVISIONUSER}} used, setting vary-revision...\n" );
                                $value = $this->getRevisionUser();
                                break;
                        case 'namespace':
-                               $value = str_replace('_',' ',$wgContLang->getNsText( $this->mTitle->getNamespace() ) );
+                               $value = str_replace( '_',' ',$wgContLang->getNsText( $this->mTitle->getNamespace() ) );
                                break;
                        case 'namespacee':
                                $value = wfUrlencode( $wgContLang->getNsText( $this->mTitle->getNamespace() ) );
                                break;
                        case 'talkspace':
-                               $value = $this->mTitle->canTalk() ? str_replace('_',' ',$this->mTitle->getTalkNsText()) : '';
+                               $value = $this->mTitle->canTalk() ? str_replace( '_',' ',$this->mTitle->getTalkNsText() ) : '';
                                break;
                        case 'talkspacee':
                                $value = $this->mTitle->canTalk() ? wfUrlencode( $this->mTitle->getTalkNsText() ) : '';
@@ -2583,8 +2694,8 @@ class Parser
                                $value = $wgContLang->formatNum( gmdate( 'H', $ts ), true );
                                break;
                        case 'currentweek':
-                               // @bug 4594 PHP5 has it zero padded, PHP4 does not, cast to
-                               // int to remove the padding
+                               # @bug 4594 PHP5 has it zero padded, PHP4 does not, cast to
+                               # int to remove the padding
                                $value = $wgContLang->formatNum( (int)gmdate( 'W', $ts ) );
                                break;
                        case 'currentdow':
@@ -2603,8 +2714,8 @@ class Parser
                                $value = $wgContLang->formatNum( $localHour, true );
                                break;
                        case 'localweek':
-                               // @bug 4594 PHP5 has it zero padded, PHP4 does not, cast to
-                               // int to remove the padding
+                               # @bug 4594 PHP5 has it zero padded, PHP4 does not, cast to
+                               # int to remove the padding
                                $value = $wgContLang->formatNum( (int)$localWeek );
                                break;
                        case 'localdow':
@@ -2626,7 +2737,7 @@ class Parser
                                $value = $wgContLang->formatNum( SiteStats::pages() );
                                break;
                        case 'numberofadmins':
-                               $value = $wgContLang->formatNum( SiteStats::numberingroup('sysop') );
+                               $value = $wgContLang->formatNum( SiteStats::numberingroup( 'sysop' ) );
                                break;
                        case 'numberofedits':
                                $value = $wgContLang->formatNum( SiteStats::edits() );
@@ -2643,12 +2754,17 @@ class Parser
                        case 'currentversion':
                                $value = SpecialVersion::getVersion();
                                break;
+                       case 'articlepath':
+                               return $wgArticlePath;
                        case 'sitename':
                                return $wgSitename;
                        case 'server':
                                return $wgServer;
                        case 'servername':
-                               return $wgServerName;
+                               wfSuppressWarnings(); # May give an E_WARNING in PHP < 5.3.3
+                               $serverName = parse_url( $wgServer, PHP_URL_HOST );
+                               wfRestoreWarnings();
+                               return $serverName ? $serverName : $wgServer;
                        case 'scriptpath':
                                return $wgScriptPath;
                        case 'stylepath':
@@ -2656,14 +2772,15 @@ class Parser
                        case 'directionmark':
                                return $wgContLang->getDirMark();
                        case 'contentlanguage':
-                               global $wgContLanguageCode;
-                               return $wgContLanguageCode;
+                               global $wgLanguageCode;
+                               return $wgLanguageCode;
                        default:
                                $ret = null;
-                               if ( wfRunHooks( 'ParserGetVariableValueSwitch', array( &$this, &$this->mVarCache, &$index, &$ret, &$frame ) ) )
+                               if ( wfRunHooks( 'ParserGetVariableValueSwitch', array( &$this, &$this->mVarCache, &$index, &$ret, &$frame ) ) ) {
                                        return $ret;
-                               else
+                               } else {
                                        return null;
+                               }
                }
 
                if ( $index )
@@ -2673,7 +2790,7 @@ class Parser
        }
 
        /**
-        * initialise the magic variables (like CURRENTMONTHNAME) and substitution modifiers 
+        * initialise the magic variables (like CURRENTMONTHNAME) and substitution modifiers
         *
         * @private
         */
@@ -2691,8 +2808,8 @@ class Parser
         * Preprocess some wikitext and return the document tree.
         * This is the ghost of replace_variables().
         *
-        * @param string $text The text to parse
-        * @param integer flags Bitwise combination of:
+        * @param $text String: The text to parse
+        * @param $flags Integer: bitwise combination of:
         *          self::PTD_FOR_INCLUSION    Handle <noinclude>/<includeonly> as if the text is being
         *                                     included. Default is to assume a direct page view.
         *
@@ -2709,12 +2826,12 @@ class Parser
         *
         * @private
         */
-       function preprocessToDom ( $text, $flags = 0 ) {
+       function preprocessToDom( $text, $flags = 0 ) {
                $dom = $this->getPreprocessor()->preprocessToObj( $text, $flags );
                return $dom;
        }
 
-       /*
+       /**
         * Return a three-element array: leading whitespace, string contents, trailing whitespace
         */
        public static function splitWhitespace( $s ) {
@@ -2740,11 +2857,11 @@ class Parser
         *  self::OT_PREPROCESS: templates but not extension tags
         *  self::OT_HTML: all templates and extension tags
         *
-        * @param string $tex The text to transform
-        * @param PPFrame $frame Object describing the arguments passed to the template.
+        * @param $text String: the text to transform
+        * @param $frame PPFrame Object describing the arguments passed to the template.
         *        Arguments may also be provided as an associative array, as was the usual case before MW1.12.
         *        Providing arguments this way may be useful for extensions wishing to perform variable replacement explicitly.
-        * @param bool $argsOnly Only do argument (triple-brace) expansion, not double-brace expansion
+        * @param $argsOnly Boolean: only do argument (triple-brace) expansion, not double-brace expansion
         * @private
         */
        function replaceVariables( $text, $frame = false, $argsOnly = false ) {
@@ -2758,7 +2875,7 @@ class Parser
                        $frame = $this->getPreprocessor()->newFrame();
                } elseif ( !( $frame instanceof PPFrame ) ) {
                        wfDebug( __METHOD__." called using plain parameters instead of a PPFrame instance. Creating custom frame.\n" );
-                       $frame = $this->getPreprocessor()->newCustomFrame($frame);
+                       $frame = $this->getPreprocessor()->newCustomFrame( $frame );
                }
 
                $dom = $this->preprocessToDom( $text );
@@ -2769,11 +2886,11 @@ class Parser
                return $text;
        }
 
-       /// Clean up argument array - refactored in 1.9 so parserfunctions can use it, too.
+       # Clean up argument array - refactored in 1.9 so parserfunctions can use it, too.
        static function createAssocArgs( $args ) {
                $assocArgs = array();
                $index = 1;
-               foreach( $args as $arg ) {
+               foreach ( $args as $arg ) {
                        $eqpos = strpos( $arg, '=' );
                        if ( $eqpos === false ) {
                                $assocArgs[$index++] = $arg;
@@ -2796,15 +2913,22 @@ class Parser
         * Warn the user when a parser limitation is reached
         * Will warn at most once the user per limitation type
         *
-        * @param string $limitationType, should be one of:
-        *   'expensive-parserfunction' (corresponding messages: 'expensive-parserfunction-warning', 'expensive-parserfunction-category')
-        *   'post-expand-template-argument' (corresponding messages: 'post-expand-template-argument-warning', 'post-expand-template-argument-category')
-        *   'post-expand-template-inclusion' (corresponding messages: 'post-expand-template-inclusion-warning', 'post-expand-template-inclusion-category')
-        * @params int $current, $max When an explicit limit has been
+        * @param $limitationType String: should be one of:
+        *   'expensive-parserfunction' (corresponding messages:
+        *       'expensive-parserfunction-warning',
+        *       'expensive-parserfunction-category')
+        *   'post-expand-template-argument' (corresponding messages:
+        *       'post-expand-template-argument-warning',
+        *       'post-expand-template-argument-category')
+        *   'post-expand-template-inclusion' (corresponding messages:
+        *       'post-expand-template-inclusion-warning',
+        *       'post-expand-template-inclusion-category')
+        * @param $current Current value
+        * @param $max Maximum allowed, when an explicit limit has been
         *       exceeded, provide the values (optional)
         */
        function limitationWarn( $limitationType, $current=null, $max=null) {
-               //does no harm if $current and $max are present but are unnecessary for the message
+               does no harm if $current and $max are present but are unnecessary for the message
                $warning = wfMsgExt( "$limitationType-warning", array( 'parsemag', 'escape' ), $current, $max );
                $this->mOutput->addWarning( $warning );
                $this->addTrackingCategory( "$limitationType-category" );
@@ -2814,12 +2938,12 @@ class Parser
         * Return the text of a template, after recursively
         * replacing any variables or templates within the template.
         *
-        * @param array $piece The parts of the template
+        * @param $piece Array: the parts of the template
         *  $piece['title']: the title, i.e. the part before the |
         *  $piece['parts']: the parameter array
         *  $piece['lineStart']: whether the brace was at the start of a line
-        * @param PPFrame The current frame, contains template arguments
-        * @return string the text of the template
+        * @param $frame PPFrame The current frame, contains template arguments
+        * @return String: the text of the template
         * @private
         */
        function braceSubstitution( $piece, $frame ) {
@@ -2848,7 +2972,8 @@ class Parser
                $originalTitle = $part1;
 
                # $args is a list of argument nodes, starting from index 0, not including $part1
-               $args = (null == $piece['parts']) ? array() : $piece['parts'];
+               # *** FIXME if piece['parts'] is null then the call to getLength() below won't work b/c this $args isn't an object
+               $args = ( null == $piece['parts'] ) ? array() : $piece['parts'];
                wfProfileOut( __METHOD__.'-setup' );
 
                # SUBST
@@ -2859,8 +2984,7 @@ class Parser
 
                        # Possibilities for substMatch: "subst", "safesubst" or FALSE
                        # Decide whether to expand template or keep wikitext as-is.
-                       if ( $this->ot['wiki'] )
-                       {
+                       if ( $this->ot['wiki'] ) {
                                if ( $substMatch === false ) {
                                        $literal = true;  # literal when in PST with no prefix
                                } else {
@@ -2885,8 +3009,9 @@ class Parser
                        $id = $this->mVariables->matchStartToEnd( $part1 );
                        if ( $id !== false ) {
                                $text = $this->getVariableValue( $id, $frame );
-                               if (MagicWord::getCacheTTL($id)>-1)
-                                       $this->mOutput->mContainsOldMagic = true;
+                               if ( MagicWord::getCacheTTL( $id ) > -1 ) {
+                                       $this->mOutput->updateCacheExpiry( MagicWord::getCacheTTL( $id ) );
+                               }
                                $found = true;
                        }
                }
@@ -2967,8 +3092,8 @@ class Parser
                                                        unset( $result[0] );
                                                }
 
-                                               // Extract flags into the local scope
-                                               // This allows callers to set flags such as nowiki, found, etc.
+                                               # Extract flags into the local scope
+                                               # This allows callers to set flags such as nowiki, found, etc.
                                                extract( $result );
                                        } else {
                                                $text = $result;
@@ -2989,21 +3114,23 @@ class Parser
                        # Split the title into page and subpage
                        $subpage = '';
                        $part1 = $this->maybeDoSubpageLink( $part1, $subpage );
-                       if ($subpage !== '') {
+                       if ( $subpage !== '' ) {
                                $ns = $this->mTitle->getNamespace();
                        }
                        $title = Title::newFromText( $part1, $ns );
                        if ( $title ) {
                                $titleText = $title->getPrefixedText();
                                # Check for language variants if the template is not found
-                               if($wgContLang->hasVariants() && $title->getArticleID() == 0){
+                               if ( $wgContLang->hasVariants() && $title->getArticleID() == 0 ) {
                                        $wgContLang->findVariantLink( $part1, $title, true );
                                }
                                # Do recursion depth check
                                $limit = $this->mOptions->getMaxTemplateDepth();
                                if ( $frame->depth >= $limit ) {
                                        $found = true;
-                                       $text = '<span class="error">' . wfMsgForContent( 'parser-template-recursion-depth-warning', $limit ) . '</span>';
+                                       $text = '<span class="error">'
+                                               . wfMsgForContent( 'parser-template-recursion-depth-warning', $limit )
+                                               . '</span>';
                                }
                        }
                }
@@ -3012,15 +3139,18 @@ class Parser
                if ( !$found && $title ) {
                        wfProfileIn( __METHOD__ . '-loadtpl' );
                        if ( !$title->isExternal() ) {
-                               if ( $title->getNamespace() == NS_SPECIAL && $this->mOptions->getAllowSpecialInclusion() && $this->ot['html'] ) {
+                               if ( $title->getNamespace() == NS_SPECIAL
+                                       && $this->mOptions->getAllowSpecialInclusion()
+                                       && $this->ot['html'] )
+                               {
                                        $text = SpecialPage::capturePath( $title );
                                        if ( is_string( $text ) ) {
                                                $found = true;
                                                $isHTML = true;
                                                $this->disableCache();
                                        }
-                               } else if ( $wgNonincludableNamespaces && in_array( $title->getNamespace(), $wgNonincludableNamespaces ) ) {
-                                       $found = false; //access denied
+                               } elseif ( $wgNonincludableNamespaces && in_array( $title->getNamespace(), $wgNonincludableNamespaces ) ) {
+                                       $found = false; access denied
                                        wfDebug( __METHOD__.": template inclusion denied for " . $title->getPrefixedDBkey() );
                                } else {
                                        list( $text, $title ) = $this->getTemplateDom( $title );
@@ -3036,13 +3166,13 @@ class Parser
                                        $found = true;
                                }
                        } elseif ( $title->isTrans() ) {
-                               // Interwiki transclusion
+                               # Interwiki transclusion
                                if ( $this->ot['html'] && !$forceRawInterwiki ) {
                                        $text = $this->interwikiTransclude( $title, 'render' );
                                        $isHTML = true;
                                } else {
                                        $text = $this->interwikiTransclude( $title, 'raw' );
-                                       // Preprocess it like a template
+                                       # Preprocess it like a template
                                        $text = $this->preprocessToDom( $text, self::PTD_FOR_INCLUSION );
                                        $isChildObj = true;
                                }
@@ -3097,22 +3227,31 @@ class Parser
                # immediately preceding headings
                if ( $isHTML ) {
                        $text = "\n\n" . $this->insertStripItem( $text );
-               }
-               # Escape nowiki-style return values
-               elseif ( $nowiki && ( $this->ot['html'] || $this->ot['pre'] ) ) {
+               } elseif ( $nowiki && ( $this->ot['html'] || $this->ot['pre'] ) ) {
+                       # Escape nowiki-style return values
                        $text = wfEscapeWikiText( $text );
-               }
-               # Bug 529: if the template begins with a table or block-level
-               # element, it should be treated as beginning a new line.
-               # This behaviour is somewhat controversial.
-               elseif ( is_string( $text ) && !$piece['lineStart'] && preg_match('/^(?:{\\||:|;|#|\*)/', $text)) /*}*/{
+               } elseif ( is_string( $text )
+                       && !$piece['lineStart']
+                       && preg_match( '/^(?:{\\||:|;|#|\*)/', $text ) )
+               {
+                       # Bug 529: if the template begins with a table or block-level
+                       # element, it should be treated as beginning a new line.
+                       # This behaviour is somewhat controversial.
                        $text = "\n" . $text;
                }
 
                if ( is_string( $text ) && !$this->incrementIncludeSize( 'post-expand', strlen( $text ) ) ) {
                        # Error, oversize inclusion
-                       $text = "[[$originalTitle]]" .
-                               $this->insertStripItem( '<!-- WARNING: template omitted, post-expand include size too large -->' );
+                       if ( $titleText !== false ) {
+                               # Make a working, properly escaped link if possible (bug 23588)
+                               $text = "[[:$titleText]]";
+                       } else {
+                               # This will probably not be a working link, but at least it may
+                               # provide some hint of where the problem is
+                               preg_replace( '/^:/', '', $originalTitle );
+                               $text = "[[:$originalTitle]]";
+                       }
+                       $text .= $this->insertStripItem( '<!-- WARNING: template omitted, post-expand include size too large -->' );
                        $this->limitationWarn( 'post-expand-template-inclusion' );
                }
 
@@ -3143,7 +3282,7 @@ class Parser
                        return array( $this->mTplDomCache[$titleText], $title );
                }
 
-               // Cache miss, go to the database
+               # Cache miss, go to the database
                list( $text, $title ) = $this->fetchTemplateAndTitle( $title );
 
                if ( $text === false ) {
@@ -3154,9 +3293,9 @@ class Parser
                $dom = $this->preprocessToDom( $text, self::PTD_FOR_INCLUSION );
                $this->mTplDomCache[ $titleText ] = $dom;
 
-               if (! $title->equals($cacheTitle)) {
+               if ( !$title->equals( $cacheTitle ) ) {
                        $this->mTplRedirCache[$cacheTitle->getPrefixedDBkey()] =
-                               array( $title->getNamespace(),$cdb = $title->getDBkey() );
+                               array( $title->getNamespace(), $cdb = $title->getDBkey() );
                }
 
                return array( $dom, $title );
@@ -3166,7 +3305,7 @@ class Parser
         * Fetch the unparsed text of a template and register a reference to it.
         */
        function fetchTemplateAndTitle( $title ) {
-               $templateCb = $this->mOptions->getTemplateCallback();
+               $templateCb = $this->mOptions->getTemplateCallback(); # Defaults to Parser::statelessFetchTemplate()
                $stuff = call_user_func( $templateCb, $title, $this );
                $text = $stuff['text'];
                $finalTitle = isset( $stuff['finalTitle'] ) ? $stuff['finalTitle'] : $title;
@@ -3175,11 +3314,11 @@ class Parser
                                $this->mOutput->addTemplate( $dep['title'], $dep['page_id'], $dep['rev_id'] );
                        }
                }
-               return array($text,$finalTitle);
+               return array( $text, $finalTitle );
        }
 
        function fetchTemplate( $title ) {
-               $rv = $this->fetchTemplateAndTitle($title);
+               $rv = $this->fetchTemplateAndTitle( $title );
                return $rv[0];
        }
 
@@ -3192,13 +3331,13 @@ class Parser
                $finalTitle = $title;
                $deps = array();
 
-               // Loop to fetch the article, with up to 1 redirect
+               # Loop to fetch the article, with up to 1 redirect
                for ( $i = 0; $i < 2 && is_object( $title ); $i++ ) {
                        # Give extensions a chance to select the revision instead
-                       $id = false; // Assume current
+                       $id = false; # Assume current
                        wfRunHooks( 'BeforeParserFetchTemplateAndtitle', array( $parser, &$title, &$skip, &$id ) );
 
-                       if( $skip ) {
+                       if ( $skip ) {
                                $text = false;
                                $deps[] = array(
                                        'title' => $title,
@@ -3208,8 +3347,8 @@ class Parser
                        }
                        $rev = $id ? Revision::newFromId( $id ) : Revision::newFromTitle( $title );
                        $rev_id = $rev ? $rev->getId() : 0;
-                       // If there is no current revision, there is no page
-                       if( $id === false && !$rev ) {
+                       # If there is no current revision, there is no page
+                       if ( $id === false && !$rev ) {
                                $linkCache = LinkCache::singleton();
                                $linkCache->addBadLinkObj( $title );
                        }
@@ -3219,13 +3358,13 @@ class Parser
                                'page_id' => $title->getArticleID(),
                                'rev_id' => $rev_id );
 
-                       if( $rev ) {
+                       if ( $rev ) {
                                $text = $rev->getText();
-                       } elseif( $title->getNamespace() == NS_MEDIAWIKI ) {
+                       } elseif ( $title->getNamespace() == NS_MEDIAWIKI ) {
                                global $wgContLang;
                                $message = $wgContLang->lcfirst( $title->getText() );
                                $text = wfMsgForContentNoTrans( $message );
-                               if( wfEmptyMsg( $message, $text ) ) {
+                               if ( wfEmptyMsg( $message, $text ) ) {
                                        $text = false;
                                        break;
                                }
@@ -3235,7 +3374,7 @@ class Parser
                        if ( $text === false ) {
                                break;
                        }
-                       // Redirect?
+                       # Redirect?
                        $finalTitle = $title;
                        $title = Title::newFromRedirect( $text );
                }
@@ -3251,35 +3390,39 @@ class Parser
        function interwikiTransclude( $title, $action ) {
                global $wgEnableScaryTranscluding;
 
-               if (!$wgEnableScaryTranscluding)
-                       return wfMsg('scarytranscludedisabled');
+               if ( !$wgEnableScaryTranscluding ) {
+                       return wfMsgForContent('scarytranscludedisabled');
+               }
 
                $url = $title->getFullUrl( "action=$action" );
 
-               if (strlen($url) > 255)
-                       return wfMsg('scarytranscludetoolong');
-               return $this->fetchScaryTemplateMaybeFromCache($url);
+               if ( strlen( $url ) > 255 ) {
+                       return wfMsgForContent( 'scarytranscludetoolong' );
+               }
+               return $this->fetchScaryTemplateMaybeFromCache( $url );
        }
 
-       function fetchScaryTemplateMaybeFromCache($url) {
+       function fetchScaryTemplateMaybeFromCache( $url ) {
                global $wgTranscludeCacheExpiry;
-               $dbr = wfGetDB(DB_SLAVE);
+               $dbr = wfGetDB( DB_SLAVE );
                $tsCond = $dbr->timestamp( time() - $wgTranscludeCacheExpiry );
-               $obj = $dbr->selectRow('transcache', array('tc_time', 'tc_contents'),
-                               array('tc_url' => $url, "tc_time >= " . $dbr->addQuotes( $tsCond ) ) );
-               if ($obj) {
+               $obj = $dbr->selectRow( 'transcache', array('tc_time', 'tc_contents' ),
+                               array( 'tc_url' => $url, "tc_time >= " . $dbr->addQuotes( $tsCond ) ) );
+               if ( $obj ) {
                        return $obj->tc_contents;
                }
 
-               $text = Http::get($url);
-               if (!$text)
-                       return wfMsg('scarytranscludefailed', $url);
+               $text = Http::get( $url );
+               if ( !$text ) {
+                       return wfMsgForContent( 'scarytranscludefailed', $url );
+               }
 
-               $dbw = wfGetDB(DB_MASTER);
-               $dbw->replace('transcache', array('tc_url'), array(
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->replace( 'transcache', array('tc_url'), array(
                        'tc_url' => $url,
                        'tc_time' => $dbw->timestamp( time() ),
-                       'tc_contents' => $text));
+                       'tc_contents' => $text)
+               );
                return $text;
        }
 
@@ -3299,9 +3442,9 @@ class Parser
                $text = $frame->getArgument( $argName );
                if (  $text === false && $parts->getLength() > 0
                  && (
-                   $this->ot['html']
-                   || $this->ot['pre']
-                   || ( $this->ot['wiki'] && $frame->isTemplate() )
+                       $this->ot['html']
+                       || $this->ot['pre']
+                       || ( $this->ot['wiki'] && $frame->isTemplate() )
                  )
                ) {
                        # No match in frame, use the supplied default
@@ -3333,21 +3476,19 @@ class Parser
         * Return the text to be used for a given extension tag.
         * This is the ghost of strip().
         *
-        * @param array $params Associative array of parameters:
+        * @param $params Associative array of parameters:
         *     name       PPNode for the tag name
         *     attr       PPNode for unparsed text where tag attributes are thought to be
         *     attributes Optional associative array of parsed attributes
         *     inner      Contents of extension element
         *     noClose    Original text did not have a close tag
-        * @param PPFrame $frame
+        * @param $frame PPFrame
         */
        function extensionSubstitution( $params, $frame ) {
-               global $wgRawHtml, $wgContLang;
-
                $name = $frame->expand( $params['name'] );
                $attrText = !isset( $params['attr'] ) ? null : $frame->expand( $params['attr'] );
                $content = !isset( $params['inner'] ) ? null : $frame->expand( $params['inner'] );
-               $marker = "{$this->mUniqPrefix}-$name-" . sprintf('%08X', $this->mMarkerIndex++) . self::MARKER_SUFFIX;
+               $marker = "{$this->mUniqPrefix}-$name-" . sprintf( '%08X', $this->mMarkerIndex++ ) . self::MARKER_SUFFIX;
 
                $isFunctionTag = isset( $this->mFunctionTagHooks[strtolower($name)] ) &&
                        ( $this->ot['html'] || $this->ot['pre'] );
@@ -3363,27 +3504,27 @@ class Parser
                                $attributes = $attributes + $params['attributes'];
                        }
 
-                       if( isset( $this->mTagHooks[$name] ) ) {
+                       if ( isset( $this->mTagHooks[$name] ) ) {
                                # Workaround for PHP bug 35229 and similar
                                if ( !is_callable( $this->mTagHooks[$name] ) ) {
                                        throw new MWException( "Tag hook for $name is not callable\n" );
                                }
                                $output = call_user_func_array( $this->mTagHooks[$name],
                                        array( $content, $attributes, $this, $frame ) );
-                       } elseif( isset( $this->mFunctionTagHooks[$name] ) ) {
+                       } elseif ( isset( $this->mFunctionTagHooks[$name] ) ) {
                                list( $callback, $flags ) = $this->mFunctionTagHooks[$name];
-                               if( !is_callable( $callback ) )
+                               if ( !is_callable( $callback ) ) {
                                        throw new MWException( "Tag hook for $name is not callable\n" );
+                               }
 
-                               $output = call_user_func_array( $callback,
-                                       array( &$this, $frame, $content, $attributes ) );
+                               $output = call_user_func_array( $callback, array( &$this, $frame, $content, $attributes ) );
                        } else {
                                $output = '<span class="error">Invalid tag extension name: ' .
                                        htmlspecialchars( $name ) . '</span>';
                        }
 
                        if ( is_array( $output ) ) {
-                               // Extract flags to local scope (to override $markerType)
+                               # Extract flags to local scope (to override $markerType)
                                $flags = $output;
                                $output = $flags[0];
                                unset( $flags[0] );
@@ -3407,7 +3548,7 @@ class Parser
                        }
                }
 
-               if( $markerType === 'none' ) {
+               if ( $markerType === 'none' ) {
                        return $output;
                } elseif ( $markerType === 'nowiki' ) {
                        $this->mStripState->nowiki->setPair( $marker, $output );
@@ -3422,12 +3563,12 @@ class Parser
        /**
         * Increment an include size counter
         *
-        * @param string $type The type of expansion
-        * @param integer $size The size of the text
-        * @return boolean False if this inclusion would take it over the maximum, true otherwise
+        * @param $type String: the type of expansion
+        * @param $size Integer: the size of the text
+        * @return Boolean: false if this inclusion would take it over the maximum, true otherwise
         */
        function incrementIncludeSize( $type, $size ) {
-               if ( $this->mIncludeSizes[$type] + $size > $this->mOptions->getMaxIncludeSize( $type ) ) {
+               if ( $this->mIncludeSizes[$type] + $size > $this->mOptions->getMaxIncludeSize() ) {
                        return false;
                } else {
                        $this->mIncludeSizes[$type] += $size;
@@ -3438,12 +3579,12 @@ class Parser
        /**
         * Increment the expensive function count
         *
-        * @return boolean False if the limit has been exceeded
+        * @return Boolean: false if the limit has been exceeded
         */
        function incrementExpensiveFunctionCount() {
                global $wgExpensiveParserFunctionLimit;
                $this->mExpensiveFunctionCount++;
-               if($this->mExpensiveFunctionCount <= $wgExpensiveParserFunctionLimit) {
+               if ( $this->mExpensiveFunctionCount <= $wgExpensiveParserFunctionLimit ) {
                        return true;
                }
                return false;
@@ -3456,20 +3597,20 @@ class Parser
        function doDoubleUnderscore( $text ) {
                wfProfileIn( __METHOD__ );
 
-               // The position of __TOC__ needs to be recorded
+               # The position of __TOC__ needs to be recorded
                $mw = MagicWord::get( 'toc' );
-               if( $mw->match( $text ) ) {
+               if ( $mw->match( $text ) ) {
                        $this->mShowToc = true;
                        $this->mForceTocPosition = true;
 
-                       // Set a placeholder. At the end we'll fill it in with the TOC.
+                       # Set a placeholder. At the end we'll fill it in with the TOC.
                        $text = $mw->replace( '<!--MWTOC-->', $text, 1 );
 
-                       // Only keep the first one.
+                       # Only keep the first one.
                        $text = $mw->replace( '', $text );
                }
 
-               // Now match and remove the rest of them
+               # Now match and remove the rest of them
                $mwa = MagicWord::getDoubleUnderscoreArray();
                $this->mDoubleUnderscores = $mwa->matchAndRemove( $text );
 
@@ -3480,22 +3621,26 @@ class Parser
                        $this->mShowToc = false;
                }
                if ( isset( $this->mDoubleUnderscores['hiddencat'] ) && $this->mTitle->getNamespace() == NS_CATEGORY ) {
-                       $this->mOutput->setProperty( 'hiddencat', 'y' );
                        $this->addTrackingCategory( 'hidden-category-category' );
                }
                # (bug 8068) Allow control over whether robots index a page.
                #
                # FIXME (bug 14899): __INDEX__ always overrides __NOINDEX__ here!  This
                # is not desirable, the last one on the page should win.
-               if( isset( $this->mDoubleUnderscores['noindex'] ) && $this->mTitle->canUseNoindex() ) {
+               if ( isset( $this->mDoubleUnderscores['noindex'] ) && $this->mTitle->canUseNoindex() ) {
                        $this->mOutput->setIndexPolicy( 'noindex' );
                        $this->addTrackingCategory( 'noindex-category' );
                }
-               if( isset( $this->mDoubleUnderscores['index'] ) && $this->mTitle->canUseNoindex() ){
+               if ( isset( $this->mDoubleUnderscores['index'] ) && $this->mTitle->canUseNoindex() ) {
                        $this->mOutput->setIndexPolicy( 'index' );
                        $this->addTrackingCategory( 'index-category' );
                }
 
+               # Cache all double underscores in the database
+               foreach ( $this->mDoubleUnderscores as $key => $val ) {
+                       $this->mOutput->setProperty( $key, '' );
+               }
+
                wfProfileOut( __METHOD__ );
                return $text;
        }
@@ -3503,14 +3648,17 @@ class Parser
        /**
         * Add a tracking category, getting the title from a system message,
         * or print a debug message if the title is invalid.
-        * @param $msg String message key
-        * @return Bool whether the addition was successful
+        *
+        * @param $msg String: message key
+        * @return Boolean: whether the addition was successful
         */
-       protected function addTrackingCategory( $msg ){
+       protected function addTrackingCategory( $msg ) {
                $cat = wfMsgForContent( $msg );
 
                # Allow tracking categories to be disabled by setting them to "-"
-               if( $cat === '-' ) return false;
+               if ( $cat === '-' ) {
+                       return false;
+               }
 
                $containerCategory = Title::makeTitleSafe( NS_CATEGORY, $cat );
                if ( $containerCategory ) {
@@ -3532,25 +3680,27 @@ class Parser
         * It loops through all headlines, collects the necessary data, then splits up the
         * string and re-inserts the newly formatted headlines.
         *
-        * @param string $text
-        * @param string $origText Original, untouched wikitext
-        * @param boolean $isMain
+        * @param $text String
+        * @param $origText String: original, untouched wikitext
+        * @param $isMain Boolean
         * @private
         */
        function formatHeadings( $text, $origText, $isMain=true ) {
                global $wgMaxTocLevel, $wgContLang, $wgHtml5, $wgExperimentalHtmlIds;
 
                $doNumberHeadings = $this->mOptions->getNumberHeadings();
-               $showEditLink = $this->mOptions->getEditSection();
-
-               // Do not call quickUserCan unless necessary
-               if( $showEditLink && !$this->mTitle->quickUserCan( 'edit' ) ) {
-                       $showEditLink = 0;
-               }
 
                # Inhibit editsection links if requested in the page
-               if ( isset( $this->mDoubleUnderscores['noeditsection'] )  || $this->mOptions->getIsPrintable() ) {
+               if ( isset( $this->mDoubleUnderscores['noeditsection'] ) ) {
                        $showEditLink = 0;
+               } else {
+                       $showEditLink = $this->mOptions->getEditSection();
+               }
+               if ( $showEditLink ) {
+                       $editLinkAsToken = $this->mOptions->getEditSectionTokens();
+                       if ( $editLinkAsToken ) {
+                               $this->mOutput->setEditSectionTokens( "{$this->mUniqPrefix}-editsection-", self::MARKER_SUFFIX );
+                       }
                }
 
                # Get all headlines for numbering them and adding funky stuff like [edit]
@@ -3561,7 +3711,7 @@ class Parser
                # if there are fewer than 4 headlines in the article, do not show TOC
                # unless it's been explicitly enabled.
                $enoughToc = $this->mShowToc &&
-                       (($numMatches >= 4) || $this->mForceTocPosition);
+                       ( ( $numMatches >= 4 ) || $this->mForceTocPosition );
 
                # Allow user to stipulate that a page should have a "new section"
                # link added via __NEWSECTIONLINK__
@@ -3583,7 +3733,7 @@ class Parser
                }
 
                # We need this to perform operations on the HTML
-               $sk = $this->mOptions->getSkin();
+               $sk = $this->mOptions->getSkin( $this->mTitle );
 
                # headline counter
                $headlineCount = 0;
@@ -3596,7 +3746,6 @@ class Parser
                $head = array();
                $sublevelCount = array();
                $levelCount = array();
-               $toclevel = 0;
                $level = 0;
                $prevlevel = 0;
                $toclevel = 0;
@@ -3610,23 +3759,23 @@ class Parser
                $node = $root->getFirstChild();
                $byteOffset = 0;
                $tocraw = array();
+               $refers = array();
 
-               foreach( $matches[3] as $headline ) {
+               foreach ( $matches[3] as $headline ) {
                        $isTemplate = false;
                        $titleText = false;
                        $sectionIndex = false;
                        $numbering = '';
                        $markerMatches = array();
-                       if (preg_match("/^$markerRegex/", $headline, $markerMatches)) {
+                       if ( preg_match("/^$markerRegex/", $headline, $markerMatches ) ) {
                                $serial = $markerMatches[1];
                                list( $titleText, $sectionIndex ) = $this->mHeadings[$serial];
-                               $isTemplate = ($titleText != $baseTitleText);
-                               $headline = preg_replace("/^$markerRegex/", "", $headline);
+                               $isTemplate = ( $titleText != $baseTitleText );
+                               $headline = preg_replace( "/^$markerRegex/", "", $headline );
                        }
 
-                       if( $toclevel ) {
+                       if ( $toclevel ) {
                                $prevlevel = $level;
-                               $prevtoclevel = $toclevel;
                        }
                        $level = $matches[1][$headlineCount];
 
@@ -3634,30 +3783,30 @@ class Parser
                                # Increase TOC level
                                $toclevel++;
                                $sublevelCount[$toclevel] = 0;
-                               if( $toclevel<$wgMaxTocLevel ) {
+                               if ( $toclevel<$wgMaxTocLevel ) {
                                        $prevtoclevel = $toclevel;
                                        $toc .= $sk->tocIndent();
                                        $numVisible++;
                                }
-                       }
-                       elseif ( $level < $prevlevel && $toclevel > 1 ) {
+                       } elseif ( $level < $prevlevel && $toclevel > 1 ) {
                                # Decrease TOC level, find level to jump to
 
-                               for ($i = $toclevel; $i > 0; $i--) {
+                               for ( $i = $toclevel; $i > 0; $i-- ) {
                                        if ( $levelCount[$i] == $level ) {
                                                # Found last matching level
                                                $toclevel = $i;
                                                break;
-                                       }
-                                       elseif ( $levelCount[$i] < $level ) {
+                                       } elseif ( $levelCount[$i] < $level ) {
                                                # Found first matching level below current level
                                                $toclevel = $i + 1;
                                                break;
                                        }
                                }
-                               if( $i == 0 ) $toclevel = 1;
-                               if( $toclevel<$wgMaxTocLevel ) {
-                                       if($prevtoclevel < $wgMaxTocLevel) {
+                               if ( $i == 0 ) {
+                                       $toclevel = 1;
+                               }
+                               if ( $toclevel<$wgMaxTocLevel ) {
+                                       if ( $prevtoclevel < $wgMaxTocLevel ) {
                                                # Unindent only if the previous toc level was shown :p
                                                $toc .= $sk->tocUnindent( $prevtoclevel - $toclevel );
                                                $prevtoclevel = $toclevel;
@@ -3665,10 +3814,9 @@ class Parser
                                                $toc .= $sk->tocLineEnd();
                                        }
                                }
-                       }
-                       else {
+                       } else {
                                # No change in level, end TOC line
-                               if( $toclevel<$wgMaxTocLevel ) {
+                               if ( $toclevel<$wgMaxTocLevel ) {
                                        $toc .= $sk->tocLineEnd();
                                }
                        }
@@ -3679,8 +3827,8 @@ class Parser
                        @$sublevelCount[$toclevel]++;
                        $dot = 0;
                        for( $i = 1; $i <= $toclevel; $i++ ) {
-                               if( !empty( $sublevelCount[$i] ) ) {
-                                       if( $dot ) {
+                               if ( !empty( $sublevelCount[$i] ) ) {
+                                       if ( $dot ) {
                                                $numbering .= '.';
                                        }
                                        $numbering .= $wgContLang->formatNum( $sublevelCount[$i] );
@@ -3701,15 +3849,14 @@ class Parser
                        # Strip out HTML (other than plain <sup> and <sub>: bug 8393)
                        $tocline = preg_replace(
                                array( '#<(?!/?(sup|sub)).*?'.'>#', '#<(/?(sup|sub)).*?'.'>#' ),
-                               array( '',                          '<$1>'),
+                               array( '',                          '<$1>' ),
                                $safeHeadline
                        );
                        $tocline = trim( $tocline );
 
                        # For the anchor, strip out HTML-y stuff period
                        $safeHeadline = preg_replace( '/<.*?'.'>/', '', $safeHeadline );
-                       $safeHeadline = preg_replace( '/[ _]+/', ' ', $safeHeadline );
-                       $safeHeadline = trim( $safeHeadline );
+                       $safeHeadline = Sanitizer::normalizeSectionNameWhitespace( $safeHeadline );
 
                        # Save headline for section edit hint before it's escaped
                        $headlineHint = $safeHeadline;
@@ -3739,9 +3886,10 @@ class Parser
                                        'noninitial' );
                        }
 
-                       # HTML names must be case-insensitively unique (bug 10721).  FIXME:
-                       # Does this apply to Unicode characters?  Because we aren't
-                       # handling those here.
+                       # HTML names must be case-insensitively unique (bug 10721).
+                       # This does not apply to Unicode characters per
+                       # http://dev.w3.org/html5/spec/infrastructure.html#case-sensitivity-and-string-comparison
+                       # FIXME: We may be changing them depending on the current locale.
                        $arrayKey = strtolower( $safeHeadline );
                        if ( $legacyHeadline === false ) {
                                $legacyArrayKey = false;
@@ -3762,7 +3910,7 @@ class Parser
                        }
 
                        # Don't number the heading if it is the only one (looks silly)
-                       if( $doNumberHeadings && count( $matches[3] ) > 1) {
+                       if ( $doNumberHeadings && count( $matches[3] ) > 1) {
                                # the two are different if the line contains a link
                                $headline = $numbering . ' ' . $headline;
                        }
@@ -3776,9 +3924,9 @@ class Parser
                        if ( $legacyHeadline !== false && $refers[$legacyArrayKey] > 1 ) {
                                $legacyAnchor .= '_' . $refers[$legacyArrayKey];
                        }
-                       if( $enoughToc && ( !isset($wgMaxTocLevel) || $toclevel<$wgMaxTocLevel ) ) {
-                               $toc .= $sk->tocLine($anchor, $tocline,
-                                       $numbering, $toclevel, ($isTemplate ? false : $sectionIndex));
+                       if ( $enoughToc && ( !isset( $wgMaxTocLevel ) || $toclevel < $wgMaxTocLevel ) ) {
+                               $toc .= $sk->tocLine( $anchor, $tocline,
+                                       $numbering, $toclevel, ( $isTemplate ? false : $sectionIndex ) );
                        }
 
                        # Add the section to the section tree
@@ -3786,8 +3934,9 @@ class Parser
                        while ( $node && !$isTemplate ) {
                                if ( $node->getName() === 'h' ) {
                                        $bits = $node->splitHeading();
-                                       if ( $bits['i'] == $sectionIndex )
+                                       if ( $bits['i'] == $sectionIndex ) {
                                                break;
+                                       }
                                }
                                $byteOffset += mb_strlen( $this->mStripState->unstripBoth(
                                        $frame->expand( $node, PPFrame::RECOVER_ORIG ) ) );
@@ -3798,20 +3947,36 @@ class Parser
                                'level' => $level,
                                'line' => $tocline,
                                'number' => $numbering,
-                               'index' => ($isTemplate ? 'T-' : '' ) . $sectionIndex,
+                               'index' => ( $isTemplate ? 'T-' : '' ) . $sectionIndex,
                                'fromtitle' => $titleText,
                                'byteoffset' => ( $isTemplate ? null : $byteOffset ),
                                'anchor' => $anchor,
                        );
 
                        # give headline the correct <h#> tag
-                       if( $showEditLink && $sectionIndex !== false ) {
-                               if( $isTemplate ) {
-                                       # Put a T flag in the section identifier, to indicate to extractSections()
-                                       # that sections inside <includeonly> should be counted.
-                                       $editlink = $sk->doEditSectionLink(Title::newFromText( $titleText ), "T-$sectionIndex");
+                       if ( $showEditLink && $sectionIndex !== false ) {
+                               if ( $editLinkAsToken ) {
+                                       // Output edit section links as markers with styles that can be customized by skins
+                                       if ( $isTemplate ) {
+                                               # Put a T flag in the section identifier, to indicate to extractSections()
+                                               # that sections inside <includeonly> should be counted.
+                                               $editlinkArgs = array( $titleText, "T-$sectionIndex", null );
+                                       } else {
+                                               $editlinkArgs = array( $this->mTitle->getPrefixedText(), $sectionIndex, $headlineHint );
+                                       }
+                                       // We use nearly the same structure as uniqPrefix and the marker stuffix (besides there being nothing random)
+                                       // However the this is output into the parser output itself not replaced early, so we hardcode this in case
+                                       // the constants change in a different version of MediaWiki, which would break this code.
+                                       $editlink = "{$this->mUniqPrefix}-editsection-" . serialize($editlinkArgs) . self::MARKER_SUFFIX;
                                } else {
-                                       $editlink = $sk->doEditSectionLink($this->mTitle, $sectionIndex, $headlineHint);
+                                       // Output edit section links directly as markup like we used to
+                                       if ( $isTemplate ) {
+                                               # Put a T flag in the section identifier, to indicate to extractSections()
+                                               # that sections inside <includeonly> should be counted.
+                                               $editlink = $sk->doEditSectionLink( Title::newFromText( $titleText ), "T-$sectionIndex", null, $this->mOptions->getUserLang() );
+                                       } else {
+                                               $editlink = $sk->doEditSectionLink( $this->mTitle, $sectionIndex, $headlineHint, $this->mOptions->getUserLang() );
+                                       }
                                }
                        } else {
                                $editlink = '';
@@ -3826,12 +3991,12 @@ class Parser
                $this->setOutputType( $oldType );
 
                # Never ever show TOC if no headers
-               if( $numVisible < 1 ) {
+               if ( $numVisible < 1 ) {
                        $enoughToc = false;
                }
 
-               if( $enoughToc ) {
-                       if( $prevtoclevel > 0 && $prevtoclevel < $wgMaxTocLevel ) {
+               if ( $enoughToc ) {
+                       if ( $prevtoclevel > 0 && $prevtoclevel < $wgMaxTocLevel ) {
                                $toc .= $sk->tocUnindent( $prevtoclevel - 1 );
                        }
                        $toc = $sk->tocList( $toc );
@@ -3847,8 +4012,8 @@ class Parser
                $blocks = preg_split( '/<H[1-6].*?' . '>.*?<\/H[1-6]>/i', $text );
                $i = 0;
 
-               foreach( $blocks as $block ) {
-                       if( $showEditLink && $headlineCount > 0 && $i == 0 && $block !== "\n" ) {
+               foreach ( $blocks as $block ) {
+                       if ( $showEditLink && $headlineCount > 0 && $i == 0 && $block !== "\n" ) {
                                # This is the [edit] link that appears for the top block of text when
                                # section editing is enabled
 
@@ -3857,128 +4022,38 @@ class Parser
                                # $full .= $sk->editSectionLink(0);
                        }
                        $full .= $block;
-                       if( $enoughToc && !$i && $isMain && !$this->mForceTocPosition ) {
+                       if ( $enoughToc && !$i && $isMain && !$this->mForceTocPosition ) {
                                # Top anchor now in skin
                                $full = $full.$toc;
                        }
 
-                       if( !empty( $head[$i] ) ) {
+                       if ( !empty( $head[$i] ) ) {
                                $full .= $head[$i];
                        }
                        $i++;
                }
-               if( $this->mForceTocPosition ) {
+               if ( $this->mForceTocPosition ) {
                        return str_replace( '<!--MWTOC-->', $toc, $full );
                } else {
                        return $full;
                }
        }
 
-       /**
-        * Merge $tree2 into $tree1 by replacing the section with index
-        * $section in $tree1 and its descendants with the sections in $tree2.
-        * Note that in the returned section tree, only the 'index' and
-        * 'byteoffset' fields are guaranteed to be correct.
-        * @param $tree1 array Section tree from ParserOutput::getSectons()
-       *  @param $tree2 array Section tree
-        * @param $section int Section index
-        * @param $title Title Title both section trees come from
-        * @param $len2 int Length of the original wikitext for $tree2
-        * @return array Merged section tree
-        */
-       public static function mergeSectionTrees( $tree1, $tree2, $section, $title, $len2 ) {
-               global $wgContLang;
-               $newTree = array();
-               $targetLevel = false;
-               $merged = false;
-               $lastLevel = 1;
-               $nextIndex = 1;
-               $numbering = array( 0 );
-               $titletext = $title->getPrefixedDBkey();
-               foreach ( $tree1 as $s ) {
-                       if ( $targetLevel !== false ) {
-                               if ( $s['level'] <= $targetLevel )
-                                       // We've skipped enough
-                                       $targetLevel = false;
-                               else
-                                       continue;
-                       }
-                       if ( $s['index'] != $section ||
-                                       $s['fromtitle'] != $titletext ) {
-                               self::incrementNumbering( $numbering,
-                                       $s['toclevel'], $lastLevel );
-
-                               // Rewrite index, byteoffset and number
-                               if ( $s['fromtitle'] == $titletext ) {
-                                       $s['index'] = $nextIndex++;
-                                       if ( $merged )
-                                               $s['byteoffset'] += $len2;
-                               }
-                               $s['number']  = implode( '.', array_map(
-                                       array( $wgContLang, 'formatnum' ),
-                                       $numbering ) );
-                               $lastLevel = $s['toclevel'];
-                               $newTree[] = $s;
-                       } else {
-                               // We're at $section
-                               // Insert sections from $tree2 here
-                               foreach ( $tree2 as $s2 ) {
-                                       // Rewrite the fields in $s2
-                                       // before inserting it
-                                       $s2['toclevel'] += $s['toclevel'] - 1;
-                                       $s2['level'] += $s['level'] - 1;
-                                       $s2['index'] = $nextIndex++;
-                                       $s2['byteoffset'] += $s['byteoffset'];
-
-                                       self::incrementNumbering( $numbering,
-                                               $s2['toclevel'], $lastLevel );
-                                       $s2['number']  = implode( '.', array_map(
-                                               array( $wgContLang, 'formatnum' ),
-                                               $numbering ) );
-                                       $lastLevel = $s2['toclevel'];
-                                       $newTree[] = $s2;
-                               }
-                               // Skip all descendants of $section in $tree1
-                               $targetLevel = $s['level'];
-                               $merged = true;
-                       }
-               }
-               return $newTree;
-       }
-
-       /**
-        * Increment a section number. Helper function for mergeSectionTrees()
-        * @param $number array Array representing a section number
-        * @param $level int Current TOC level (depth)
-        * @param $lastLevel int Level of previous TOC entry
-        */
-       private static function incrementNumbering( &$number, $level, $lastLevel ) {
-               if ( $level > $lastLevel )
-                       $number[$level - 1] = 1;
-               else if ( $level < $lastLevel ) {
-                       foreach ( $number as $key => $unused )
-                               if ( $key >= $level )
-                                       unset( $number[$key] );
-                       $number[$level - 1]++;
-               } else
-                       $number[$level - 1]++;
-       }
-
        /**
         * Transform wiki markup when saving a page by doing \r\n -> \n
         * conversion, substitting signatures, {{subst:}} templates, etc.
         *
-        * @param string $text the text to transform
-        * @param Title &$title the Title object for the current article
-        * @param User $user the User object describing the current user
-        * @param ParserOptions $options parsing options
-        * @param bool $clearState whether to clear the parser state first
-        * @return string the altered wiki markup
-        * @public
+        * @param $text String: the text to transform
+        * @param $title Title: the Title object for the current article
+        * @param $user User: the User object describing the current user
+        * @param $options ParserOptions: parsing options
+        * @param $clearState Boolean: whether to clear the parser state first
+        * @return String: the altered wiki markup
         */
-       function preSaveTransform( $text, Title $title, $user, $options, $clearState = true ) {
+       public function preSaveTransform( $text, Title $title, User $user, ParserOptions $options, $clearState = true ) {
                $this->mOptions = $options;
                $this->setTitle( $title );
+               $this->setUser( $user );
                $this->setOutputType( self::OT_WIKI );
 
                if ( $clearState ) {
@@ -3991,6 +4066,9 @@ class Parser
                $text = str_replace( array_keys( $pairs ), array_values( $pairs ), $text );
                $text = $this->pstPass2( $text, $user );
                $text = $this->mStripState->unstripBoth( $text );
+
+               $this->setUser( null ); #Reset
+
                return $text;
        }
 
@@ -4001,13 +4079,11 @@ class Parser
        function pstPass2( $text, $user ) {
                global $wgContLang, $wgLocaltimezone;
 
-               /* Note: This is the timestamp saved as hardcoded wikitext to
-                * the database, we use $wgContLang here in order to give
-                * everyone the same signature and use the default one rather
-                * than the one selected in each user's preferences.
-                *
-                * (see also bug 12815)
-                */
+               # Note: This is the timestamp saved as hardcoded wikitext to
+               # the database, we use $wgContLang here in order to give
+               # everyone the same signature and use the default one rather
+               # than the one selected in each user's preferences.
+               # (see also bug 12815)
                $ts = $this->mOptions->getTimestamp();
                if ( isset( $wgLocaltimezone ) ) {
                        $tz = $wgLocaltimezone;
@@ -4021,13 +4097,14 @@ class Parser
                $ts = date( 'YmdHis', $unixts );
                $tzMsg = date( 'T', $unixts );  # might vary on DST changeover!
 
-               /* Allow translation of timezones trough wiki. date() can return
-                * whatever crap the system uses, localised or not, so we cannot
-                * ship premade translations.
-                */
+               # Allow translation of timezones through wiki. date() can return
+               # whatever crap the system uses, localised or not, so we cannot
+               # ship premade translations.
                $key = 'timezone-' . strtolower( trim( $tzMsg ) );
                $value = wfMsgForContent( $key );
-               if ( !wfEmptyMsg( $key, $value ) ) $tzMsg = $value;
+               if ( !wfEmptyMsg( $key, $value ) ) {
+                       $tzMsg = $value;
+               }
 
                date_default_timezone_set( $oldtz );
 
@@ -4045,11 +4122,31 @@ class Parser
                        '~~~' => $sigText
                ) );
 
-               # Links of the form [[|<blah>]] or [[<blah>|]] perform pipe tricks
-               # Note this only allows the # in the position it works.
+               # Context links: [[|name]] and [[name (context)|]]
                global $wgLegalTitleChars;
-               $pipeTrickRe = "/\[\[(?:(\\|)([$wgLegalTitleChars]+)|([#$wgLegalTitleChars]+)\\|)\]\]/";
-               $text = preg_replace_callback( $pipeTrickRe, array( $this, 'pstPipeTrickCallback' ), $text );
+               $tc = "[$wgLegalTitleChars]";
+               $nc = '[ _0-9A-Za-z\x80-\xff-]'; # Namespaces can use non-ascii!
+
+               $p1 = "/\[\[(:?$nc+:|:|)($tc+?)( \\($tc+\\))\\|]]/";            # [[ns:page (context)|]]
+               $p4 = "/\[\[(:?$nc+:|:|)($tc+?)(($tc+))\\|]]/";             # [[ns:page(context)|]]
+               $p3 = "/\[\[(:?$nc+:|:|)($tc+?)( \\($tc+\\)|)(, $tc+|)\\|]]/";  # [[ns:page (context), context|]]
+               $p2 = "/\[\[\\|($tc+)]]/";                                      # [[|page]]
+
+               # try $p1 first, to turn "[[A, B (C)|]]" into "[[A, B (C)|A, B]]"
+               $text = preg_replace( $p1, '[[\\1\\2\\3|\\2]]', $text );
+               $text = preg_replace( $p4, '[[\\1\\2\\3|\\2]]', $text );
+               $text = preg_replace( $p3, '[[\\1\\2\\3\\4|\\2]]', $text );
+
+               $t = $this->mTitle->getText();
+               $m = array();
+               if ( preg_match( "/^($nc+:|)$tc+?( \\($tc+\\))$/", $t, $m ) ) {
+                       $text = preg_replace( $p2, "[[$m[1]\\1$m[2]|\\1]]", $text );
+               } elseif ( preg_match( "/^($nc+:|)$tc+?(, $tc+|)$/", $t, $m ) && "$m[1]$m[2]" != '' ) {
+                       $text = preg_replace( $p2, "[[$m[1]\\1$m[2]|\\1]]", $text );
+               } else {
+                       # if there's no context, don't bother duplicating the title
+                       $text = preg_replace( $p2, '[[\\1]]', $text );
+               }
 
                # Trim trailing whitespace
                $text = rtrim( $text );
@@ -4057,32 +4154,16 @@ class Parser
                return $text;
        }
 
-       /**
-        * Called from pstPass2 to perform the pipe trick on links.
-        * Original was either [[|text]] or [[link|]]
-        *
-        * @param Array ("|" or "", text, link) $m
-        */
-       function pstPipeTrickCallback( $m )
-       {
-               if( $m[1] ) { # [[|<blah>]]
-                       $text = $m[2];
-                       $link = $this->getPipeTrickLink( $text );
-               } else { # [[<blah>|]]
-                       $link = $m[3];
-                       $text = $this->getPipeTrickText( $link );
-               }
-
-               return $link === $text ? "[[$link]]" : "[[$link|$text]]";
-       }
-
        /**
         * Fetch the user's signature text, if any, and normalize to
         * validated, ready-to-insert wikitext.
         * If you have pre-fetched the nickname or the fancySig option, you can
         * specify them here to save a database query.
         *
-        * @param User $user
+        * @param $user User
+        * @param $nickname String: nickname to use or false to use user's default nickname
+        * @param $fancySig Boolean: whether the nicknname is the complete signature
+        *                  or null to use default value
         * @return string
         */
        function getUserSig( &$user, $nickname = false, $fancySig = null ) {
@@ -4090,21 +4171,22 @@ class Parser
 
                $username = $user->getName();
 
-               // If not given, retrieve from the user object.
+               # If not given, retrieve from the user object.
                if ( $nickname === false )
                        $nickname = $user->getOption( 'nickname' );
 
-               if ( is_null( $fancySig ) )
+               if ( is_null( $fancySig ) ) {
                        $fancySig = $user->getBoolOption( 'fancysig' );
+               }
 
                $nickname = $nickname == null ? $username : $nickname;
 
-               if( mb_strlen( $nickname ) > $wgMaxSigChars ) {
+               if ( mb_strlen( $nickname ) > $wgMaxSigChars ) {
                        $nickname = $username;
                        wfDebug( __METHOD__ . ": $username has overlong signature.\n" );
-               } elseif( $fancySig !== false ) {
+               } elseif ( $fancySig !== false ) {
                        # Sig. might contain markup; validate this
-                       if( $this->validateSig( $nickname ) !== false ) {
+                       if ( $this->validateSig( $nickname ) !== false ) {
                                # Validated; clean up (if needed) and return it
                                return $this->cleanSig( $nickname, true );
                        } else {
@@ -4114,7 +4196,7 @@ class Parser
                        }
                }
 
-               // Make sure nickname doesnt get a sig in a sig
+               # Make sure nickname doesnt get a sig in a sig
                $nickname = $this->cleanSigInSig( $nickname );
 
                # If we're still here, make it a link to the user page
@@ -4130,7 +4212,7 @@ class Parser
        /**
         * Check that the user's signature contains no bad XML
         *
-        * @param string $text
+        * @param $text String
         * @return mixed An expanded string, or false if invalid.
         */
        function validateSig( $text ) {
@@ -4143,16 +4225,16 @@ class Parser
         * 1) Strip ~~~, ~~~~ and ~~~~~ out of signatures @see cleanSigInSig
         * 2) Substitute all transclusions
         *
-        * @param string $text
+        * @param $text String
         * @param $parsing Whether we're cleaning (preferences save) or parsing
-        * @return string Signature text
+        * @return String: signature text
         */
        function cleanSig( $text, $parsing = false ) {
                if ( !$parsing ) {
                        global $wgTitle;
+                       $this->mOptions = new ParserOptions;
                        $this->clearState();
                        $this->setTitle( $wgTitle );
-                       $this->mOptions = new ParserOptions;
                        $this->setOutputType = self::OT_PREPROCESS;
                }
 
@@ -4182,8 +4264,9 @@ class Parser
 
        /**
         * Strip ~~~, ~~~~ and ~~~~~ out of signatures
-        * @param string $text
-        * @return string Signature text with /~{3,5}/ removed
+        *
+        * @param $text String
+        * @return String: signature text with /~{3,5}/ removed
         */
        function cleanSigInSig( $text ) {
                $text = preg_replace( '/~{3,5}/', '', $text );
@@ -4193,10 +4276,10 @@ class Parser
        /**
         * Set up some variables which are usually set up in parse()
         * so that an external function can call some class members with confidence
-        * @public
         */
-       function startExternalParse( &$title, $options, $outputType, $clearState = true ) {
+       public function startExternalParse( &$title, $options, $outputType, $clearState = true ) {
                $this->setTitle( $title );
+               $options->resetUsage();
                $this->mOptions = $options;
                $this->setOutputType( $outputType );
                if ( $clearState ) {
@@ -4207,12 +4290,11 @@ class Parser
        /**
         * Wrapper for preprocess()
         *
-        * @param string $text the text to preprocess
-        * @param ParserOptions $options  options
-        * @return string
-        * @public
+        * @param $text String: the text to preprocess
+        * @param $options ParserOptions: options
+        * @return String
         */
-       function transformMsg( $text, $options ) {
+       public function transformMsg( $text, $options ) {
                global $wgTitle;
                static $executing = false;
 
@@ -4222,43 +4304,38 @@ class Parser
                }
                $executing = true;
 
-               wfProfileIn(__METHOD__);
+               wfProfileIn( __METHOD__ );
                $text = $this->preprocess( $text, $wgTitle, $options );
 
                $executing = false;
-               wfProfileOut(__METHOD__);
+               wfProfileOut( __METHOD__ );
                return $text;
        }
 
        /**
         * Create an HTML-style tag, e.g. <yourtag>special text</yourtag>
         * The callback should have the following form:
-        *    function myParserHook( $text, $params, &$parser ) { ... }
+        *    function myParserHook( $text, $params, $parser ) { ... }
         *
         * Transform and return $text. Use $parser for any required context, e.g. use
         * $parser->getTitle() and $parser->getOptions() not $wgTitle or $wgOut->mParserOptions
         *
-        * @public
-        *
-        * @param mixed $tag The tag to use, e.g. 'hook' for <hook>
-        * @param mixed $callback The callback function (and object) to use for the tag
-        *
+        * @param $tag Mixed: the tag to use, e.g. 'hook' for <hook>
+        * @param $callback Mixed: the callback function (and object) to use for the tag
         * @return The old value of the mTagHooks array associated with the hook
         */
-       function setHook( $tag, $callback ) {
+       public function setHook( $tag, $callback ) {
                $tag = strtolower( $tag );
                $oldVal = isset( $this->mTagHooks[$tag] ) ? $this->mTagHooks[$tag] : null;
                $this->mTagHooks[$tag] = $callback;
-               if( !in_array( $tag, $this->mStripList ) ) {
+               if ( !in_array( $tag, $this->mStripList ) ) {
                        $this->mStripList[] = $tag;
                }
 
                return $oldVal;
        }
 
-       /* An old work-around for bug 2257 - deprecated 2010-02-13 */
        function setTransparentTagHook( $tag, $callback ) {
-               wfDeprecated( __METHOD__ );
                $tag = strtolower( $tag );
                $oldVal = isset( $this->mTransparentTagHooks[$tag] ) ? $this->mTransparentTagHooks[$tag] : null;
                $this->mTransparentTagHooks[$tag] = $callback;
@@ -4290,11 +4367,9 @@ class Parser
         *   nowiki                    Wiki markup in the return value should be escaped
         *   isHTML                    The returned text is HTML, armour it against wikitext transformation
         *
-        * @public
-        *
-        * @param string $id The magic word ID
-        * @param mixed $callback The callback function (and object) to use
-        * @param integer $flags a combination of the following flags:
+        * @param $id String: The magic word ID
+        * @param $callback Mixed: the callback function (and object) to use
+        * @param $flags Integer: a combination of the following flags:
         *     SFH_NO_HASH   No leading hash, i.e. {{plural:...}} instead of {{#if:...}}
         *
         *     SFH_OBJECT_ARGS   Pass the template arguments as PPNode objects instead of text. This
@@ -4318,7 +4393,7 @@ class Parser
         *
         * @return The old callback function for this name, if any
         */
-       function setFunctionHook( $id, $callback, $flags = 0 ) {
+       public function setFunctionHook( $id, $callback, $flags = 0 ) {
                global $wgContLang;
 
                $oldVal = isset( $this->mFunctionHooks[$id] ) ? $this->mFunctionHooks[$id][0] : null;
@@ -4326,7 +4401,7 @@ class Parser
 
                # Add to function cache
                $mw = MagicWord::get( $id );
-               if( !$mw )
+               if ( !$mw )
                        throw new MWException( __METHOD__.'() expecting a magic word identifier.' );
 
                $synonyms = $mw->getSynonyms();
@@ -4353,7 +4428,7 @@ class Parser
        /**
         * Get all registered function hook identifiers
         *
-        * @return array
+        * @return Array
         */
        function getFunctionHooks() {
                return array_keys( $this->mFunctionHooks );
@@ -4370,7 +4445,7 @@ class Parser
                        $this->mFunctionTagHooks[$tag] : null;
                $this->mFunctionTagHooks[$tag] = array( $callback, $flags );
 
-               if( !in_array( $tag, $this->mStripList ) ) {
+               if ( !in_array( $tag, $this->mStripList ) ) {
                        $this->mStripList[] = $tag;
                }
 
@@ -4390,8 +4465,9 @@ class Parser
        /**
         * Replace <!--LINK--> link placeholders with plain text of links
         * (not HTML-formatted).
-        * @param string $text
-        * @return string
+        *
+        * @param $text String
+        * @return String
         */
        function replaceLinkHoldersText( $text ) {
                return $this->mLinkHolders->replaceText( $text );
@@ -4414,22 +4490,27 @@ class Parser
                $ig->setParser( $this );
                $ig->setHideBadImages();
                $ig->setAttributes( Sanitizer::validateTagAttributes( $params, 'table' ) );
-               $ig->useSkin( $this->mOptions->getSkin() );
+               $ig->useSkin( $this->mOptions->getSkin( $this->mTitle ) );
                $ig->mRevisionId = $this->mRevisionId;
 
-               if( isset( $params['caption'] ) ) {
+               if ( isset( $params['showfilename'] ) ) {
+                       $ig->setShowFilename( true );
+               } else {
+                       $ig->setShowFilename( false );
+               }
+               if ( isset( $params['caption'] ) ) {
                        $caption = $params['caption'];
                        $caption = htmlspecialchars( $caption );
                        $caption = $this->replaceInternalLinks( $caption );
                        $ig->setCaptionHtml( $caption );
                }
-               if( isset( $params['perrow'] ) ) {
+               if ( isset( $params['perrow'] ) ) {
                        $ig->setPerRow( $params['perrow'] );
                }
-               if( isset( $params['widths'] ) ) {
+               if ( isset( $params['widths'] ) ) {
                        $ig->setWidths( $params['widths'] );
                }
-               if( isset( $params['heights'] ) ) {
+               if ( isset( $params['heights'] ) ) {
                        $ig->setHeights( $params['heights'] );
                }
 
@@ -4446,11 +4527,12 @@ class Parser
                                continue;
                        }
 
-                       if ( strpos( $matches[0], '%' ) !== false )
-                               $matches[1] = urldecode( $matches[1] );
-                       $tp = Title::newFromText( $matches[1]/*, NS_FILE*/ );
+                       if ( strpos( $matches[0], '%' ) !== false ) {
+                               $matches[1] = rawurldecode( $matches[1] );
+                       }
+                       $tp = Title::newFromText( $matches[1] );
                        $nt =& $tp;
-                       if( is_null( $nt ) ) {
+                       if ( is_null( $nt ) ) {
                                # Bogus title. Ignore these so we don't bomb out later.
                                continue;
                        }
@@ -4479,7 +4561,7 @@ class Parser
                        $handlerClass = '';
                }
                if ( !isset( $this->mImageParams[$handlerClass]  ) ) {
-                       // Initialise static lists
+                       # Initialise static lists
                        static $internalParamNames = array(
                                'horizAlign' => array( 'left', 'right', 'center', 'none' ),
                                'vertAlign' => array( 'baseline', 'sub', 'super', 'top', 'text-top', 'middle',
@@ -4498,7 +4580,7 @@ class Parser
                                }
                        }
 
-                       // Add handler params
+                       # Add handler params
                        $paramMap = $internalParamMap;
                        if ( $handler ) {
                                $handlerParamMap = $handler->getParamMap();
@@ -4514,9 +4596,10 @@ class Parser
 
        /**
         * Parse image options text and use it to make an image
-        * @param Title $title
-        * @param string $options
-        * @param LinkHolderArray $holders
+        *
+        * @param $title Title
+        * @param $options String
+        * @param $holders LinkHolderArray
         */
        function makeImage( $title, $options, $holders = false ) {
                # Check if the options text is of the form "options|alt text"
@@ -4545,7 +4628,7 @@ class Parser
                #  * text-bottom
 
                $parts = StringUtils::explode( "|", $options );
-               $sk = $this->mOptions->getSkin();
+               $sk = $this->mOptions->getSkin( $this->mTitle );
 
                # Give extensions a chance to select the file revision for us
                $skip = $time = $descQuery = false;
@@ -4556,7 +4639,6 @@ class Parser
                }
 
                # Get the file
-               $imagename = $title->getDBkey();
                $file = wfFindFile( $title, array( 'time' => $time ) );
                # Get parameter map
                $handler = $file ? $file->getHandler() : false;
@@ -4567,15 +4649,15 @@ class Parser
                $caption = '';
                $params = array( 'frame' => array(), 'handler' => array(),
                        'horizAlign' => array(), 'vertAlign' => array() );
-               foreach( $parts as $part ) {
+               foreach ( $parts as $part ) {
                        $part = trim( $part );
                        list( $magicName, $value ) = $mwArray->matchVariableStartToEnd( $part );
                        $validated = false;
-                       if( isset( $paramMap[$magicName] ) ) {
+                       if ( isset( $paramMap[$magicName] ) ) {
                                list( $type, $paramName ) = $paramMap[$magicName];
 
-                               // Special case; width and height come in one variable together
-                               if( $type === 'handler' && $paramName === 'width' ) {
+                               # Special case; width and height come in one variable together
+                               if ( $type === 'handler' && $paramName === 'width' ) {
                                        $m = array();
                                        # (bug 13500) In both cases (width/height and width only),
                                        # permit trailing "px" for backward compatibility.
@@ -4596,7 +4678,7 @@ class Parser
                                                        $params[$type]['width'] = $width;
                                                        $validated = true;
                                                }
-                                       } // else no validation -- bug 13436
+                                       } # else no validation -- bug 13436
                                } else {
                                        if ( $type === 'handler' ) {
                                                # Validate handler parameter
@@ -4606,9 +4688,9 @@ class Parser
                                                switch( $paramName ) {
                                                case 'manualthumb':
                                                case 'alt':
-                                                       // @todo Fixme: possibly check validity here for
-                                                       // manualthumb? downstream behavior seems odd with
-                                                       // missing manual thumbs.
+                                                       # @todo Fixme: possibly check validity here for
+                                                       # manualthumb? downstream behavior seems odd with
+                                                       # missing manual thumbs.
                                                        $validated = true;
                                                        $value = $this->stripAltText( $value, $holders );
                                                        break;
@@ -4623,6 +4705,9 @@ class Parser
                                                                if ( preg_match( "/^($prots)$chars+$/", $value, $m ) ) {
                                                                        $paramName = 'link-url';
                                                                        $this->mOutput->addExternalLink( $value );
+                                                                       if ( $this->mOptions->getExternalLinkTarget() ) {
+                                                                               $params[$type]['link-target'] = $this->mOptions->getExternalLinkTarget();
+                                                                       }
                                                                        $validated = true;
                                                                }
                                                        } else {
@@ -4636,7 +4721,7 @@ class Parser
                                                        }
                                                        break;
                                                default:
-                                                       // Most other things appear to be empty or numeric...
+                                                       # Most other things appear to be empty or numeric...
                                                        $validated = ( $value === false || is_numeric( trim( $value ) ) );
                                                }
                                        }
@@ -4663,9 +4748,9 @@ class Parser
 
                # Will the image be presented in a frame, with the caption below?
                $imageIsFramed = isset( $params['frame']['frame'] ) ||
-                                isset( $params['frame']['framed'] ) ||
-                                isset( $params['frame']['thumbnail'] ) ||
-                                isset( $params['frame']['manualthumb'] );
+                                                isset( $params['frame']['framed'] ) ||
+                                                isset( $params['frame']['thumbnail'] ) ||
+                                                isset( $params['frame']['manualthumb'] );
 
                # In the old days, [[Image:Foo|text...]] would set alt text.  Later it
                # came to also set the caption, ordinary text after the image -- which
@@ -4709,7 +4794,7 @@ class Parser
                wfRunHooks( 'ParserMakeImageParams', array( $title, $file, &$params ) );
 
                # Linker does the rest
-               $ret = $sk->makeImageLink2( $title, $file, $params['frame'], $params['handler'], $time, $descQuery );
+               $ret = $sk->makeImageLink2( $title, $file, $params['frame'], $params['handler'], $time, $descQuery, $this->mOptions->getThumbSize() );
 
                # Give the handler a chance to modify the parser object
                if ( $handler ) {
@@ -4744,15 +4829,17 @@ class Parser
         */
        function disableCache() {
                wfDebug( "Parser output marked as uncacheable.\n" );
-               $this->mOutput->mCacheTime = -1;
+               $this->mOutput->setCacheTime( -1 ); // old style, for compatibility
+               $this->mOutput->updateCacheExpiry( 0 ); // new style, for consistency
        }
 
-       /**#@+
+       /**
         * Callback from the Sanitizer for expanding items found in HTML attribute
         * values, so they can be safely tested and escaped.
-        * @param string $text
-        * @param PPFrame $frame
-        * @return string
+        *
+        * @param $text String
+        * @param $frame PPFrame
+        * @return String
         * @private
         */
        function attributeStripCallback( &$text, $frame = false ) {
@@ -4761,22 +4848,12 @@ class Parser
                return $text;
        }
 
-       /**#@-*/
-
-       /**#@+
-        * Accessor/mutator
-        */
-       function Title( $x = null ) { return wfSetVar( $this->mTitle, $x ); }
-       function Options( $x = null ) { return wfSetVar( $this->mOptions, $x ); }
-       function OutputType( $x = null ) { return wfSetVar( $this->mOutputType, $x ); }
-       /**#@-*/
-
-       /**#@+
+       /**
         * Accessor
         */
-       function getTags() { return array_merge( array_keys($this->mTransparentTagHooks), array_keys( $this->mTagHooks ) ); }
-       /**#@-*/
-
+       function getTags() {
+               return array_merge( array_keys( $this->mTransparentTagHooks ), array_keys( $this->mTagHooks ) );
+       }
 
        /**
         * Break wikitext input into sections, and either pull or replace
@@ -4784,8 +4861,8 @@ class Parser
         *
         * External callers should use the getSection and replaceSection methods.
         *
-        * @param string $text Page wikitext
-        * @param string $section A section identifier string of the form:
+        * @param $text String: Page wikitext
+        * @param $section String: a section identifier string of the form:
         *   <flag1> - <flag2> - ... - <section number>
         *
         * Currently the only recognised flag is "T", which means the target section number
@@ -4798,21 +4875,21 @@ class Parser
         * pull the given section along with its lower-level subsections. If the section is
         * not found, $mode=get will return $newtext, and $mode=replace will return $text.
         *
-        * @param string $mode One of "get" or "replace"
-        * @param string $newText Replacement text for section data.
-        * @return string for "get", the extracted section text.
-        *                for "replace", the whole page with the section replaced.
+        * @param $mode String: one of "get" or "replace"
+        * @param $newText String: replacement text for section data.
+        * @return String: for "get", the extracted section text.
+        *                 for "replace", the whole page with the section replaced.
         */
        private function extractSections( $text, $section, $mode, $newText='' ) {
                global $wgTitle;
-               $this->clearState();
-               $this->setTitle( $wgTitle ); // not generally used but removes an ugly failure mode
                $this->mOptions = new ParserOptions;
-               $this->setOutputType( self::OT_WIKI );
+               $this->clearState();
+               $this->setTitle( $wgTitle ); # not generally used but removes an ugly failure mode
+               $this->setOutputType( self::OT_PLAIN );
                $outText = '';
                $frame = $this->getPreprocessor()->newFrame();
 
-               // Process section extraction flags
+               # Process section extraction flags
                $flags = 0;
                $sectionParts = explode( '-', $section );
                $sectionIndex = array_pop( $sectionParts );
@@ -4821,23 +4898,23 @@ class Parser
                                $flags |= self::PTD_FOR_INCLUSION;
                        }
                }
-               // Preprocess the text
+               # Preprocess the text
                $root = $this->preprocessToDom( $text, $flags );
 
-               // <h> nodes indicate section breaks
-               // They can only occur at the top level, so we can find them by iterating the root's children
+               # <h> nodes indicate section breaks
+               # They can only occur at the top level, so we can find them by iterating the root's children
                $node = $root->getFirstChild();
 
-               // Find the target section
+               # Find the target section
                if ( $sectionIndex == 0 ) {
-                       // Section zero doesn't nest, level=big
+                       # Section zero doesn't nest, level=big
                        $targetLevel = 1000;
                } else {
-            while ( $node ) {
-                if ( $node->getName() === 'h' ) {
-                    $bits = $node->splitHeading();
+                       while ( $node ) {
+                               if ( $node->getName() === 'h' ) {
+                                       $bits = $node->splitHeading();
                                        if ( $bits['i'] == $sectionIndex ) {
-                                       $targetLevel = $bits['level'];
+                                               $targetLevel = $bits['level'];
                                                break;
                                        }
                                }
@@ -4849,7 +4926,7 @@ class Parser
                }
 
                if ( !$node ) {
-                       // Not found
+                       # Not found
                        if ( $mode === 'get' ) {
                                return $newText;
                        } else {
@@ -4857,7 +4934,7 @@ class Parser
                        }
                }
 
-               // Find the end of the section, including nested sections
+               # Find the end of the section, including nested sections
                do {
                        if ( $node->getName() === 'h' ) {
                                $bits = $node->splitHeading();
@@ -4872,13 +4949,13 @@ class Parser
                        $node = $node->getNextSibling();
                } while ( $node );
 
-               // Write out the remainder (in replace mode only)
+               # Write out the remainder (in replace mode only)
                if ( $mode === 'replace' ) {
-                       // Output the replacement text
-                       // Add two newlines on -- trailing whitespace in $newText is conventionally
-                       // stripped by the editor, so we need both newlines to restore the paragraph gap
-                       // Only add trailing whitespace if there is newText
-                       if($newText != "") {
+                       # Output the replacement text
+                       # Add two newlines on -- trailing whitespace in $newText is conventionally
+                       # stripped by the editor, so we need both newlines to restore the paragraph gap
+                       # Only add trailing whitespace if there is newText
+                       if ( $newText != "" ) {
                                $outText .= $newText . "\n\n";
                        }
 
@@ -4889,7 +4966,7 @@ class Parser
                }
 
                if ( is_string( $outText ) ) {
-                       // Re-insert stripped tags
+                       # Re-insert stripped tags
                        $outText = rtrim( $this->mStripState->unstripBoth( $outText ) );
                }
 
@@ -4903,19 +4980,54 @@ class Parser
         *
         * If a section contains subsections, these are also returned.
         *
-        * @param string $text text to look in
-        * @param string $section section identifier
-        * @param string $deftext default to return if section is not found
+        * @param $text String: text to look in
+        * @param $section String: section identifier
+        * @param $deftext String: default to return if section is not found
         * @return string text of the requested section
         */
        public function getSection( $text, $section, $deftext='' ) {
                return $this->extractSections( $text, $section, "get", $deftext );
        }
 
+       /**
+        * This function returns $oldtext after the content of the section 
+        * specified by $section has been replaced with $text.
+        * 
+        * @param $text String: former text of the article
+        * @param $section Numeric: section identifier
+        * @param $text String: replacing text
+        * #return String: modified text
+        */
        public function replaceSection( $oldtext, $section, $text ) {
                return $this->extractSections( $oldtext, $section, "replace", $text );
        }
 
+       /**
+        * Get the ID of the revision we are parsing
+        *
+        * @return Mixed: integer or null
+        */
+       function getRevisionId() {
+               return $this->mRevisionId;
+       }
+
+       /**
+        * Get the revision object for $this->mRevisionId
+        *
+        * @return either a Revision object or null
+        */
+       protected function getRevisionObject() {
+               if ( !is_null( $this->mRevisionObject ) ) {
+                       return $this->mRevisionObject;
+               }
+               if ( is_null( $this->mRevisionId ) ) {
+                       return null;
+               }
+
+               $this->mRevisionObject = Revision::newFromId( $this->mRevisionId );
+               return $this->mRevisionObject;
+       }
+
        /**
         * Get the timestamp associated with the current revision, adjusted for
         * the default server-local timestamp
@@ -4923,24 +5035,21 @@ class Parser
        function getRevisionTimestamp() {
                if ( is_null( $this->mRevisionTimestamp ) ) {
                        wfProfileIn( __METHOD__ );
-                       global $wgContLang;
-                       $dbr = wfGetDB( DB_SLAVE );
-                       $timestamp = $dbr->selectField( 'revision', 'rev_timestamp',
-                                       array( 'rev_id' => $this->mRevisionId ), __METHOD__ );
-
-                       // Normalize timestamp to internal MW format for timezone processing.
-                       // This has the added side-effect of replacing a null value with
-                       // the current time, which gives us more sensible behavior for
-                       // previews.
-                       $timestamp = wfTimestamp( TS_MW, $timestamp );
-
-                       // The cryptic '' timezone parameter tells to use the site-default
-                       // timezone offset instead of the user settings.
-                       //
-                       // Since this value will be saved into the parser cache, served
-                       // to other users, and potentially even used inside links and such,
-                       // it needs to be consistent for all visitors.
-                       $this->mRevisionTimestamp = $wgContLang->userAdjust( $timestamp, '' );
+
+                       $revObject = $this->getRevisionObject();
+                       $timestamp = $revObject ? $revObject->getTimestamp() : false;
+
+                       if( $timestamp !== false ) {
+                               global $wgContLang;
+
+                               # The cryptic '' timezone parameter tells to use the site-default
+                               # timezone offset instead of the user settings.
+                               #
+                               # Since this value will be saved into the parser cache, served
+                               # to other users, and potentially even used inside links and such,
+                               # it needs to be consistent for all visitors.
+                               $this->mRevisionTimestamp = $wgContLang->userAdjust( $timestamp, '' );
+                       }
 
                        wfProfileOut( __METHOD__ );
                }
@@ -4949,18 +5058,22 @@ class Parser
 
        /**
         * Get the name of the user that edited the last revision
+        *
+        * @return String: user name
         */
        function getRevisionUser() {
-               // if this template is subst: the revision id will be blank,
-               // so just use the current user's name
-               if( $this->mRevisionId ) {
-                       $revision = Revision::newFromId( $this->mRevisionId );
-                       $revuser = $revision->getUserText();
-               } else {
-                       global $wgUser;
-                       $revuser = $wgUser->getName();
+               if( is_null( $this->mRevisionUser ) ) {
+                       $revObject = $this->getRevisionObject();
+
+                       # if this template is subst: the revision id will be blank,
+                       # so just use the current user's name
+                       if( $revObject ) {
+                               $this->mRevisionUser = $revObject->getUserText();
+                       } elseif( $this->ot['wiki'] || $this->mOptions->getIsPreview() ) {
+                               $this->mRevisionUser = $this->getUser()->getName();
+                       }
                }
-               return $revuser;
+               return $this->mRevisionUser;
        }
 
        /**
@@ -4970,6 +5083,7 @@ class Parser
         */
        public function setDefaultSort( $sort ) {
                $this->mDefaultSort = $sort;
+               $this->mOutput->setProperty( 'defaultsort', $sort );
        }
 
        /**
@@ -4979,14 +5093,10 @@ class Parser
         * @return string
         */
        public function getDefaultSort() {
-               global $wgCategoryPrefixedDefaultSortkey;
-               if( $this->mDefaultSort !== false ) {
+               if ( $this->mDefaultSort !== false ) {
                        return $this->mDefaultSort;
-               } elseif ($this->mTitle->getNamespace() == NS_CATEGORY ||
-                       !$wgCategoryPrefixedDefaultSortkey) {
-                       return $this->mTitle->getText();
                } else {
-                       return $this->mTitle->getPrefixedText();
+                       return $this->mTitle->getCategorySortkey();
                }
        }
 
@@ -5008,19 +5118,23 @@ class Parser
        public function guessSectionNameFromWikiText( $text ) {
                # Strip out wikitext links(they break the anchor)
                $text = $this->stripSectionName( $text );
-               $headline = Sanitizer::decodeCharReferences( $text );
-               # strip out HTML
-               $headline = StringUtils::delimiterReplace( '<', '>', '', $headline );
-               $headline = trim( $headline );
-               $sectionanchor = '#' . urlencode( str_replace( ' ', '_', $headline ) );
-               $replacearray = array(
-                       '%3A' => ':',
-                       '%' => '.'
-               );
-               return str_replace(
-                       array_keys( $replacearray ),
-                       array_values( $replacearray ),
-                       $sectionanchor );
+               $text = Sanitizer::normalizeSectionNameWhitespace( $text );
+               return '#' . Sanitizer::escapeId( $text, 'noninitial' );
+       }
+
+       /**
+        * Same as guessSectionNameFromWikiText(), but produces legacy anchors
+        * instead.  For use in redirects, since IE6 interprets Redirect: headers
+        * as something other than UTF-8 (apparently?), resulting in breakage.
+        *
+        * @param $text String: The section name
+        * @return string An anchor
+        */
+       public function guessLegacySectionNameFromWikiText( $text ) {
+               # Strip out wikitext links(they break the anchor)
+               $text = $this->stripSectionName( $text );
+               $text = Sanitizer::normalizeSectionNameWhitespace( $text );
+               return '#' . Sanitizer::escapeId( $text, array( 'noninitial', 'legacy' ) );
        }
 
        /**
@@ -5033,42 +5147,39 @@ class Parser
         * to create valid section anchors by mimicing the output of the
         * parser when headings are parsed.
         *
-        * @param $text string Text string to be stripped of wikitext
+        * @param $text String: text string to be stripped of wikitext
         * for use in a Section anchor
         * @return Filtered text string
         */
        public function stripSectionName( $text ) {
                # Strip internal link markup
-               $text = preg_replace('/\[\[:?([^[|]+)\|([^[]+)\]\]/','$2',$text);
-               $text = preg_replace('/\[\[:?([^[]+)\|?\]\]/','$1',$text);
+               $text = preg_replace( '/\[\[:?([^[|]+)\|([^[]+)\]\]/', '$2', $text );
+               $text = preg_replace( '/\[\[:?([^[]+)\|?\]\]/', '$1', $text );
 
                # Strip external link markup (FIXME: Not Tolerant to blank link text
                # I.E. [http://www.mediawiki.org] will render as [1] or something depending
                # on how many empty links there are on the page - need to figure that out.
-               $text = preg_replace('/\[(?:' . wfUrlProtocols() . ')([^ ]+?) ([^[]+)\]/','$2',$text);
+               $text = preg_replace( '/\[(?:' . wfUrlProtocols() . ')([^ ]+?) ([^[]+)\]/', '$2', $text );
 
                # Parse wikitext quotes (italics & bold)
-               $text = $this->doQuotes($text);
+               $text = $this->doQuotes( $text );
 
                # Strip HTML tags
                $text = StringUtils::delimiterReplace( '<', '>', '', $text );
                return $text;
        }
 
-       function srvus( $text ) {
-               return $this->testSrvus( $text, $this->mOutputType );
-       }
-
        /**
         * strip/replaceVariables/unstrip for preprocessor regression testing
         */
        function testSrvus( $text, $title, $options, $outputType = self::OT_HTML ) {
+               $this->mOptions = $options;
                $this->clearState();
-               if ( ! ( $title instanceof Title ) ) {
+               if ( !$title instanceof Title ) {
                        $title = Title::newFromText( $title );
                }
                $this->mTitle = $title;
-               $this->mOptions = $options;
+               $options->resetUsage();
                $this->setOutputType( $outputType );
                $text = $this->replaceVariables( $text );
                $text = $this->mStripState->unstripBoth( $text );
@@ -5078,14 +5189,14 @@ class Parser
 
        function testPst( $text, $title, $options ) {
                global $wgUser;
-               if ( ! ( $title instanceof Title ) ) {
+               if ( !$title instanceof Title ) {
                        $title = Title::newFromText( $title );
                }
                return $this->preSaveTransform( $text, $title, $wgUser, $options );
        }
 
        function testPreprocess( $text, $title, $options ) {
-               if ( ! ( $title instanceof Title ) ) {
+               if ( !$title instanceof Title ) {
                        $title = Title::newFromText( $title );
                }
                return $this->testSrvus( $text, $title, $options, self::OT_PREPROCESS );
@@ -5119,11 +5230,13 @@ class Parser
                $data = array();
                $data['text'] = $text;
 
-               // First, find all strip markers, and store their
-               //  data in an array.
+               # First, find all strip markers, and store their
+               #  data in an array.
                $stripState = new StripState;
                $pos = 0;
-               while( ( $start_pos = strpos( $text, $this->mUniqPrefix, $pos ) ) && ( $end_pos = strpos( $text, self::MARKER_SUFFIX, $pos ) ) ) {
+               while ( ( $start_pos = strpos( $text, $this->mUniqPrefix, $pos ) )
+                       && ( $end_pos = strpos( $text, self::MARKER_SUFFIX, $pos ) ) )
+               {
                        $end_pos += strlen( self::MARKER_SUFFIX );
                        $marker = substr( $text, $start_pos, $end_pos-$start_pos );
 
@@ -5142,17 +5255,17 @@ class Parser
                }
                $data['stripstate'] = $stripState;
 
-               // Now, find all of our links, and store THEIR
-               //  data in an array! :)
+               # Now, find all of our links, and store THEIR
+               #  data in an array! :)
                $links = array( 'internal' => array(), 'interwiki' => array() );
                $pos = 0;
 
-               // Internal links
-               while( ( $start_pos = strpos( $text, '<!--LINK ', $pos ) ) ) {
+               # Internal links
+               while ( ( $start_pos = strpos( $text, '<!--LINK ', $pos ) ) ) {
                        list( $ns, $trail ) = explode( ':', substr( $text, $start_pos + strlen( '<!--LINK ' ) ), 2 );
 
-                       $ns = trim($ns);
-                       if (empty( $links['internal'][$ns] )) {
+                       $ns = trim( $ns );
+                       if ( empty( $links['internal'][$ns] ) ) {
                                $links['internal'][$ns] = array();
                        }
 
@@ -5163,8 +5276,8 @@ class Parser
 
                $pos = 0;
 
-               // Interwiki links
-               while( ( $start_pos = strpos( $text, '<!--IWLINK ', $pos ) ) ) {
+               # Interwiki links
+               while ( ( $start_pos = strpos( $text, '<!--IWLINK ', $pos ) ) ) {
                        $data = substr( $text, $start_pos );
                        $key = trim( substr( $data, 0, strpos( $data, '-->' ) ) );
                        $links['interwiki'][] = $this->mLinkHolders->interwiki[$key];
@@ -5176,22 +5289,29 @@ class Parser
                return $data;
        }
 
-       function unserialiseHalfParsedText( $data, $intPrefix = null /* Unique identifying prefix */ ) {
-               if (!$intPrefix)
-                       $intPrefix = $this->getRandomString();
+       /**
+        * TODO: document
+        * @param $data Array
+        * @param $intPrefix String unique identifying prefix
+        * @return String
+        */
+       function unserialiseHalfParsedText( $data, $intPrefix = null ) {
+               if ( !$intPrefix ) {
+                       $intPrefix = self::getRandomString();
+               }
 
-               // First, extract the strip state.
+               # First, extract the strip state.
                $stripState = $data['stripstate'];
                $this->mStripState->general->merge( $stripState->general );
                $this->mStripState->nowiki->merge( $stripState->nowiki );
 
-               // Now, extract the text, and renumber links
+               # Now, extract the text, and renumber links
                $text = $data['text'];
                $links = $data['linkholder'];
 
-               // Internal...
-               foreach( $links['internal'] as $ns => $nsLinks ) {
-                       foreach( $nsLinks as $key => $entry ) {
+               # Internal...
+               foreach ( $links['internal'] as $ns => $nsLinks ) {
+                       foreach ( $nsLinks as $key => $entry ) {
                                $newKey = $intPrefix . '-' . $key;
                                $this->mLinkHolders->internals[$ns][$newKey] = $entry;
 
@@ -5199,15 +5319,15 @@ class Parser
                        }
                }
 
-               // Interwiki...
-               foreach( $links['interwiki'] as $key => $entry ) {
+               # Interwiki...
+               foreach ( $links['interwiki'] as $key => $entry ) {
                        $newKey = "$intPrefix-$key";
                        $this->mLinkHolders->interwikis[$newKey] = $entry;
 
                        $text = str_replace( "<!--IWLINK $key-->", "<!--IWLINK $newKey-->", $text );
                }
 
-               // Should be good to go.
+               # Should be good to go.
                return $text;
        }
 }