mediawiki.action.edit.editWarning: Reuse jQuery collections
[lhc/web/wiklou.git] / includes / OutputPage.php
index 8542904..76f0aab 100644 (file)
@@ -252,17 +252,17 @@ class OutputPage extends ContextSource {
        private $mProperties = array();
 
        /**
-        * @var string|null: ResourceLoader target for load.php links. If null, will be omitted
+        * @var string|null ResourceLoader target for load.php links. If null, will be omitted
         */
        private $mTarget = null;
 
        /**
-        * @var bool: Whether parser output should contain table of contents
+        * @var bool Whether parser output should contain table of contents
         */
        private $mEnableTOC = true;
 
        /**
-        * @var bool: Whether parser output should contain section edit links
+        * @var bool Whether parser output should contain section edit links
         */
        private $mEnableSectionEditLinks = true;
 
@@ -295,7 +295,7 @@ class OutputPage extends ContextSource {
        /**
         * Get the URL to redirect to, or an empty string if not redirect URL set
         *
-        * @return String
+        * @return string
         */
        public function getRedirect() {
                return $this->mRedirect;
@@ -304,7 +304,7 @@ class OutputPage extends ContextSource {
        /**
         * Set the HTTP status code to send with the output.
         *
-        * @param $statusCode Integer
+        * @param int $statusCode
         */
        public function setStatusCode( $statusCode ) {
                $this->mStatusCode = $statusCode;
@@ -355,7 +355,7 @@ class OutputPage extends ContextSource {
        /**
         * Get the value of the "rel" attribute for metadata links
         *
-        * @return String
+        * @return string
         */
        public function getMetadataAttribute() {
                # note: buggy CC software only reads first "meta" link
@@ -392,7 +392,7 @@ class OutputPage extends ContextSource {
        /**
         * Get all styles added by extensions
         *
-        * @return Array
+        * @return array
         */
        function getExtStyle() {
                return $this->mExtStyles;
@@ -431,7 +431,7 @@ class OutputPage extends ContextSource {
        /**
         * Get all registered JS and CSS tags for the header.
         *
-        * @return String
+        * @return string
         */
        function getScript() {
                return $this->mScripts . $this->getHeadItems();
@@ -440,10 +440,10 @@ class OutputPage extends ContextSource {
        /**
         * Filter an array of modules to remove insufficiently trustworthy members, and modules
         * which are no longer registered (eg a page is cached before an extension is disabled)
-        * @param $modules Array
-        * @param string $position if not null, only return modules with this position
-        * @param $type string
-        * @return Array
+        * @param array $modules
+        * @param string|null $position if not null, only return modules with this position
+        * @param string $type
+        * @return array
         */
        protected function filterModules( $modules, $position = null, $type = ResourceLoaderModule::TYPE_COMBINED ) {
                $resourceLoader = $this->getResourceLoader();
@@ -464,10 +464,10 @@ class OutputPage extends ContextSource {
        /**
         * Get the list of modules to include on this page
         *
-        * @param bool $filter whether to filter out insufficiently trustworthy modules
-        * @param string $position if not null, only return modules with this position
-        * @param $param string
-        * @return Array of module names
+        * @param bool $filter Whether to filter out insufficiently trustworthy modules
+        * @param string|null $position If not null, only return modules with this position
+        * @param string $param
+        * @return array Array of module names
         */
        public function getModules( $filter = false, $position = null, $param = 'mModules' ) {
                $modules = array_values( array_unique( $this->$param ) );
@@ -481,7 +481,7 @@ class OutputPage extends ContextSource {
         * through this function will be loaded by the resource loader when the
         * page loads.
         *
-        * @param $modules Mixed: module name (string) or array of module names
+        * @param string|array $modules Module name (string) or array of module names
         */
        public function addModules( $modules ) {
                $this->mModules = array_merge( $this->mModules, (array)$modules );
@@ -490,10 +490,10 @@ class OutputPage extends ContextSource {
        /**
         * Get the list of module JS to include on this page
         *
-        * @param $filter
-        * @param $position
+        * @param bool $filter
+        * @param string|null $position
         *
-        * @return array of module names
+        * @return array Array of module names
         */
        public function getModuleScripts( $filter = false, $position = null ) {
                return $this->getModules( $filter, $position, 'mModuleScripts' );
@@ -504,7 +504,7 @@ class OutputPage extends ContextSource {
         * scripts added through this function will be loaded by the resource loader when
         * the page loads.
         *
-        * @param $modules Mixed: module name (string) or array of module names
+        * @param string|array $modules Module name (string) or array of module names
         */
        public function addModuleScripts( $modules ) {
                $this->mModuleScripts = array_merge( $this->mModuleScripts, (array)$modules );
@@ -513,10 +513,10 @@ class OutputPage extends ContextSource {
        /**
         * Get the list of module CSS to include on this page
         *
-        * @param $filter
-        * @param $position
+        * @param bool $filter
+        * @param string|null $position
         *
-        * @return Array of module names
+        * @return array Array of module names
         */
        public function getModuleStyles( $filter = false, $position = null ) {
                return $this->getModules( $filter, $position, 'mModuleStyles' );
@@ -529,7 +529,7 @@ class OutputPage extends ContextSource {
         * tags, rather than as a combined Javascript and CSS package. Thus, they will
         * load when JavaScript is disabled (unless CSS also happens to be disabled).
         *
-        * @param $modules Mixed: module name (string) or array of module names
+        * @param string|array $modules Module name (string) or array of module names
         */
        public function addModuleStyles( $modules ) {
                $this->mModuleStyles = array_merge( $this->mModuleStyles, (array)$modules );
@@ -538,10 +538,10 @@ class OutputPage extends ContextSource {
        /**
         * Get the list of module messages to include on this page
         *
-        * @param $filter
-        * @param $position
+        * @param bool $filter
+        * @param string|null $position
         *
-        * @return Array of module names
+        * @return array Array of module names
         */
        public function getModuleMessages( $filter = false, $position = null ) {
                return $this->getModules( $filter, $position, 'mModuleMessages' );
@@ -552,14 +552,14 @@ class OutputPage extends ContextSource {
         * Module messages added through this function will be loaded by the resource
         * loader when the page loads.
         *
-        * @param $modules Mixed: module name (string) or array of module names
+        * @param string|array $modules Module name (string) or array of module names
         */
        public function addModuleMessages( $modules ) {
                $this->mModuleMessages = array_merge( $this->mModuleMessages, (array)$modules );
        }
 
        /**
-        * @return null|string: ResourceLoader target
+        * @return null|string ResourceLoader target
         */
        public function getTarget() {
                return $this->mTarget;
@@ -568,7 +568,7 @@ class OutputPage extends ContextSource {
        /**
         * Sets ResourceLoader target for load.php links. If null, will be omitted
         *
-        * @param $target string|null
+        * @param string|null $target
         */
        public function setTarget( $target ) {
                $this->mTarget = $target;
@@ -577,7 +577,7 @@ class OutputPage extends ContextSource {
        /**
         * Get an array of head items
         *
-        * @return Array
+        * @return array
         */
        function getHeadItemsArray() {
                return $this->mHeadItems;
@@ -586,7 +586,7 @@ class OutputPage extends ContextSource {
        /**
         * Get all header items in a string
         *
-        * @return String
+        * @return string
         */
        function getHeadItems() {
                $s = '';
@@ -609,8 +609,8 @@ class OutputPage extends ContextSource {
        /**
         * Check if the header item $name is already set
         *
-        * @param string $name item name
-        * @return Boolean
+        * @param string $name Item name
+        * @return bool
         */
        public function hasHeadItem( $name ) {
                return isset( $this->mHeadItems[$name] );
@@ -630,7 +630,7 @@ class OutputPage extends ContextSource {
         * without any skin, sidebar, etc.
         * Used e.g. when calling with "action=render".
         *
-        * @param $only Boolean: whether to output only the body of the article
+        * @param bool $only Whether to output only the body of the article
         */
        public function setArticleBodyOnly( $only ) {
                $this->mArticleBodyOnly = $only;
@@ -639,7 +639,7 @@ class OutputPage extends ContextSource {
        /**
         * Return whether the output will contain only the body of the article
         *
-        * @return Boolean
+        * @return bool
         */
        public function getArticleBodyOnly() {
                return $this->mArticleBodyOnly;
@@ -660,8 +660,8 @@ class OutputPage extends ContextSource {
         * Get an additional output property
         * @since 1.21
         *
-        * @param $name
-        * @return mixed: Property value or null if not found
+        * @param string $name
+        * @return mixed Property value or null if not found
         */
        public function getProperty( $name ) {
                if ( isset( $this->mProperties[$name] ) ) {
@@ -678,9 +678,9 @@ class OutputPage extends ContextSource {
         *
         * Side effect: sets mLastModified for Last-Modified header
         *
-        * @param $timestamp string
+        * @param string $timestamp
         *
-        * @return Boolean: true if cache-ok headers was sent.
+        * @return bool True if cache-ok headers was sent.
         */
        public function checkLastModified( $timestamp ) {
                global $wgCachePages, $wgCacheEpoch, $wgUseSquid, $wgSquidMaxage;
@@ -833,7 +833,7 @@ class OutputPage extends ContextSource {
        /**
         * Get the value of the "action text"
         *
-        * @return String
+        * @return string
         */
        public function getPageTitleActionText() {
                if ( isset( $this->mPageTitleActionText ) ) {
@@ -846,7 +846,7 @@ class OutputPage extends ContextSource {
         * "HTML title" means the contents of "<title>".
         * It is stored as plain, unescaped text and will be run through htmlspecialchars in the skin file.
         *
-        * @param $name string
+        * @param string $name
         */
        public function setHTMLTitle( $name ) {
                if ( $name instanceof Message ) {
@@ -859,7 +859,7 @@ class OutputPage extends ContextSource {
        /**
         * Return the "HTML title", i.e. the content of the "<title>" tag.
         *
-        * @return String
+        * @return string
         */
        public function getHTMLTitle() {
                return $this->mHTMLtitle;
@@ -868,7 +868,7 @@ class OutputPage extends ContextSource {
        /**
         * Set $mRedirectedFrom, the Title of the page which redirected us to the current page.
         *
-        * @param $t Title
+        * @param Title $t
         */
        public function setRedirectedFrom( $t ) {
                $this->mRedirectedFrom = $t;
@@ -880,7 +880,7 @@ class OutputPage extends ContextSource {
         * This function automatically sets \<title\> to the same content as \<h1\> but with all tags removed.
         * Bad tags that were escaped in \<h1\> will still be escaped in \<title\>, and good tags like \<i\> will be dropped entirely.
         *
-        * @param $name string|Message
+        * @param string|Message $name
         */
        public function setPageTitle( $name ) {
                if ( $name instanceof Message ) {
@@ -902,7 +902,7 @@ class OutputPage extends ContextSource {
        /**
         * Return the "page title", i.e. the content of the \<h1\> tag.
         *
-        * @return String
+        * @return string
         */
        public function getPageTitle() {
                return $this->mPagetitle;
@@ -911,7 +911,7 @@ class OutputPage extends ContextSource {
        /**
         * Set the Title object to use
         *
-        * @param $t Title object
+        * @param Title $t
         */
        public function setTitle( Title $t ) {
                $this->getContext()->setTitle( $t );
@@ -930,7 +930,7 @@ class OutputPage extends ContextSource {
        /**
         * Add $str to the subtitle
         *
-        * @deprecated in 1.19; use addSubtitle() instead
+        * @deprecated since 1.19; use addSubtitle() instead
         * @param string|Message $str to add to the subtitle
         */
        public function appendSubtitle( $str ) {
@@ -953,7 +953,7 @@ class OutputPage extends ContextSource {
        /**
         * Add a subtitle containing a backlink to a page
         *
-        * @param $title Title to link to
+        * @param Title $title Title to link to
         */
        public function addBacklinkSubtitle( Title $title ) {
                $query = array();
@@ -973,7 +973,7 @@ class OutputPage extends ContextSource {
        /**
         * Get the subtitle
         *
-        * @return String
+        * @return string
         */
        public function getSubtitle() {
                return implode( "<br />\n\t\t\t\t", $this->mSubtitle );
@@ -990,7 +990,7 @@ class OutputPage extends ContextSource {
        /**
         * Return whether the page is "printable"
         *
-        * @return Boolean
+        * @return bool
         */
        public function isPrintable() {
                return $this->mPrintable;
@@ -1006,7 +1006,7 @@ class OutputPage extends ContextSource {
        /**
         * Return whether the output will be completely disabled
         *
-        * @return Boolean
+        * @return bool
         */
        public function isDisabled() {
                return $this->mDoNothing;
@@ -1015,7 +1015,7 @@ class OutputPage extends ContextSource {
        /**
         * Show an "add new section" link?
         *
-        * @return Boolean
+        * @return bool
         */
        public function showNewSectionLink() {
                return $this->mNewSectionLink;
@@ -1024,7 +1024,7 @@ class OutputPage extends ContextSource {
        /**
         * Forcibly hide the new section link?
         *
-        * @return Boolean
+        * @return bool
         */
        public function forceHideNewSectionLink() {
                return $this->mHideNewSectionLink;
@@ -1036,7 +1036,7 @@ class OutputPage extends ContextSource {
         * for the new version
         * @see addFeedLink()
         *
-        * @param $show Boolean: true: add default feeds, false: remove all feeds
+        * @param bool $show true: add default feeds, false: remove all feeds
         */
        public function setSyndicated( $show = true ) {
                if ( $show ) {
@@ -1085,7 +1085,7 @@ class OutputPage extends ContextSource {
 
        /**
         * Should we output feed links for this page?
-        * @return Boolean
+        * @return bool
         */
        public function isSyndicated() {
                return count( $this->mFeedLinks ) > 0;
@@ -1113,7 +1113,7 @@ class OutputPage extends ContextSource {
         * corresponding article on the wiki
         * Setting true will cause the change "article related" toggle to true
         *
-        * @param $v Boolean
+        * @param bool $v
         */
        public function setArticleFlag( $v ) {
                $this->mIsarticle = $v;
@@ -1126,7 +1126,7 @@ class OutputPage extends ContextSource {
         * Return whether the content displayed page is related to the source of
         * the corresponding article on the wiki
         *
-        * @return Boolean
+        * @return bool
         */
        public function isArticle() {
                return $this->mIsarticle;
@@ -1136,7 +1136,7 @@ class OutputPage extends ContextSource {
         * Set whether this page is related an article on the wiki
         * Setting false will cause the change of "article flag" toggle to false
         *
-        * @param $v Boolean
+        * @param bool $v
         */
        public function setArticleRelated( $v ) {
                $this->mIsArticleRelated = $v;
@@ -1148,7 +1148,7 @@ class OutputPage extends ContextSource {
        /**
         * Return whether this page is related an article on the wiki
         *
-        * @return Boolean
+        * @return bool
         */
        public function isArticleRelated() {
                return $this->mIsArticleRelated;
@@ -1177,7 +1177,7 @@ class OutputPage extends ContextSource {
        /**
         * Get the list of language links
         *
-        * @return Array of Interwiki Prefixed (non DB key) Titles (e.g. 'fr:Test page')
+        * @return array Array of Interwiki Prefixed (non DB key) Titles (e.g. 'fr:Test page')
         */
        public function getLanguageLinks() {
                return $this->mLanguageLinks;
@@ -1260,7 +1260,7 @@ class OutputPage extends ContextSource {
         * hidden categories) and $link a HTML fragment with a link to the category
         * page
         *
-        * @return Array
+        * @return array
         */
        public function getCategoryLinks() {
                return $this->mCategoryLinks;
@@ -1269,7 +1269,7 @@ class OutputPage extends ContextSource {
        /**
         * Get the list of category names this page belongs to
         *
-        * @return Array of strings
+        * @return array Array of strings
         */
        public function getCategories() {
                return $this->mCategories;
@@ -1290,7 +1290,7 @@ class OutputPage extends ContextSource {
         * Return whether user JavaScript is allowed for this page
         * @deprecated since 1.18 Load modules with ResourceLoader, and origin and
         *     trustworthiness is identified and enforced automagically.
-        * @return Boolean
+        * @return bool
         */
        public function isUserJsAllowed() {
                wfDeprecated( __METHOD__, '1.18' );
@@ -1301,7 +1301,7 @@ class OutputPage extends ContextSource {
         * Show what level of JavaScript / CSS untrustworthiness is allowed on this page
         * @see ResourceLoaderModule::$origin
         * @param string $type ResourceLoaderModule TYPE_ constant
-        * @return Int ResourceLoaderModule ORIGIN_ class constant
+        * @return int ResourceLoaderModule ORIGIN_ class constant
         */
        public function getAllowedModules( $type ) {
                if ( $type == ResourceLoaderModule::TYPE_COMBINED ) {
@@ -1315,8 +1315,8 @@ class OutputPage extends ContextSource {
 
        /**
         * Set the highest level of CSS/JS untrustworthiness allowed
-        * @param  $type String ResourceLoaderModule TYPE_ constant
-        * @param  $level Int ResourceLoaderModule class constant
+        * @param string $type ResourceLoaderModule TYPE_ constant
+        * @param int $level ResourceLoaderModule class constant
         */
        public function setAllowedModules( $type, $level ) {
                $this->mAllowedModules[$type] = $level;
@@ -1324,8 +1324,8 @@ class OutputPage extends ContextSource {
 
        /**
         * As for setAllowedModules(), but don't inadvertently make the page more accessible
-        * @param  $type String
-        * @param  $level Int ResourceLoaderModule class constant
+        * @param string $type
+        * @param int $level ResourceLoaderModule class constant
         */
        public function reduceAllowedModules( $type, $level ) {
                $this->mAllowedModules[$type] = min( $this->getAllowedModules( $type ), $level );
@@ -1354,9 +1354,9 @@ class OutputPage extends ContextSource {
         *
         * @since 1.19
         *
-        * @param $element string
-        * @param $attribs array
-        * @param $contents string
+        * @param string $element
+        * @param array $attribs
+        * @param string $contents
         */
        public function addElement( $element, $attribs = array(), $contents = '' ) {
                $this->addHTML( Html::element( $element, $attribs, $contents ) );
@@ -1372,7 +1372,7 @@ class OutputPage extends ContextSource {
        /**
         * Get the body HTML
         *
-        * @return String: HTML
+        * @return string HTML
         */
        public function getHTML() {
                return $this->mBodytext;
@@ -1381,9 +1381,9 @@ class OutputPage extends ContextSource {
        /**
         * Get/set the ParserOptions object to use for wikitext parsing
         *
-        * @param $options ParserOptions|null either the ParserOption to use or null to only get the
-        *                 current ParserOption object
-        * @return ParserOptions object
+        * @param ParserOptions|null $options Either the ParserOption to use or null to only get the
+        *   current ParserOption object
+        * @return ParserOptions
         */
        public function parserOptions( $options = null ) {
                if ( !$this->mParserOptions ) {
@@ -1397,8 +1397,8 @@ class OutputPage extends ContextSource {
         * Set the revision ID which will be seen by the wiki text parser
         * for things such as embedded {{REVISIONID}} variable use.
         *
-        * @param $revid Mixed: an positive integer, or null
-        * @return Mixed: previous value
+        * @param int|null $revid An positive integer, or null
+        * @return mixed Previous value
         */
        public function setRevisionId( $revid ) {
                $val = is_null( $revid ) ? null : intval( $revid );
@@ -1408,7 +1408,7 @@ class OutputPage extends ContextSource {
        /**
         * Get the displayed revision ID
         *
-        * @return Integer
+        * @return int
         */
        public function getRevisionId() {
                return $this->mRevisionId;
@@ -1418,8 +1418,8 @@ class OutputPage extends ContextSource {
         * Set the timestamp of the revision which will be displayed. This is used
         * to avoid a extra DB call in Skin::lastModified().
         *
-        * @param $timestamp Mixed: string, or null
-        * @return Mixed: previous value
+        * @param string|null $timestamp
+        * @return mixed Previous value
         */
        public function setRevisionTimestamp( $timestamp ) {
                return wfSetVar( $this->mRevisionTimestamp, $timestamp );
@@ -1429,7 +1429,7 @@ class OutputPage extends ContextSource {
         * Get the timestamp of displayed revision.
         * This will be null if not filled by setRevisionTimestamp().
         *
-        * @return String or null
+        * @return string|null
         */
        public function getRevisionTimestamp() {
                return $this->mRevisionTimestamp;
@@ -1438,8 +1438,8 @@ class OutputPage extends ContextSource {
        /**
         * Set the displayed file version
         *
-        * @param $file File|bool
-        * @return Mixed: previous value
+        * @param File|bool $file
+        * @return mixed Previous value
         */
        public function setFileVersion( $file ) {
                $val = null;
@@ -1452,7 +1452,7 @@ class OutputPage extends ContextSource {
        /**
         * Get the displayed file version
         *
-        * @return Array|null ('time' => MW timestamp, 'sha1' => sha1)
+        * @return array|null ('time' => MW timestamp, 'sha1' => sha1)
         */
        public function getFileVersion() {
                return $this->mFileVersion;
@@ -1461,7 +1461,7 @@ class OutputPage extends ContextSource {
        /**
         * Get the templates used on this page
         *
-        * @return Array (namespace => dbKey => revId)
+        * @return array (namespace => dbKey => revId)
         * @since 1.18
         */
        public function getTemplateIds() {
@@ -1471,7 +1471,7 @@ class OutputPage extends ContextSource {
        /**
         * Get the files used on this page
         *
-        * @return Array (dbKey => array('time' => MW timestamp or null, 'sha1' => sha1 or ''))
+        * @return array (dbKey => array('time' => MW timestamp or null, 'sha1' => sha1 or ''))
         * @since 1.18
         */
        public function getFileSearchOptions() {
@@ -1482,9 +1482,9 @@ class OutputPage extends ContextSource {
         * Convert wikitext to HTML and add it to the buffer
         * Default assumes that the current page title will be used.
         *
-        * @param $text String
-        * @param $linestart Boolean: is this the start of a line?
-        * @param $interface Boolean: is this text in the user interface language?
+        * @param string $text
+        * @param bool $linestart Is this the start of a line?
+        * @param bool $interface Is this text in the user interface language?
         */
        public function addWikiText( $text, $linestart = true, $interface = true ) {
                $title = $this->getTitle(); // Work around E_STRICT
@@ -1497,9 +1497,9 @@ class OutputPage extends ContextSource {
        /**
         * Add wikitext with a custom Title object
         *
-        * @param string $text wikitext
-        * @param $title Title object
-        * @param $linestart Boolean: is this the start of a line?
+        * @param string $text Wikitext
+        * @param Title $title
+        * @param bool $linestart Is this the start of a line?
         */
        public function addWikiTextWithTitle( $text, &$title, $linestart = true ) {
                $this->addWikiTextTitle( $text, $title, $linestart );
@@ -1508,9 +1508,9 @@ class OutputPage extends ContextSource {
        /**
         * Add wikitext with a custom Title object and tidy enabled.
         *
-        * @param string $text wikitext
-        * @param $title Title object
-        * @param $linestart Boolean: is this the start of a line?
+        * @param string $text Wikitext
+        * @param Title $title
+        * @param bool $linestart Is this the start of a line?
         */
        function addWikiTextTitleTidy( $text, &$title, $linestart = true ) {
                $this->addWikiTextTitle( $text, $title, $linestart, true );
@@ -1519,8 +1519,8 @@ class OutputPage extends ContextSource {
        /**
         * Add wikitext with tidy enabled
         *
-        * @param string $text wikitext
-        * @param $linestart Boolean: is this the start of a line?
+        * @param string $text Wikitext
+        * @param bool $linestart Is this the start of a line?
         */
        public function addWikiTextTidy( $text, $linestart = true ) {
                $title = $this->getTitle();
@@ -1530,12 +1530,12 @@ class OutputPage extends ContextSource {
        /**
         * Add wikitext with a custom Title object
         *
-        * @param string $text wikitext
-        * @param $title Title object
-        * @param $linestart Boolean: is this the start of a line?
-        * @param $tidy Boolean: whether to use tidy
-        * @param $interface Boolean: whether it is an interface message
-        *                                                              (for example disables conversion)
+        * @param string $text Wikitext
+        * @param Title $title
+        * @param bool $linestart Is this the start of a line?
+        * @param bool $tidy Whether to use tidy
+        * @param bool $interface Whether it is an interface message
+        *   (for example disables conversion)
         */
        public function addWikiTextTitle( $text, Title $title, $linestart, $tidy = false, $interface = false ) {
                global $wgParser;
@@ -1561,7 +1561,7 @@ class OutputPage extends ContextSource {
        /**
         * Add a ParserOutput object, but without Html
         *
-        * @param $parserOutput ParserOutput object
+        * @param ParserOutput $parserOutput
         */
        public function addParserOutputNoText( &$parserOutput ) {
                $this->mLanguageLinks += $parserOutput->getLanguageLinks();
@@ -1613,7 +1613,7 @@ class OutputPage extends ContextSource {
        /**
         * Add a ParserOutput object
         *
-        * @param $parserOutput ParserOutput
+        * @param ParserOutput $parserOutput
         */
        function addParserOutput( &$parserOutput ) {
                $this->addParserOutputNoText( $parserOutput );
@@ -1631,7 +1631,7 @@ class OutputPage extends ContextSource {
        /**
         * Add the output of a QuickTemplate to the output buffer
         *
-        * @param $template QuickTemplate
+        * @param QuickTemplate $template
         */
        public function addTemplate( &$template ) {
                $this->addHTML( $template->getHTML() );
@@ -1640,16 +1640,14 @@ class OutputPage extends ContextSource {
        /**
         * Parse wikitext and return the HTML.
         *
-        * @param $text String
-        * @param $linestart Boolean: is this the start of a line?
-        * @param $interface Boolean: use interface language ($wgLang instead of
-        *                   $wgContLang) while parsing language sensitive magic
-        *                   words like GRAMMAR and PLURAL. This also disables
-        *                   LanguageConverter.
-        * @param $language  Language object: target language object, will override
-        *                   $interface
+        * @param string $text
+        * @param bool $linestart Is this the start of a line?
+        * @param bool $interface Use interface language ($wgLang instead of
+        *   $wgContLang) while parsing language sensitive magic words like GRAMMAR and PLURAL.
+        *   This also disables LanguageConverter.
+        * @param Language $language Target language object, will override $interface
         * @throws MWException
-        * @return String: HTML
+        * @return string HTML
         */
        public function parse( $text, $linestart = true, $interface = false, $language = null ) {
                global $wgParser;
@@ -1684,12 +1682,12 @@ class OutputPage extends ContextSource {
        /**
         * Parse wikitext, strip paragraphs, and return the HTML.
         *
-        * @param $text String
-        * @param $linestart Boolean: is this the start of a line?
-        * @param $interface Boolean: use interface language ($wgLang instead of
-        *                   $wgContLang) while parsing language sensitive magic
-        *                   words like GRAMMAR and PLURAL
-        * @return String: HTML
+        * @param string $text
+        * @param bool $linestart Is this the start of a line?
+        * @param bool $interface Use interface language ($wgLang instead of
+        *   $wgContLang) while parsing language sensitive magic
+        *   words like GRAMMAR and PLURAL
+        * @return string HTML
         */
        public function parseInline( $text, $linestart = true, $interface = false ) {
                $parsed = $this->parse( $text, $linestart, $interface );
@@ -1705,7 +1703,7 @@ class OutputPage extends ContextSource {
        /**
         * Set the value of the "s-maxage" part of the "Cache-control" HTTP header
         *
-        * @param $maxage Integer: maximum cache time on the Squid, in seconds.
+        * @param int $maxage Maximum cache time on the Squid, in seconds.
         */
        public function setSquidMaxage( $maxage ) {
                $this->mSquidMaxage = $maxage;
@@ -1714,7 +1712,7 @@ class OutputPage extends ContextSource {
        /**
         * Use enableClientCache(false) to force it to send nocache headers
         *
-        * @param $state bool
+        * @param bool $state
         *
         * @return bool
         */
@@ -1725,7 +1723,7 @@ class OutputPage extends ContextSource {
        /**
         * Get the list of cookies that will influence on the cache
         *
-        * @return Array
+        * @return array
         */
        function getCacheVaryCookies() {
                global $wgCookiePrefix, $wgCacheVaryCookies;
@@ -1749,7 +1747,7 @@ class OutputPage extends ContextSource {
         * Check if the request has a cache-varying cookie header
         * If it does, it's very important that we don't allow public caching
         *
-        * @return Boolean
+        * @return bool
         */
        function haveCacheVaryCookies() {
                $cookieHeader = $this->getRequest()->getHeader( 'cookie' );
@@ -1772,7 +1770,7 @@ class OutputPage extends ContextSource {
         * Add an HTTP header that will influence on the cache
         *
         * @param string $header header name
-        * @param $option Array|null
+        * @param array|null $option
         * @todo FIXME: Document the $option parameter; it appears to be for
         *        X-Vary-Options but what format is acceptable?
         */
@@ -1793,7 +1791,7 @@ class OutputPage extends ContextSource {
         * Return a Vary: header on which to vary caches. Based on the keys of $mVaryHeader,
         * such as Accept-Encoding or Cookie
         *
-        * @return String
+        * @return string
         */
        public function getVaryHeader() {
                return 'Vary: ' . join( ', ', array_keys( $this->mVaryHeader ) );
@@ -1802,7 +1800,7 @@ class OutputPage extends ContextSource {
        /**
         * Get a complete X-Vary-Options header
         *
-        * @return String
+        * @return string
         */
        public function getXVO() {
                $cvCookies = $this->getCacheVaryCookies();
@@ -1866,7 +1864,7 @@ class OutputPage extends ContextSource {
         * This is the default for special pages. If you display a CSRF-protected
         * form on an ordinary view page, then you need to call this function.
         *
-        * @param $enable bool
+        * @param bool $enable
         */
        public function preventClickjacking( $enable = true ) {
                $this->mPreventClickjacking = $enable;
@@ -1969,9 +1967,8 @@ class OutputPage extends ContextSource {
        /**
         * Get the message associated with the HTTP response code $code
         *
-        * @param $code Integer: status code
-        * @return String or null: message or null if $code is not in the list of
-        *         messages
+        * @param int $code Status code
+        * @return string|null Message or null if $code is not in the list of messages
         *
         * @deprecated since 1.18 Use HttpStatus::getMessage() instead.
         */
@@ -2042,6 +2039,10 @@ class OutputPage extends ContextSource {
                $response->header( "Content-type: $wgMimeType; charset=UTF-8" );
                $response->header( 'Content-language: ' . $wgLanguageCode );
 
+               // Avoid Internet Explorer "compatibility view" in IE 8-10, so that
+               // jQuery etc. can work correctly.
+               $response->header( 'X-UA-Compatible: IE=Edge' );
+
                // Prevent framing, if requested
                $frameOptions = $this->getFrameOptions();
                if ( $frameOptions ) {
@@ -2147,9 +2148,9 @@ class OutputPage extends ContextSource {
         * showErrorPage( 'titlemsg', $messageObject );
         * showErrorPage( $titleMessageObject, $messageObject );
         *
-        * @param $title Mixed: message key (string) for page title, or a Message object
-        * @param $msg Mixed: message key (string) for page text, or a Message object
-        * @param array $params message parameters; ignored if $msg is a Message object
+        * @param string|Message $title Message key (string) for page title, or a Message object
+        * @param string|Message $msg Message key (string) for page text, or a Message object
+        * @param array $params Message parameters; ignored if $msg is a Message object
         */
        public function showErrorPage( $title, $msg, $params = array() ) {
                if ( !$title instanceof Message ) {
@@ -2246,7 +2247,7 @@ class OutputPage extends ContextSource {
         * Display an error page indicating that a given version of MediaWiki is
         * required to use it
         *
-        * @param $version Mixed: the version of MediaWiki needed to use the page
+        * @param mixed $version The version of MediaWiki needed to use the page
         */
        public function versionRequired( $version ) {
                $this->prepareErrorPage( $this->msg( 'versionrequired', $version ) );
@@ -2268,7 +2269,7 @@ class OutputPage extends ContextSource {
        /**
         * Produce the stock "please login to use the wiki" page
         *
-        * @deprecated in 1.19; throw the exception directly
+        * @deprecated since 1.19; throw the exception directly
         */
        public function loginToUse() {
                throw new PermissionsError( 'read' );
@@ -2277,9 +2278,9 @@ class OutputPage extends ContextSource {
        /**
         * Format a list of error messages
         *
-        * @param array $errors of arrays returned by Title::getUserPermissionsErrors
-        * @param string $action action that was denied or null if unknown
-        * @return String: the wikitext error-messages, formatted into a list.
+        * @param array $errors Array of arrays returned by Title::getUserPermissionsErrors
+        * @param string $action Action that was denied or null if unknown
+        * @return string The wikitext error-messages, formatted into a list.
         */
        public function formatPermissionsErrorMessage( $errors, $action = null ) {
                if ( $action == null ) {
@@ -2326,10 +2327,10 @@ class OutputPage extends ContextSource {
         *
         * @todo Needs to be split into multiple functions.
         *
-        * @param $source    String: source code to show (or null).
-        * @param $protected Boolean: is this a permissions error?
-        * @param $reasons   Array: list of reasons for this error, as returned by Title::getUserPermissionsErrors().
-        * @param $action    String: action that was denied or null if unknown
+        * @param string $source Source code to show (or null).
+        * @param bool $protected Is this a permissions error?
+        * @param array $reasons List of reasons for this error, as returned by Title::getUserPermissionsErrors().
+        * @param string $action Action that was denied or null if unknown
         * @throws ReadOnlyError
         */
        public function readOnlyPage( $source = null, $protected = false, $reasons = array(), $action = null ) {
@@ -2403,7 +2404,7 @@ $templates
         * then the warning is a bit more obvious. If the lag is
         * lower than $wgSlaveLagWarning, then no warning is shown.
         *
-        * @param $lag Integer: slave lag
+        * @param int $lag Slave lag
         */
        public function showLagWarning( $lag ) {
                global $wgSlaveLagWarning, $wgSlaveLagCritical;
@@ -2445,10 +2446,10 @@ $templates
        /**
         * Add a "return to" link pointing to a specified title
         *
-        * @param $title Title to link
-        * @param array $query query string parameters
-        * @param string $text text of the link (input is not escaped)
-        * @param $options Options array to pass to Linker
+        * @param Title $title Title to link
+        * @param array $query Query string parameters
+        * @param string $text Text of the link (input is not escaped)
+        * @param array $options Options array to pass to Linker
         */
        public function addReturnTo( $title, $query = array(), $text = null, $options = array() ) {
                $link = $this->msg( 'returnto' )->rawParams(
@@ -2460,9 +2461,9 @@ $templates
         * Add a "return to" link pointing to a specified title,
         * or the title indicated in the request, or else the main page
         *
-        * @param $unused
-        * @param $returnto Title or String to return to
-        * @param string $returntoquery query string for the return to link
+        * @param mixed $unused
+        * @param Title|string $returnto Title or String to return to
+        * @param string $returntoquery Query string for the return to link
         */
        public function returnToMain( $unused = null, $returnto = null, $returntoquery = null ) {
                if ( $returnto == null ) {
@@ -2490,9 +2491,9 @@ $templates
        }
 
        /**
-        * @param $sk Skin The given Skin
-        * @param $includeStyle Boolean: unused
-        * @return String: The doctype, opening "<html>", and head element.
+        * @param Skin $sk The given Skin
+        * @param bool $includeStyle Unused
+        * @return string The doctype, opening "<html>", and head element.
         */
        public function headElement( Skin $sk, $includeStyle = true ) {
                global $wgContLang, $wgMimeType;
@@ -2526,16 +2527,15 @@ $templates
 
                $ret .= Html::element( 'title', null, $this->getHTMLTitle() ) . "\n";
 
-               // Avoid Internet Explorer "compatibility view", so that
-               // jQuery can work correctly.
-               $ret .= Html::element( 'meta', array( 'http-equiv' => 'X-UA-Compatible', 'content' => 'IE=EDGE' ) ) . "\n";
-
-               $ret .= implode( "\n", array(
-                       $this->getHeadLinks(),
-                       $this->buildCssLinks(),
-                       $this->getHeadScripts(),
+               $ret .= (
+                       $this->getHeadLinks() .
+                       "\n" .
+                       $this->buildCssLinks() .
+                       // No newline after buildCssLinks since makeResourceLoaderLink did that already
+                       $this->getHeadScripts() .
+                       "\n" .
                        $this->getHeadItems()
-               ) );
+               );
 
                $closeHead = Html::closeElement( 'head' );
                if ( $closeHead ) {
@@ -2586,22 +2586,28 @@ $templates
 
        /**
         * TODO: Document
-        * @param $modules Array/string with the module name(s)
+        * @param array|string $modules One or more module names
         * @param string $only ResourceLoaderModule TYPE_ class constant
-        * @param $useESI boolean
-        * @param array $extraQuery with extra query parameters to add to each request. array( param => value )
-        * @param $loadCall boolean If true, output an (asynchronous) mw.loader.load() call rather than a "<script src='...'>" tag
-        * @return string html "<script>" and "<style>" tags
+        * @param bool $useESI
+        * @param array $extraQuery Array with extra query parameters to add to each request. array( param => value )
+        * @param bool $loadCall If true, output an (asynchronous) mw.loader.load() call rather than a "<script src='...'>" tag
+        * @return string The html "<script>", "<link>" and "<style>" tags
         */
        protected function makeResourceLoaderLink( $modules, $only, $useESI = false, array $extraQuery = array(), $loadCall = false ) {
                global $wgResourceLoaderUseESI;
 
                $modules = (array)$modules;
 
+               $links = array(
+                       'html' => '',
+                       'states' => array(),
+               );
+
                if ( !count( $modules ) ) {
-                       return '';
+                       return $links;
                }
 
+
                if ( count( $modules ) > 1 ) {
                        // Remove duplicate module requests
                        $modules = array_unique( $modules );
@@ -2610,13 +2616,15 @@ $templates
 
                        if ( ResourceLoader::inDebugMode() ) {
                                // Recursively call us for every item
-                               $links = '';
                                foreach ( $modules as $name ) {
-                                       $links .= $this->makeResourceLoaderLink( $name, $only, $useESI );
+                                       $link = $this->makeResourceLoaderLink( $name, $only, $useESI );
+                                       $links['html'] .= $link['html'];
+                                       $links['states'] += $link['states'];
                                }
                                return $links;
                        }
                }
+
                if ( !is_null( $this->mTarget ) ) {
                        $extraQuery['target'] = $this->mTarget;
                }
@@ -2644,7 +2652,6 @@ $templates
                        $groups[$group][$name] = $module;
                }
 
-               $links = '';
                foreach ( $groups as $group => $grpModules ) {
                        // Special handling for user-specific groups
                        $user = null;
@@ -2667,25 +2674,20 @@ $templates
                                $extraQuery
                        );
                        $context = new ResourceLoaderContext( $resourceLoader, new FauxRequest( $query ) );
+
                        // Extract modules that know they're empty
-                       $emptyModules = array();
                        foreach ( $grpModules as $key => $module ) {
+                               // Inline empty modules: since they're empty, just mark them as 'ready' (bug 46857)
+                               // If we're only getting the styles, we don't need to do anything for empty modules.
                                if ( $module->isKnownEmpty( $context ) ) {
-                                       $emptyModules[$key] = 'ready';
                                        unset( $grpModules[$key] );
+                                       if ( $only !== ResourceLoaderModule::TYPE_STYLES ) {
+                                               $links['states'][$key] = 'ready';
+                                       }
                                }
                        }
-                       // Inline empty modules: since they're empty, just mark them as 'ready'
-                       if ( count( $emptyModules ) > 0 && $only !== ResourceLoaderModule::TYPE_STYLES ) {
-                               // If we're only getting the styles, we don't need to do anything for empty modules.
-                               $links .= Html::inlineScript(
-                                               ResourceLoader::makeLoaderConditionalScript(
-                                                               ResourceLoader::makeLoaderStateScript( $emptyModules )
-                                               )
-                               ) . "\n";
-                       }
 
-                       // If there are no modules left, skip this group
+                       // If there are no non-empty modules, skip this group
                        if ( count( $grpModules ) === 0 ) {
                                continue;
                        }
@@ -2696,19 +2698,20 @@ $templates
                        // properly use them as dependencies (bug 30914)
                        if ( $group === 'private' ) {
                                if ( $only == ResourceLoaderModule::TYPE_STYLES ) {
-                                       $links .= Html::inlineStyle(
+                                       $links['html'] .= Html::inlineStyle(
                                                $resourceLoader->makeModuleResponse( $context, $grpModules )
                                        );
                                } else {
-                                       $links .= Html::inlineScript(
+                                       $links['html'] .= Html::inlineScript(
                                                ResourceLoader::makeLoaderConditionalScript(
                                                        $resourceLoader->makeModuleResponse( $context, $grpModules )
                                                )
                                        );
                                }
-                               $links .= "\n";
+                               $links['html'] .= "\n";
                                continue;
                        }
+
                        // Special handling for the user group; because users might change their stuff
                        // on-wiki like user pages, or user preferences; we need to find the highest
                        // timestamp of these user-changeable modules so we can ensure cache misses on change
@@ -2756,32 +2759,71 @@ $templates
                                        );
                                } else {
                                        $link = Html::linkedScript( $url );
+
+                                       // For modules requested directly in the html via <link> or <script>,
+                                       // tell mw.loader they are being loading to prevent duplicate requests.
+                                       foreach ( $grpModules as $key => $module ) {
+                                               // Don't output state=loading for the startup module..
+                                               if ( $key !== 'startup' ) {
+                                                       $links['states'][$key] = 'loading';
+                                               }
+                                       }
                                }
                        }
 
                        if ( $group == 'noscript' ) {
-                               $links .= Html::rawElement( 'noscript', array(), $link ) . "\n";
+                               $links['html'] .= Html::rawElement( 'noscript', array(), $link ) . "\n";
                        } else {
-                               $links .= $link . "\n";
+                               $links['html'] .= $link . "\n";
                        }
                }
+
                return $links;
        }
 
+       /**
+        * Build html output from an array of links from makeResourceLoaderLink.
+        * @param array $links
+        * @return string HTML
+        */
+       protected static function getHtmlFromLoaderLinks( Array $links ) {
+               $html = '';
+               $states = array();
+               foreach ( $links as $link ) {
+                       if ( !is_array( $link ) ) {
+                               $html .= $link;
+                       } else {
+                               $html .= $link['html'];
+                               $states += $link['states'];
+                       }
+               }
+
+               if ( count( $states ) ) {
+                       $html = Html::inlineScript(
+                               ResourceLoader::makeLoaderConditionalScript(
+                                       ResourceLoader::makeLoaderStateScript( $states )
+                               )
+                       ) . "\n" . $html;
+               }
+
+               return $html;
+       }
+
        /**
         * JS stuff to put in the "<head>". This is the startup module, config
         * vars and modules marked with position 'top'
         *
-        * @return String: HTML fragment
+        * @return string HTML fragment
         */
        function getHeadScripts() {
                global $wgResourceLoaderExperimentalAsyncLoading;
 
                // Startup - this will immediately load jquery and mediawiki modules
-               $scripts = $this->makeResourceLoaderLink( 'startup', ResourceLoaderModule::TYPE_SCRIPTS, true );
+               $links = array();
+               $links[] = $this->makeResourceLoaderLink( 'startup', ResourceLoaderModule::TYPE_SCRIPTS, true );
 
                // Load config before anything else
-               $scripts .= Html::inlineScript(
+               $links[] = Html::inlineScript(
                        ResourceLoader::makeLoaderConditionalScript(
                                ResourceLoader::makeConfigSetScript( $this->getJSVars() )
                        )
@@ -2791,18 +2833,18 @@ $templates
                // This needs to be TYPE_COMBINED so these modules are properly wrapped
                // in mw.loader.implement() calls and deferred until mw.user is available
                $embedScripts = array( 'user.options', 'user.tokens' );
-               $scripts .= $this->makeResourceLoaderLink( $embedScripts, ResourceLoaderModule::TYPE_COMBINED );
+               $links[] = $this->makeResourceLoaderLink( $embedScripts, ResourceLoaderModule::TYPE_COMBINED );
 
-               // Script and Messages "only" requests marked for top inclusion
+               // Scripts and messages "only" requests marked for top inclusion
                // Messages should go first
-               $scripts .= $this->makeResourceLoaderLink( $this->getModuleMessages( true, 'top' ), ResourceLoaderModule::TYPE_MESSAGES );
-               $scripts .= $this->makeResourceLoaderLink( $this->getModuleScripts( true, 'top' ), ResourceLoaderModule::TYPE_SCRIPTS );
+               $links[] = $this->makeResourceLoaderLink( $this->getModuleMessages( true, 'top' ), ResourceLoaderModule::TYPE_MESSAGES );
+               $links[] = $this->makeResourceLoaderLink( $this->getModuleScripts( true, 'top' ), ResourceLoaderModule::TYPE_SCRIPTS );
 
                // Modules requests - let the client calculate dependencies and batch requests as it likes
                // Only load modules that have marked themselves for loading at the top
                $modules = $this->getModules( true, 'top' );
                if ( $modules ) {
-                       $scripts .= Html::inlineScript(
+                       $links[] = Html::inlineScript(
                                ResourceLoader::makeLoaderConditionalScript(
                                        Xml::encodeJsCall( 'mw.loader.load', array( $modules ) )
                                )
@@ -2810,32 +2852,33 @@ $templates
                }
 
                if ( $wgResourceLoaderExperimentalAsyncLoading ) {
-                       $scripts .= $this->getScriptsForBottomQueue( true );
+                       $links[] = $this->getScriptsForBottomQueue( true );
                }
 
-               return $scripts;
+               return self::getHtmlFromLoaderLinks( $links );
        }
 
        /**
         * JS stuff to put at the 'bottom', which can either be the bottom of the "<body>"
         * or the bottom of the "<head>" depending on $wgResourceLoaderExperimentalAsyncLoading:
         * modules marked with position 'bottom', legacy scripts ($this->mScripts),
-        * user preferences, site JS and user JS
+        * user preferences, site JS and user JS.
         *
-        * @param $inHead boolean If true, this HTML goes into the "<head>", if false it goes into the "<body>"
+        * @param bool $inHead If true, this HTML goes into the "<head>", if false it goes into the "<body>"
         * @return string
         */
        function getScriptsForBottomQueue( $inHead ) {
                global $wgUseSiteJs, $wgAllowUserJs;
 
-               // Script and Messages "only" requests marked for bottom inclusion
+               // Scripts and messages "only" requests marked for bottom inclusion
                // If we're in the <head>, use load() calls rather than <script src="..."> tags
                // Messages should go first
-               $scripts = $this->makeResourceLoaderLink( $this->getModuleMessages( true, 'bottom' ),
+               $links = array();
+               $links[] = $this->makeResourceLoaderLink( $this->getModuleMessages( true, 'bottom' ),
                        ResourceLoaderModule::TYPE_MESSAGES, /* $useESI = */ false, /* $extraQuery = */ array(),
                        /* $loadCall = */ $inHead
                );
-               $scripts .= $this->makeResourceLoaderLink( $this->getModuleScripts( true, 'bottom' ),
+               $links[] = $this->makeResourceLoaderLink( $this->getModuleScripts( true, 'bottom' ),
                        ResourceLoaderModule::TYPE_SCRIPTS, /* $useESI = */ false, /* $extraQuery = */ array(),
                        /* $loadCall = */ $inHead
                );
@@ -2844,7 +2887,7 @@ $templates
                // Only load modules that have marked themselves for loading at the bottom
                $modules = $this->getModules( true, 'bottom' );
                if ( $modules ) {
-                       $scripts .= Html::inlineScript(
+                       $links[] = Html::inlineScript(
                                ResourceLoader::makeLoaderConditionalScript(
                                        Xml::encodeJsCall( 'mw.loader.load', array( $modules, null, true ) )
                                )
@@ -2852,88 +2895,40 @@ $templates
                }
 
                // Legacy Scripts
-               $scripts .= "\n" . $this->mScripts;
-
-               $defaultModules = array();
+               $links[] = "\n" . $this->mScripts;
 
                // Add site JS if enabled
-               if ( $wgUseSiteJs ) {
-                       $scripts .= $this->makeResourceLoaderLink( 'site', ResourceLoaderModule::TYPE_SCRIPTS,
-                               /* $useESI = */ false, /* $extraQuery = */ array(), /* $loadCall = */ $inHead
-                       );
-                       $defaultModules['site'] = 'loading';
-               } else {
-                       // Site module is empty, save request by marking ready in advance (bug 46857)
-                       $defaultModules['site'] = 'ready';
-               }
+               $links[] = $this->makeResourceLoaderLink( 'site', ResourceLoaderModule::TYPE_SCRIPTS,
+                       /* $useESI = */ false, /* $extraQuery = */ array(), /* $loadCall = */ $inHead
+               );
 
                // Add user JS if enabled
-               if ( $wgAllowUserJs ) {
-                       if ( $this->getUser()->isLoggedIn() ) {
-                               if ( $this->getTitle() && $this->getTitle()->isJsSubpage() && $this->userCanPreview() ) {
-                                       # XXX: additional security check/prompt?
-                                       // We're on a preview of a JS subpage
-                                       // Exclude this page from the user module in case it's in there (bug 26283)
-                                       $scripts .= $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_SCRIPTS, false,
-                                               array( 'excludepage' => $this->getTitle()->getPrefixedDBkey() ), $inHead
-                                       );
-                                       // Load the previewed JS
-                                       $scripts .= Html::inlineScript( "\n" . $this->getRequest()->getText( 'wpTextbox1' ) . "\n" ) . "\n";
-                                       // FIXME: If the user is previewing, say, ./vector.js, his ./common.js will be loaded
-                                       // asynchronously and may arrive *after* the inline script here. So the previewed code
-                                       // may execute before ./common.js runs. Normally, ./common.js runs before ./vector.js...
-                               } else {
-                                       // Include the user module normally, i.e., raw to avoid it being wrapped in a closure.
-                                       $scripts .= $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_SCRIPTS,
-                                               /* $useESI = */ false, /* $extraQuery = */ array(), /* $loadCall = */ $inHead
-                                       );
-                               }
-                               $defaultModules['user'] = 'loading';
-                       } else {
-                               // Non-logged-in users have an empty user module.
-                               // Save request by marking ready in advance (bug 46857)
-                               $defaultModules['user'] = 'ready';
-                       }
+               if ( $wgAllowUserJs && $this->getUser()->isLoggedIn() && $this->getTitle() && $this->getTitle()->isJsSubpage() && $this->userCanPreview() ) {
+                       # XXX: additional security check/prompt?
+                       // We're on a preview of a JS subpage
+                       // Exclude this page from the user module in case it's in there (bug 26283)
+                       $links[] = $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_SCRIPTS, false,
+                               array( 'excludepage' => $this->getTitle()->getPrefixedDBkey() ), $inHead
+                       );
+                       // Load the previewed JS
+                       $links[] = Html::inlineScript( "\n" . $this->getRequest()->getText( 'wpTextbox1' ) . "\n" ) . "\n";
+
+                       // FIXME: If the user is previewing, say, ./vector.js, his ./common.js will be loaded
+                       // asynchronously and may arrive *after* the inline script here. So the previewed code
+                       // may execute before ./common.js runs. Normally, ./common.js runs before ./vector.js...
                } else {
-                       // User modules are disabled on this wiki.
-                       // Save request by marking ready in advance (bug 46857)
-                       $defaultModules['user'] = 'ready';
+                       // Include the user module normally, i.e., raw to avoid it being wrapped in a closure.
+                       $links[] = $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_SCRIPTS,
+                               /* $useESI = */ false, /* $extraQuery = */ array(), /* $loadCall = */ $inHead
+                       );
                }
 
                // Group JS is only enabled if site JS is enabled.
-               if ( $wgUseSiteJs ) {
-                       if ( $this->getUser()->isLoggedIn() ) {
-                               $scripts .= $this->makeResourceLoaderLink( 'user.groups', ResourceLoaderModule::TYPE_COMBINED,
-                                       /* $useESI = */ false, /* $extraQuery = */ array(), /* $loadCall = */ $inHead
-                               );
-                               $defaultModules['user.groups'] = 'loading';
-                       } else {
-                               // Non-logged-in users have no user.groups module.
-                               // Save request by marking ready in advance (bug 46857)
-                               $defaultModules['user.groups'] = 'ready';
-                       }
-               } else {
-                       // Site (and group JS) disabled
-                       $defaultModules['user.groups'] = 'ready';
-               }
+               $links[] = $this->makeResourceLoaderLink( 'user.groups', ResourceLoaderModule::TYPE_COMBINED,
+                       /* $useESI = */ false, /* $extraQuery = */ array(), /* $loadCall = */ $inHead
+               );
 
-               $loaderInit = '';
-               if ( $inHead ) {
-                       // We generate loader calls anyway, so no need to fix the client-side loader's state to 'loading'.
-                       foreach ( $defaultModules as $m => $state ) {
-                               if ( $state == 'loading' ) {
-                                       unset( $defaultModules[$m] );
-                               }
-                       }
-               }
-               if ( count( $defaultModules ) > 0 ) {
-                       $loaderInit = Html::inlineScript(
-                               ResourceLoader::makeLoaderConditionalScript(
-                                       ResourceLoader::makeLoaderStateScript( $defaultModules )
-                               )
-                       ) . "\n";
-               }
-               return $loaderInit . $scripts;
+               return self::getHtmlFromLoaderLinks( $links );
        }
 
        /**
@@ -2959,7 +2954,7 @@ $templates
        /**
         * Get the javascript config vars to include on this page
         *
-        * @return Array of javascript config vars
+        * @return array Array of javascript config vars
         * @since 1.23
         */
        public function getJsConfigVars() {
@@ -2967,10 +2962,10 @@ $templates
        }
 
        /**
-        * Add one or more variables to be set in mw.config in JavaScript.
+        * Add one or more variables to be set in mw.config in JavaScript
         *
-        * @param $keys {String|Array} Key or array of key/value pairs.
-        * @param $value {Mixed} [optional] Value of the configuration variable.
+        * @param string|array $keys Key or array of key/value pairs
+        * @param mixed $value [optional] Value of the configuration variable
         */
        public function addJsConfigVars( $keys, $value = null ) {
                if ( is_array( $keys ) ) {
@@ -3350,10 +3345,10 @@ $templates
        /**
         * Generate a "<link rel/>" for a feed.
         *
-        * @param string $type feed type
+        * @param string $type Feed type
         * @param string $url URL to the feed
-        * @param string $text value of the "title" attribute
-        * @return String: HTML fragment
+        * @param string $text Value of the "title" attribute
+        * @return string HTML fragment
         */
        private function feedLink( $type, $url, $text ) {
                return Html::element( 'link', array(
@@ -3391,7 +3386,7 @@ $templates
 
        /**
         * Adds inline CSS styles
-        * @param $style_css Mixed: inline CSS
+        * @param mixed $style_css Inline CSS
         * @param string $flip Set to 'flip' to flip the CSS if needed
         */
        public function addInlineStyle( $style_css, $flip = 'noflip' ) {
@@ -3399,7 +3394,7 @@ $templates
                        # If wanted, and the interface is right-to-left, flip the CSS
                        $style_css = CSSJanus::transform( $style_css, true, false );
                }
-               $this->mInlineStyles .= Html::inlineStyle( $style_css );
+               $this->mInlineStyles .= Html::inlineStyle( $style_css ) . "\n";
        }
 
        /**
@@ -3414,49 +3409,43 @@ $templates
                $this->getSkin()->setupSkinUserCss( $this );
 
                // Add ResourceLoader styles
-               // Split the styles into four groups
+               // Split the styles into these groups
                $styles = array( 'other' => array(), 'user' => array(), 'site' => array(), 'private' => array(), 'noscript' => array() );
+               $links = array();
                $otherTags = ''; // Tags to append after the normal <link> tags
                $resourceLoader = $this->getResourceLoader();
 
                $moduleStyles = $this->getModuleStyles();
 
                // Per-site custom styles
-               if ( $wgUseSiteCss ) {
-                       $moduleStyles[] = 'site';
-                       $moduleStyles[] = 'noscript';
-                       if ( $this->getUser()->isLoggedIn() ) {
-                               $moduleStyles[] = 'user.groups';
-                       }
-               }
+               $moduleStyles[] = 'site';
+               $moduleStyles[] = 'noscript';
+               $moduleStyles[] = 'user.groups';
 
                // Per-user custom styles
-               if ( $wgAllowUserCss ) {
-                       if ( $this->getTitle()->isCssSubpage() && $this->userCanPreview() ) {
-                               // We're on a preview of a CSS subpage
-                               // Exclude this page from the user module in case it's in there (bug 26283)
-                               $otherTags .= $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_STYLES, false,
-                                       array( 'excludepage' => $this->getTitle()->getPrefixedDBkey() )
-                               );
-
-                               // Load the previewed CSS
-                               // If needed, Janus it first. This is user-supplied CSS, so it's
-                               // assumed to be right for the content language directionality.
-                               $previewedCSS = $this->getRequest()->getText( 'wpTextbox1' );
-                               if ( $this->getLanguage()->getDir() !== $wgContLang->getDir() ) {
-                                       $previewedCSS = CSSJanus::transform( $previewedCSS, true, false );
-                               }
-                               $otherTags .= Html::inlineStyle( $previewedCSS );
-                       } else {
-                               // Load the user styles normally
-                               $moduleStyles[] = 'user';
+               if ( $wgAllowUserCss && $this->getTitle()->isCssSubpage() && $this->userCanPreview() ) {
+                       // We're on a preview of a CSS subpage
+                       // Exclude this page from the user module in case it's in there (bug 26283)
+                       $link = $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_STYLES, false,
+                               array( 'excludepage' => $this->getTitle()->getPrefixedDBkey() )
+                       );
+                       $otherTags .= $link['html'];
+
+                       // Load the previewed CSS
+                       // If needed, Janus it first. This is user-supplied CSS, so it's
+                       // assumed to be right for the content language directionality.
+                       $previewedCSS = $this->getRequest()->getText( 'wpTextbox1' );
+                       if ( $this->getLanguage()->getDir() !== $wgContLang->getDir() ) {
+                               $previewedCSS = CSSJanus::transform( $previewedCSS, true, false );
                        }
+                       $otherTags .= Html::inlineStyle( $previewedCSS ) . "\n";
+               } else {
+                       // Load the user styles normally
+                       $moduleStyles[] = 'user';
                }
 
                // Per-user preference styles
-               if ( $wgAllowUserCssPrefs ) {
-                       $moduleStyles[] = 'user.cssprefs';
-               }
+               $moduleStyles[] = 'user.cssprefs';
 
                foreach ( $moduleStyles as $name ) {
                        $module = $resourceLoader->getModule( $name );
@@ -3464,38 +3453,37 @@ $templates
                                continue;
                        }
                        $group = $module->getGroup();
-                       // Modules in groups named "other" or anything different than "user", "site" or "private"
+                       // Modules in groups different than the ones listed on top (see $styles assignment)
                        // will be placed in the "other" group
-                       $styles[isset( $styles[$group] ) ? $group : 'other'][] = $name;
+                       $styles[ isset( $styles[$group] ) ? $group : 'other' ][] = $name;
                }
 
                // We want site, private and user styles to override dynamically added styles from modules, but we want
                // dynamically added styles to override statically added styles from other modules. So the order
                // has to be other, dynamic, site, private, user
                // Add statically added styles for other modules
-               $ret = $this->makeResourceLoaderLink( $styles['other'], ResourceLoaderModule::TYPE_STYLES );
+               $links[] = $this->makeResourceLoaderLink( $styles['other'], ResourceLoaderModule::TYPE_STYLES );
                // Add normal styles added through addStyle()/addInlineStyle() here
-               $ret .= implode( "\n", $this->buildCssLinksArray() ) . $this->mInlineStyles;
+               $links[] = implode( "\n", $this->buildCssLinksArray() ) . $this->mInlineStyles;
                // Add marker tag to mark the place where the client-side loader should inject dynamic styles
                // We use a <meta> tag with a made-up name for this because that's valid HTML
-               $ret .= Html::element( 'meta', array( 'name' => 'ResourceLoaderDynamicStyles', 'content' => '' ) ) . "\n";
+               $links[] = Html::element( 'meta', array( 'name' => 'ResourceLoaderDynamicStyles', 'content' => '' ) ) . "\n";
 
                // Add site, private and user styles
                // 'private' at present only contains user.options, so put that before 'user'
                // Any future private modules will likely have a similar user-specific character
                foreach ( array( 'site', 'noscript', 'private', 'user' ) as $group ) {
-                       $ret .= $this->makeResourceLoaderLink( $styles[$group],
-                                       ResourceLoaderModule::TYPE_STYLES
+                       $links[] = $this->makeResourceLoaderLink( $styles[$group],
+                               ResourceLoaderModule::TYPE_STYLES
                        );
                }
 
                // Add stuff in $otherTags (previewed user CSS if applicable)
-               $ret .= $otherTags;
-               return $ret;
+               return self::getHtmlFromLoaderLinks( $links ) . $otherTags;
        }
 
        /**
-        * @return Array
+        * @return array
         */
        public function buildCssLinksArray() {
                $links = array();
@@ -3519,9 +3507,8 @@ $templates
         * Generate \<link\> tags for stylesheets
         *
         * @param string $style URL to the file
-        * @param array $options option, can contain 'condition', 'dir', 'media'
-        *                 keys
-        * @return String: HTML fragment
+        * @param array $options Option, can contain 'condition', 'dir', 'media' keys
+        * @return string HTML fragment
         */
        protected function styleLink( $style, $options ) {
                if ( isset( $options['dir'] ) ) {
@@ -3560,8 +3547,8 @@ $templates
        /**
         * Transform "media" attribute based on request parameters
         *
-        * @param string $media current value of the "media" attribute
-        * @return String: modified value of the "media" attribute, or null to skip
+        * @param string $media Current value of the "media" attribute
+        * @return string Modified value of the "media" attribute, or null to skip
         * this stylesheet
         */
        public static function transformCssMedia( $media ) {
@@ -3617,8 +3604,8 @@ $templates
         * Like addWikiMsg() except the parameters are taken as an array
         * instead of a variable argument list.
         *
-        * @param $name string
-        * @param $args array
+        * @param string $name
+        * @param array $args
         */
        public function addWikiMsgArray( $name, $args ) {
                $this->addHTML( $this->msg( $name, $args )->parseAsBlock() );
@@ -3645,7 +3632,7 @@ $templates
         *
         * The newline after opening div is needed in some wikitext. See bug 19226.
         *
-        * @param $wrap string
+        * @param string $wrap
         */
        public function wrapWikiMsg( $wrap /*, ...*/ ) {
                $msgSpecs = func_get_args();
@@ -3676,8 +3663,8 @@ $templates
         * Include jQuery core. Use this to avoid loading it multiple times
         * before we get a usable script loader.
         *
-        * @param array $modules list of jQuery modules which should be loaded
-        * @return Array: the list of modules which were not loaded.
+        * @param array $modules List of jQuery modules which should be loaded
+        * @return array The list of modules which were not loaded.
         * @since 1.16
         * @deprecated since 1.17
         */