Merge "Language: Convert names cache from MapCacheLRU to HashBagOStuff"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 11 Nov 2015 15:20:31 +0000 (15:20 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 11 Nov 2015 15:20:31 +0000 (15:20 +0000)
115 files changed:
.jscsrc
autoload.php
composer.json
includes/FeedUtils.php
includes/Setup.php
includes/api/i18n/cs.json
includes/cache/BacklinkCache.php
includes/cache/MessageBlobStore.php
includes/cache/MessageCache.php
includes/changes/ChangesFeed.php
includes/compat/MemcachedClientCompat.php [new file with mode: 0644]
includes/db/ChronologyProtector.php
includes/db/Database.php
includes/db/DatabaseMysqlBase.php
includes/db/DatabaseUtility.php
includes/db/loadbalancer/LoadBalancer.php
includes/installer/i18n/jv.json
includes/jobqueue/JobRunner.php
includes/jobqueue/aggregator/JobQueueAggregatorRedis.php
includes/jobqueue/jobs/RefreshLinksJob.php
includes/libs/objectcache/MemcachedBagOStuff.php
includes/libs/objectcache/MemcachedClient.php
includes/libs/objectcache/MemcachedPhpBagOStuff.php
includes/mail/UserMailer.php
includes/objectcache/MemcachedPeclBagOStuff.php
includes/objectcache/SqlBagOStuff.php
includes/specials/SpecialAllMessages.php
includes/specials/SpecialAllPages.php
includes/specials/SpecialPrefixindex.php
languages/classes/LanguageAz.php
languages/classes/LanguageKaa.php
languages/classes/LanguageKk.php
languages/classes/LanguageTr.php
languages/i18n/ady-cyrl.json
languages/i18n/bgn.json
languages/i18n/cv.json
languages/i18n/eu.json
languages/i18n/fa.json
languages/i18n/it.json
languages/i18n/ru.json
languages/i18n/sah.json
languages/i18n/sh.json
languages/i18n/ur.json
maintenance/findOrphanedFiles.php
maintenance/mcc.php
resources/Resources.php
resources/lib/oojs-ui/i18n/bs.json
resources/lib/oojs-ui/i18n/cs.json
resources/lib/oojs-ui/i18n/wuu.json
resources/lib/oojs-ui/oojs-ui-apex-noimages.css
resources/lib/oojs-ui/oojs-ui-apex.js
resources/lib/oojs-ui/oojs-ui-mediawiki-noimages.css
resources/lib/oojs-ui/oojs-ui-mediawiki.js
resources/lib/oojs-ui/oojs-ui.js
resources/lib/oojs-ui/themes/apex/icons-editing-advanced.json
resources/lib/oojs-ui/themes/apex/images/icons/add.png
resources/lib/oojs-ui/themes/apex/images/icons/add.svg
resources/lib/oojs-ui/themes/apex/images/icons/insert.png [deleted file]
resources/lib/oojs-ui/themes/apex/images/icons/insert.svg [deleted file]
resources/lib/oojs-ui/themes/apex/images/icons/noWikiText-ltr.png
resources/lib/oojs-ui/themes/apex/images/icons/noWikiText-ltr.svg
resources/lib/oojs-ui/themes/apex/images/icons/noWikiText-rtl.png
resources/lib/oojs-ui/themes/apex/images/icons/noWikiText-rtl.svg
resources/lib/oojs-ui/themes/apex/images/icons/wikiText.png
resources/lib/oojs-ui/themes/apex/images/icons/wikiText.svg
resources/lib/oojs-ui/themes/mediawiki/icons-editing-advanced.json
resources/lib/oojs-ui/themes/mediawiki/images/icons/add-constructive.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/add-constructive.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/add-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/add-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/add.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/add.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/insert-invert.png [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/insert-invert.svg [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/insert.png [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/insert.svg [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/menu-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/menu-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/menu.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/menu.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-ltr.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-ltr.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-rtl.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-rtl.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/wikiText-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/wikiText-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/wikiText.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/wikiText.svg
resources/src/jquery/jquery.placeholder.js
resources/src/jquery/jquery.tablesorter.js
resources/src/jquery/jquery.textSelection.js
resources/src/mediawiki.legacy/commonPrint.css
resources/src/mediawiki.legacy/shared.css
resources/src/mediawiki.widgets/mw.widgets.CalendarWidget.js
resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.js
resources/src/mediawiki.widgets/mw.widgets.TitleSearchWidget.js
resources/src/mediawiki.widgets/mw.widgets.TitleWidget.js
resources/src/mediawiki.widgets/mw.widgets.UserInputWidget.js
resources/src/mediawiki/mediawiki.Upload.BookletLayout.js
resources/src/mediawiki/mediawiki.Upload.Dialog.js
resources/src/mediawiki/mediawiki.Uri.js
resources/src/mediawiki/mediawiki.experiments.js
resources/src/mediawiki/mediawiki.feedback.js
resources/src/mediawiki/mediawiki.htmlform.css
resources/src/mediawiki/mediawiki.notification.js
resources/src/mediawiki/mediawiki.requestIdleCallback.js [new file with mode: 0644]
tests/parser/parserTests.txt
tests/phpunit/includes/db/DatabaseMysqlBaseTest.php
tests/phpunit/includes/jobqueue/JobQueueTest.php
tests/qunit/QUnitTestResources.php
tests/qunit/suites/resources/mediawiki/mediawiki.requestIdleCallback.test.js [new file with mode: 0644]

diff --git a/.jscsrc b/.jscsrc
index c1fbc9a..3b94b90 100644 (file)
--- a/.jscsrc
+++ b/.jscsrc
@@ -7,7 +7,17 @@
        "disallowQuotedKeysInObjects": "allButReserved",
        "requireDotNotation": { "allExcept": [ "keywords" ] },
        "jsDoc": {
+               "checkAnnotations": {
+                       "preset": "jsduck5",
+                       "extra": {
+                               "context": true,
+                               "source": true,
+                               "see": true,
+                               "private": true
+                       }
+               },
                "checkParamNames": true,
+               "checkRedundantAccess": true,
                "checkRedundantReturns": true,
                "checkTypes": "strictNativeCase",
                "requireNewlineAfterDescription": true,
index b27ce46..15f1b10 100644 (file)
@@ -721,7 +721,7 @@ $wgAutoloadLocalClasses = array(
        'MWExceptionHandler' => __DIR__ . '/includes/exception/MWExceptionHandler.php',
        'MWHookException' => __DIR__ . '/includes/Hooks.php',
        'MWHttpRequest' => __DIR__ . '/includes/HttpFunctions.php',
-       'MWMemcached' => __DIR__ . '/includes/libs/objectcache/MemcachedClient.php',
+       'MWMemcached' => __DIR__ . '/includes/compat/MemcachedClientCompat.php',
        'MWMessagePack' => __DIR__ . '/includes/libs/MWMessagePack.php',
        'MWNamespace' => __DIR__ . '/includes/MWNamespace.php',
        'MWOldPassword' => __DIR__ . '/includes/password/MWOldPassword.php',
@@ -779,9 +779,10 @@ $wgAutoloadLocalClasses = array(
        'MediaWiki\\Widget\\NamespaceInputWidget' => __DIR__ . '/includes/widget/NamespaceInputWidget.php',
        'MediaWiki\\Widget\\TitleInputWidget' => __DIR__ . '/includes/widget/TitleInputWidget.php',
        'MediaWiki\\Widget\\UserInputWidget' => __DIR__ . '/includes/widget/UserInputWidget.php',
-       'MemCachedClientforWiki' => __DIR__ . '/includes/libs/objectcache/MemcachedClient.php',
+       'MemCachedClientforWiki' => __DIR__ . '/includes/compat/MemcachedClientCompat.php',
        'MemcLockManager' => __DIR__ . '/includes/filebackend/lockmanager/MemcLockManager.php',
        'MemcachedBagOStuff' => __DIR__ . '/includes/libs/objectcache/MemcachedBagOStuff.php',
+       'MemcachedClient' => __DIR__ . '/includes/libs/objectcache/MemcachedClient.php',
        'MemcachedPeclBagOStuff' => __DIR__ . '/includes/objectcache/MemcachedPeclBagOStuff.php',
        'MemcachedPhpBagOStuff' => __DIR__ . '/includes/libs/objectcache/MemcachedPhpBagOStuff.php',
        'MemoizedCallable' => __DIR__ . '/includes/libs/MemoizedCallable.php',
index 1cdf813..bf4d402 100644 (file)
@@ -21,7 +21,7 @@
                "ext-iconv": "*",
                "liuggio/statsd-php-client": "1.0.16",
                "mediawiki/at-ease": "1.1.0",
-               "oojs/oojs-ui": "0.13.1",
+               "oojs/oojs-ui": "0.13.2",
                "oyejorge/less.php": "1.7.0.9",
                "php": ">=5.3.3",
                "psr/log": "1.0.0",
index 57ba4b3..b058975 100644 (file)
@@ -30,18 +30,19 @@ class FeedUtils {
 
        /**
         * Check whether feed's cache should be cleared; for changes feeds
-        * If the feed should be purged; $timekey and $key will be removed from
-        * $messageMemc
+        * If the feed should be purged; $timekey and $key will be removed from cache
         *
         * @param string $timekey Cache key of the timestamp of the last item
         * @param string $key Cache key of feed's content
         */
        public static function checkPurge( $timekey, $key ) {
-               global $wgRequest, $wgUser, $messageMemc;
+               global $wgRequest, $wgUser;
+
                $purge = $wgRequest->getVal( 'action' ) === 'purge';
                if ( $purge && $wgUser->isAllowed( 'purge' ) ) {
-                       $messageMemc->delete( $timekey );
-                       $messageMemc->delete( $key );
+                       $cache = ObjectCache::getMainWANInstance();
+                       $cache->delete( $timekey, 1 );
+                       $cache->delete( $key, 1 );
                }
        }
 
index 355b03a..d826b87 100644 (file)
@@ -516,6 +516,21 @@ MWExceptionHandler::installHandler();
 
 require_once "$IP/includes/compat/normal/UtfNormalUtil.php";
 
+
+$ps_validation = Profiler::instance()->scopedProfileIn( $fname . '-validation' );
+
+// T48998: Bail out early if $wgArticlePath is non-absolute
+if ( !preg_match( '/^https?:\/\/|\//', $wgArticlePath ) ) {
+       throw new FatalError(
+               'If you use a relative URL on $wgArticlePath, it must start ' .
+               'with a slash (/).<br><br>See ' .
+               '<a href="https://www.mediawiki.org/wiki/Manual:$wgArticlePath">' .
+               'https://www.mediawiki.org/wiki/Manual:$wgArticlePath</a>'
+       );
+}
+
+Profiler::instance()->scopedProfileOut( $ps_validation );
+
 $ps_default2 = Profiler::instance()->scopedProfileIn( $fname . '-defaults2' );
 
 if ( $wgCanonicalServer === false ) {
@@ -627,7 +642,10 @@ $wgMemc = wfGetMainCache();
 $messageMemc = wfGetMessageCacheStorage();
 $parserMemc = wfGetParserCacheStorage();
 
-wfDebugLog( 'caches', 'main: ' . get_class( $wgMemc ) .
+wfDebugLog( 'caches',
+       'cluster: ' . get_class( $wgMemc ) .
+       ', WAN: ' . $wgMainWANCache .
+       ', stash: ' . $wgMainStash .
        ', message: ' . get_class( $messageMemc ) .
        ', parser: ' . get_class( $parserMemc ) );
 
index 1c3a1de..b693118 100644 (file)
@@ -36,6 +36,8 @@
        "apihelp-block-example-user-complex": "Trvale zablokovat uživatele <kbd>Vandal</kbd> s odůvodněním <kbd>Vandalism</kbd> a zabránit vytváření nových účtů a odesílání e-mailů.",
        "apihelp-checktoken-param-type": "Typ testovaného tokenu.",
        "apihelp-checktoken-param-token": "Token, který se má otestovat.",
+       "apihelp-checktoken-param-maxtokenage": "Nejvyšší povolené stáří tokenu v sekundách.",
+       "apihelp-checktoken-example-simple": "Testuje správnost tokenu <kbd>csrf</kbd>.",
        "apihelp-compare-description": "Vrátí rozdíl dvou stránek.\n\nVe „from“ i „to“ musíte zadat číslo revize, název stránky nebo ID stránky.",
        "apihelp-compare-param-fromtitle": "Název první stránky k porovnání.",
        "apihelp-compare-param-fromid": "ID první stránky k porovnání.",
index 1323c70..c04a22a 100644 (file)
@@ -40,8 +40,8 @@
  * Introduced by r47317
  */
 class BacklinkCache {
-       /** @var ProcessCacheLRU */
-       protected static $cache;
+       /** @var BacklinkCache */
+       protected static $instance;
 
        /**
         * Multi dimensions array representing batches. Keys are:
@@ -101,15 +101,10 @@ class BacklinkCache {
         * @return BacklinkCache
         */
        public static function get( Title $title ) {
-               if ( !self::$cache ) { // init cache
-                       self::$cache = new ProcessCacheLRU( 1 );
+               if ( !self::$instance || !self::$instance->title->equals( $title ) ) {
+                       self::$instance = new self( $title );
                }
-               $dbKey = $title->getPrefixedDBkey();
-               if ( !self::$cache->has( $dbKey, 'obj', 3600 ) ) {
-                       self::$cache->set( $dbKey, 'obj', new self( $title ) );
-               }
-
-               return self::$cache->get( $dbKey, 'obj' );
+               return self::$instance;
        }
 
        /**
index ab7e171..dcb358f 100644 (file)
@@ -321,6 +321,19 @@ class MessageBlobStore {
                return $updates;
        }
 
+       /**
+        * @param string $key Message key
+        * @param string $lang Language code
+        * @return string
+        */
+       private function fetchMessage( $key, $lang ) {
+               $message = wfMessage( $key )->inLanguage( $lang );
+               if ( !$message->exists() ) {
+                       wfDebugLog( 'resourceloader', __METHOD__ . " failed to find: '$key' ($lang)" );
+               }
+               return $message->plain();
+       }
+
        /**
         * Reencode a message blob with the updated value for a message
         *
@@ -331,8 +344,7 @@ class MessageBlobStore {
         */
        private function reencodeBlob( $blob, $key, $lang ) {
                $decoded = FormatJson::decode( $blob, true );
-               $decoded[$key] = wfMessage( $key )->inLanguage( $lang )->plain();
-
+               $decoded[$key] = $this->fetchMessage( $key, $lang );
                return FormatJson::encode( (object)$decoded );
        }
 
@@ -390,7 +402,7 @@ class MessageBlobStore {
                $messages = array();
 
                foreach ( $module->getMessages() as $key ) {
-                       $messages[$key] = wfMessage( $key )->inLanguage( $lang )->plain();
+                       $messages[$key] = $this->fetchMessage( $key, $lang );
                }
 
                return FormatJson::encode( (object)$messages );
index 2818d2f..a9af9c4 100644 (file)
@@ -749,11 +749,7 @@ class MessageCache {
 
                Hooks::run( 'MessageCache::get', array( &$lckey ) );
 
-               if ( ord( $lckey ) < 128 ) {
-                       $uckey = ucfirst( $lckey );
-               } else {
-                       $uckey = $wgContLang->ucfirst( $lckey );
-               }
+               $uckey = $wgContLang->ucfirst( $lckey );
 
                // Loop through each language in the fallback list until we find something useful
                $lang = wfGetLangObj( $langcode );
index 28a1cca..dc58835 100644 (file)
@@ -83,7 +83,8 @@ class ChangesFeed {
                }
 
                $optionsHash = md5( serialize( $opts->getAllValues() ) ) . $wgRenderHashAppend;
-               $timekey = wfMemcKey( $this->type, $this->format, $wgLang->getCode(), $optionsHash, 'timestamp' );
+               $timekey = wfMemcKey(
+                       $this->type, $this->format, $wgLang->getCode(), $optionsHash, 'timestamp' );
                $key = wfMemcKey( $this->type, $this->format, $wgLang->getCode(), $optionsHash );
 
                FeedUtils::checkPurge( $timekey, $key );
@@ -110,21 +111,20 @@ class ChangesFeed {
        }
 
        /**
-        * Save to feed result to $messageMemc
+        * Save to feed result to cache
         *
         * @param string $feed Feed's content
         * @param string $timekey Memcached key of the last modification
         * @param string $key Memcached key of the content
         */
        public function saveToCache( $feed, $timekey, $key ) {
-               global $messageMemc;
-               $expire = 3600 * 24; # One day
-               $messageMemc->set( $key, $feed, $expire );
-               $messageMemc->set( $timekey, wfTimestamp( TS_MW ), $expire );
+               $cache = ObjectCache::getMainWANInstance();
+               $cache->set( $key, $feed, $cache::TTL_DAY );
+               $cache->set( $timekey, wfTimestamp( TS_MW ), $cache::TTL_DAY );
        }
 
        /**
-        * Try to load the feed result from $messageMemc
+        * Try to load the feed result from cache
         *
         * @param int $lastmod Timestamp of the last item in the recentchanges table
         * @param string $timekey Memcached key of the last modification
@@ -132,9 +132,10 @@ class ChangesFeed {
         * @return string|bool Feed's content on cache hit or false on cache miss
         */
        public function loadFromCache( $lastmod, $timekey, $key ) {
-               global $wgFeedCacheTimeout, $wgOut, $messageMemc;
+               global $wgFeedCacheTimeout, $wgOut;
 
-               $feedLastmod = $messageMemc->get( $timekey );
+               $cache = ObjectCache::getMainWANInstance();
+               $feedLastmod = $cache->get( $timekey );
 
                if ( ( $wgFeedCacheTimeout > 0 ) && $feedLastmod ) {
                        /**
@@ -153,7 +154,7 @@ class ChangesFeed {
                                if ( $feedLastmodUnix < $lastmodUnix ) {
                                        $wgOut->setLastModified( $feedLastmod ); // bug 21916
                                }
-                               return $messageMemc->get( $key );
+                               return $cache->get( $key );
                        } else {
                                wfDebug( "RC: cached feed timestamp check failed ($feedLastmod; $lastmod)\n" );
                        }
@@ -164,7 +165,7 @@ class ChangesFeed {
        /**
         * Generate the feed items given a row from the database, printing the feed.
         * @param object $rows DatabaseBase resource with recentchanges rows
-        * @param Feed $feed
+        * @param ChannelFeed $feed
         */
        public static function generateFeed( $rows, &$feed ) {
                $items = self::buildItems( $rows );
diff --git a/includes/compat/MemcachedClientCompat.php b/includes/compat/MemcachedClientCompat.php
new file mode 100644 (file)
index 0000000..2304733
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Backward-compatibility alias for MemcachedClient
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @since 1.27
+ * @file
+ */
+
+/**
+ * @deprecated since 1.27
+ */
+class MWMemcached extends MemcachedClient {
+}
+
+/**
+ * @deprecated since 1.27
+ */
+class MemCachedClientforWiki extends MWMemcached {
+}
index 0c7b612..6840d17 100644 (file)
@@ -60,7 +60,7 @@ class ChronologyProtector {
                if ( !empty( $this->startupPositions[$masterName] ) ) {
                        $info = $lb->parentInfo();
                        $pos = $this->startupPositions[$masterName];
-                       wfDebug( __METHOD__ . ": LB " . $info['id'] . " waiting for master pos $pos\n" );
+                       wfDebug( __METHOD__ . ": LB '" . $info['id'] . "' waiting for master pos $pos\n" );
                        $lb->waitFor( $pos );
                }
        }
index 60cf0ec..99716a5 100644 (file)
@@ -3314,6 +3314,11 @@ abstract class DatabaseBase implements IDatabase {
                                                MWExceptionHandler::logException( $ePrior );
                                        }
                                        $ePrior = $e;
+                                       // Some callbacks may use startAtomic/endAtomic, so make sure
+                                       // their transactions are ended so other callbacks don't fail
+                                       if ( $this->trxLevel() ) {
+                                               $this->rollback( __METHOD__ );
+                                       }
                                }
                        }
                } while ( count( $this->mTrxIdleCallbacks ) );
index 907cdbf..4f93419 100644 (file)
@@ -1354,6 +1354,21 @@ class MySQLMasterPos implements DBMasterPos {
                $this->asOfTime = microtime( true );
        }
 
+       function asOfTime() {
+               return $this->asOfTime;
+       }
+
+       function hasReached( DBMasterPos $pos ) {
+               if ( !( $pos instanceof self ) ) {
+                       throw new InvalidArgumentException( "Position not an instance of " . __CLASS__ );
+               }
+
+               $thisPos = $this->getCoordinates();
+               $thatPos = $pos->getCoordinates();
+
+               return ( $thisPos && $thatPos && $thisPos >= $thatPos );
+       }
+
        function __toString() {
                // e.g db1034-bin.000976/843431247
                return "{$this->file}/{$this->pos}";
@@ -1370,15 +1385,4 @@ class MySQLMasterPos implements DBMasterPos {
 
                return false;
        }
-
-       function hasReached( MySQLMasterPos $pos ) {
-               $thisPos = $this->getCoordinates();
-               $thatPos = $pos->getCoordinates();
-
-               return ( $thisPos && $thatPos && $thisPos >= $thatPos );
-       }
-
-       function asOfTime() {
-               return $this->asOfTime;
-       }
 }
index 969ed5e..dea7d94 100644 (file)
@@ -321,6 +321,20 @@ class LikeMatch {
 interface DBMasterPos {
        /**
         * @return float UNIX timestamp
+        * @since 1.25
         */
        public function asOfTime();
+
+       /**
+        * @param DBMasterPos $pos
+        * @return bool Whether this position is at or higher than $pos
+        * @since 1.27
+        */
+       public function hasReached( DBMasterPos $pos );
+
+       /**
+        * @return string
+        * @since 1.27
+        */
+       public function __toString();
 }
index a5a5c37..052a0fa 100644 (file)
@@ -47,6 +47,8 @@ class LoadBalancer {
        private $mLoadMonitorClass;
        /** @var LoadMonitor */
        private $mLoadMonitor;
+       /** @var BagOStuff */
+       private $srvCache;
 
        /** @var bool|DatabaseBase Database connection that caused a problem */
        private $mErrorConnection;
@@ -123,6 +125,8 @@ class LoadBalancer {
                                }
                        }
                }
+
+               $this->srvCache = ObjectCache::getLocalServerInstance();
        }
 
        /**
@@ -445,17 +449,27 @@ class LoadBalancer {
        protected function doWait( $index, $open = false, $timeout = null ) {
                $close = false; // close the connection afterwards
 
-               # Find a connection to wait on, creating one if needed and allowed
+               // Check if we already know that the DB has reached this point
+               $server = $this->getServerName( $index );
+               $key = $this->srvCache->makeGlobalKey( __CLASS__, 'last-known-pos', $server );
+               /** @var DBMasterPos $knownReachedPos */
+               $knownReachedPos = $this->srvCache->get( $key );
+               if ( $knownReachedPos && $knownReachedPos->hasReached( $this->mWaitForPos ) ) {
+                       wfDebugLog( 'replication', __METHOD__ . ": Slave $server known to be caught up.\n" );
+                       return true;
+               }
+
+               // Find a connection to wait on, creating one if needed and allowed
                $conn = $this->getAnyOpenConnection( $index );
                if ( !$conn ) {
                        if ( !$open ) {
-                               wfDebug( __METHOD__ . ": no connection open\n" );
+                               wfDebugLog( 'replication', __METHOD__ . ": no connection open for $server\n" );
 
                                return false;
                        } else {
                                $conn = $this->openConnection( $index, '' );
                                if ( !$conn ) {
-                                       wfDebug( __METHOD__ . ": failed to open connection\n" );
+                                       wfDebugLog( 'replication', __METHOD__ . ": failed to open connection to $server\n" );
 
                                        return false;
                                }
@@ -465,20 +479,21 @@ class LoadBalancer {
                        }
                }
 
-               wfDebug( __METHOD__ . ": Waiting for slave #$index to catch up...\n" );
+               wfDebugLog( 'replication', __METHOD__ . ": Waiting for slave $server to catch up...\n" );
                $timeout = $timeout ?: $this->mWaitTimeout;
                $result = $conn->masterPosWait( $this->mWaitForPos, $timeout );
 
                if ( $result == -1 || is_null( $result ) ) {
-                       # Timed out waiting for slave, use master instead
-                       $server = $server = $this->getServerName( $index );
+                       // Timed out waiting for slave, use master instead
                        $msg = __METHOD__ . ": Timed out waiting on $server pos {$this->mWaitForPos}";
-                       wfDebug( "$msg\n" );
+                       wfDebugLog( 'replication', "$msg\n" );
                        wfDebugLog( 'DBPerformance', "$msg:\n" . wfBacktrace( true ) );
                        $ok = false;
                } else {
-                       wfDebug( __METHOD__ . ": Done\n" );
+                       wfDebugLog( 'replication', __METHOD__ . ": Done\n" );
                        $ok = true;
+                       // Remember that the DB reached this point
+                       $this->srvCache->set( $key, $this->mWaitForPos, BagOStuff::TTL_DAY );
                }
 
                if ( $close ) {
@@ -579,7 +594,6 @@ class LoadBalancer {
                $refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
                if ( $serverIndex === null || $refCount === null ) {
                        wfDebug( __METHOD__ . ": this connection was not opened as a foreign connection\n" );
-
                        /**
                         * This can happen in code like:
                         *   foreach ( $dbs as $db ) {
@@ -590,7 +604,6 @@ class LoadBalancer {
                         * When a connection to the local DB is opened in this way, reuseConnection()
                         * should be ignored
                         */
-
                        return;
                }
 
@@ -1158,7 +1171,8 @@ class LoadBalancer {
                if ( !$this->laggedSlaveMode && $this->getServerCount() > 1 ) {
                        try {
                                // See if laggedSlaveMode gets set
-                               $this->getConnection( DB_SLAVE, false, $wiki );
+                               $conn = $this->getConnection( DB_SLAVE, false, $wiki );
+                               $this->reuseConnection( $conn );
                        } catch ( DBConnectionError $e ) {
                                // Avoid expensive re-connect attempts and failures
                                $this->slavesDownMode = true;
index 4f53087..d33e03b 100644 (file)
@@ -1,5 +1,9 @@
 {
-       "@metadata": [],
+       "@metadata": {
+               "authors": [
+                       "Anggoro"
+               ]
+       },
        "mainpagetext": "'''Prangkat empuk wiki wis suksès dipasang.'''",
-       "mainpagedocfooter": "Mangga maca [//meta.wikimedia.org/wiki/Help:Contents User's Guide] kanggo katrangan luwih langkung prakara panggunan prangkat empuk wiki\n== Miwiti panggunan  ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Daftar pangaturan préférènsi]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Milis rilis MediaWiki]"
+       "mainpagedocfooter": "Mangga maca [//meta.wikimedia.org/wiki/Help:Contents User's Guide] kanggo katrangan luwih langkung prakara panggunan prangkat empuk wiki\n== Miwiti panggunan  ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Daftar pangaturan préférènsi]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Milis rilis MediaWiki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Learn how to combat spam on your wiki]"
 }
index 7ce731d..318330e 100644 (file)
@@ -467,7 +467,7 @@ class JobRunner implements LoggerAwareInterface {
 
                $ms = intval( 1000 * $dbwSerial->pendingWriteQueryDuration() );
                $msg = $job->toString() . " COMMIT ENQUEUED [{$ms}ms of writes]";
-               $this->logger->info( $msg );
+               $this->logger->warning( $msg );
                $this->debugCallback( $msg );
 
                // Wait for an exclusive lock to commit
index 6c49646..f8a0040 100644 (file)
@@ -59,7 +59,11 @@ class JobQueueAggregatorRedis extends JobQueueAggregator {
                        return false;
                }
                try {
-                       $conn->hDel( $this->getReadyQueueKey(), $this->encQueueName( $type, $wiki ) );
+                       // Make sure doNotifyQueueNonEmpty() takes precedence to avoid races
+                       $conn->watch( $this->getReadyQueueKey() );
+                       $conn->multi()
+                               ->hDel( $this->getReadyQueueKey(), $this->encQueueName( $type, $wiki ) )
+                               ->exec();
 
                        return true;
                } catch ( RedisException $e ) {
index 25c3ba4..5f1a1a6 100644 (file)
@@ -216,10 +216,10 @@ class RefreshLinksJob extends Job {
                        $title, null, !empty( $this->params['useRecursiveLinksUpdate'] ), $parserOutput );
                foreach ( $updates as $key => $update ) {
                        if ( $update instanceof LinksUpdate ) {
-                               if ( isset( $this->params['triggeredRecursive'] ) ) {
+                               if ( !empty( $this->params['triggeredRecursive'] ) ) {
                                        $update->setTriggeredRecursive();
                                }
-                               if ( isset( $this->params['triggeringUser'] ) && $this->params['triggeringUser'] ) {
+                               if ( !empty( $this->params['triggeringUser'] ) ) {
                                        $userInfo = $this->params['triggeringUser'];
                                        if ( $userInfo['userId'] ) {
                                                $user = User::newFromId( $userInfo['userId'] );
@@ -229,7 +229,7 @@ class RefreshLinksJob extends Job {
                                        }
                                        $update->setTriggeringUser( $user );
                                }
-                               if ( isset( $this->params['triggeringRevisionId'] ) && $this->params['triggeringRevisionId'] ) {
+                               if ( !empty( $this->params['triggeringRevisionId'] ) ) {
                                        $revision = Revision::newFromId( $this->params['triggeringRevisionId'] );
                                        if ( $revision === null ) {
                                                $revision = Revision::newFromId(
index 162a531..7a36214 100644 (file)
@@ -27,7 +27,7 @@
  * @ingroup Cache
  */
 class MemcachedBagOStuff extends BagOStuff {
-       /** @var MWMemcached|Memcached */
+       /** @var MemcachedClient|Memcached */
        protected $client;
 
        /**
@@ -86,7 +86,7 @@ class MemcachedBagOStuff extends BagOStuff {
        /**
         * Get the underlying client object. This is provided for debugging
         * purposes.
-        * @return BagOStuff
+        * @return MemcachedClient|Memcached
         */
        public function getClient() {
                return $this->client;
index 5010b89..8007a53 100644 (file)
  */
 
 /**
- * This is the PHP client for memcached - a distributed memory cache daemon.
+ * This is a PHP client for memcached - a distributed memory cache daemon.
+ *
  * More information is available at http://www.danga.com/memcached/
  *
  * Usage example:
  *
- * require_once 'memcached.php';
- *
- * $mc = new MWMemcached(array(
- *              'servers' => array('127.0.0.1:10000',
- *                                 array('192.0.0.1:10010', 2),
- *                                 '127.0.0.1:10020'),
- *              'debug'   => false,
- *              'compress_threshold' => 10240,
- *              'persistent' => true));
+ *     $mc = new MemcachedClient(array(
+ *         'servers' => array(
+ *             '127.0.0.1:10000',
+ *             array( '192.0.0.1:10010', 2 ),
+ *             '127.0.0.1:10020'
+ *         ),
+ *         'debug'   => false,
+ *         'compress_threshold' => 10240,
+ *         'persistent' => true
+ *     ));
  *
- * $mc->add( 'key', array( 'some', 'array' ) );
- * $mc->replace( 'key', 'some random string' );
- * $val = $mc->get( 'key' );
+ *     $mc->add( 'key', array( 'some', 'array' ) );
+ *     $mc->replace( 'key', 'some random string' );
+ *     $val = $mc->get( 'key' );
  *
- * @author  Ryan T. Dean <rtdean@cytherianage.net>
+ * @author Ryan T. Dean <rtdean@cytherianage.net>
  * @version 0.1.2
  */
 
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 
-// {{{ requirements
-// }}}
-
-// {{{ class MWMemcached
+// {{{ class MemcachedClient
 /**
  * memcached client class implemented using (p)fsockopen()
  *
  * @author  Ryan T. Dean <rtdean@cytherianage.net>
  * @ingroup Cache
  */
-class MWMemcached {
+class MemcachedClient {
        // {{{ properties
        // {{{ public
 
@@ -659,7 +658,7 @@ class MWMemcached {
        // {{{ set_compress_threshold()
 
        /**
-        * Sets the compression threshold
+        * Set the compression threshold
         *
         * @param int $thresh Threshold to compress if larger than
         */
@@ -671,11 +670,10 @@ class MWMemcached {
        // {{{ set_debug()
 
        /**
-        * Sets the debug flag
+        * Set the debug flag
         *
+        * @see __construct()
         * @param bool $dbg True for debugging, false otherwise
-        *
-        * @see MWMemcached::__construct
         */
        public function set_debug( $dbg ) {
                $this->_debug = $dbg;
@@ -685,11 +683,10 @@ class MWMemcached {
        // {{{ set_servers()
 
        /**
-        * Sets the server list to distribute key gets and puts between
+        * Set the server list to distribute key gets and puts between
         *
+        * @see __construct()
         * @param array $list Array of servers to connect to
-        *
-        * @see MWMemcached::__construct()
         */
        public function set_servers( $list ) {
                $this->_servers = $list;
@@ -1271,6 +1268,3 @@ class MWMemcached {
 }
 
 // }}}
-
-class MemCachedClientforWiki extends MWMemcached {
-}
index 1bb38fa..e2d9d93 100644 (file)
@@ -42,7 +42,7 @@ class MemcachedPhpBagOStuff extends MemcachedBagOStuff {
                parent::__construct( $params );
                $params = $this->applyDefaultParams( $params );
 
-               $this->client = new MWMemcached( $params );
+               $this->client = new MemcachedClient( $params );
                $this->client->set_servers( $params['servers'] );
                $this->client->set_debug( $params['debug'] );
        }
index 49ce21c..85595f1 100644 (file)
@@ -64,7 +64,7 @@ class UserMailer {
         *
         * @return string
         */
-       static function arrayToHeaderString( $headers, $endl = "\n" ) {
+       static function arrayToHeaderString( $headers, $endl = PHP_EOL ) {
                $strings = array();
                foreach ( $headers as $name => $value ) {
                        // Prevent header injection by stripping newlines from value
@@ -292,11 +292,7 @@ class UserMailer {
 
                // Line endings need to be different on Unix and Windows due to
                // the bug described at http://trac.wordpress.org/ticket/2603
-               if ( wfIsWindows() ) {
-                       $endl = "\r\n";
-               } else {
-                       $endl = "\n";
-               }
+               $endl = PHP_EOL;
 
                if ( is_array( $body ) ) {
                        // we are sending a multipart message
index b7d1eaf..aa21cbb 100644 (file)
@@ -43,7 +43,7 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
         *                          values, but serialization is much slower unless the php.ini option
         *                          igbinary.compact_strings is off.
         * @param array $params
-        * @throws MWException
+        * @throws InvalidArgumentException
         */
        function __construct( $params ) {
                parent::__construct( $params );
@@ -89,7 +89,7 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
                // is as good as any. There's no way to configure libmemcached to use
                // hashes identical to the ones currently in use by the PHP client, and
                // even implementing one of the libmemcached hashes in pure PHP for
-               // forwards compatibility would require MWMemcached::get_sock() to be
+               // forwards compatibility would require MemcachedClient::get_sock() to be
                // rewritten.
                $this->client->setOption( Memcached::OPT_LIBKETAMA_COMPATIBLE, true );
 
index a6f2d37..5776519 100644 (file)
@@ -27,8 +27,6 @@
  * @ingroup Cache
  */
 class SqlBagOStuff extends BagOStuff {
-       /** @var LoadBalancer */
-       protected $lb;
        /** @var array */
        protected $serverInfos;
        /** @var array */
@@ -150,15 +148,13 @@ class SqlBagOStuff extends BagOStuff {
                                $db = DatabaseBase::factory( $type, $info );
                                $db->clearFlag( DBO_TRX );
                        } else {
-                               /*
-                                * We must keep a separate connection to MySQL in order to avoid deadlocks
-                                * However, SQLite has an opposite behavior. And PostgreSQL needs to know
-                                * if we are in transaction or no
-                                */
+                               // We must keep a separate connection to MySQL in order to avoid deadlocks
+                               // However, SQLite has an opposite behavior. And PostgreSQL needs to know
+                               // if we are in transaction or not (@TODO: find some work-around).
                                $index = $this->slaveOnly ? DB_SLAVE : DB_MASTER;
                                if ( wfGetDB( $index )->getType() == 'mysql' ) {
-                                       $this->lb = wfGetLBFactory()->newMainLB();
-                                       $db = $this->lb->getConnection( $index );
+                                       $lb = wfGetLBFactory()->newMainLB();
+                                       $db = $lb->getConnection( $index );
                                        $db->clearFlag( DBO_TRX ); // auto-commit mode
                                } else {
                                        $db = wfGetDB( $index );
index ed1c903..8109cc9 100644 (file)
@@ -446,7 +446,7 @@ class AllMessagesTablePager extends TablePager {
                        return array( 'class' => $field );
                } else {
                        return array(
-                               'lang' => wfBCP47( $this->langcode ),
+                               'lang' => $this->lang->getHtmlCode(),
                                'dir' => $this->lang->getDir(),
                                'class' => $field
                        );
index 2446270..190fe8f 100644 (file)
@@ -205,8 +205,7 @@ class SpecialAllPages extends IncludableSpecialPage {
                        );
 
                        if ( $res->numRows() > 0 ) {
-                               $out = Html::openElement( 'div', array( 'class' => 'mw-allpages-body' ) );
-                               $out .= Html::openElement( 'ul', array( 'class' => 'mw-allpages-chunk' ) );
+                               $out = Html::openElement( 'ul', array( 'class' => 'mw-allpages-chunk' ) );
 
                                while ( ( $n < $this->maxPerPage ) && ( $s = $res->fetchObject() ) ) {
                                        $t = Title::newFromRow( $s );
@@ -222,7 +221,12 @@ class SpecialAllPages extends IncludableSpecialPage {
                                        $n++;
                                }
                                $out .= Html::closeElement( 'ul' );
-                               $out .= Html::closeElement( 'div' );
+
+                               if ( $res->numRows() > 2 ) {
+                                       // Only apply CSS column styles if there's more than 2 entries.
+                                       // Otherwise, rendering is broken as "mw-allpages-body"'s CSS column count is 3.
+                                       $out = Html::rawElement( 'div', array( 'class' => 'mw-allpages-body' ), $out );
+                               }
                        } else {
                                $out = '';
                        }
index fbe5ab3..f10a979 100644 (file)
@@ -205,8 +205,7 @@ class SpecialPrefixindex extends SpecialAllPages {
 
                        $n = 0;
                        if ( $res->numRows() > 0 ) {
-                               $out = Html::openElement( 'div', array( 'class' => 'mw-prefixindex-body' ) );
-                               $out .= Html::openElement( 'ul', array( 'class' => 'mw-prefixindex-list' ) );
+                               $out = Html::openElement( 'ul', array( 'class' => 'mw-prefixindex-list' ) );
 
                                $prefixLength = strlen( $prefix );
                                while ( ( $n < $this->maxPerPage ) && ( $s = $res->fetchObject() ) ) {
@@ -228,12 +227,17 @@ class SpecialPrefixindex extends SpecialAllPages {
                                                $link = '[[' . htmlspecialchars( $s->page_title ) . ']]';
                                        }
 
-                                       $out .= "<li> $link </li>\n";
+                                       $out .= "<li>$link</li>\n";
                                        $n++;
 
                                }
                                $out .= Html::closeElement( 'ul' );
-                               $out .= Html::closeElement( 'div' );
+
+                               if ( $res->numRows() > 2 ) {
+                                       // Only apply CSS column styles if there's more than 2 entries.
+                                       // Otherwise rendering is broken as "mw-prefixindex-body"'s CSS column count is 3.
+                                       $out = Html::rawElement( 'div', array( 'class' => 'mw-prefixindex-body' ), $out );
+                               }
                        } else {
                                $out = '';
                        }
index 2199467..f9ebdec 100644 (file)
@@ -35,8 +35,7 @@ class LanguageAz extends Language {
        function ucfirst( $string ) {
                if ( $string[0] == 'i' ) {
                        return 'İ' . substr( $string, 1 );
-               } else {
-                       return parent::ucfirst( $string );
                }
+               return parent::ucfirst( $string );
        }
 }
index eab08fe..0cd0c0d 100644 (file)
@@ -57,9 +57,8 @@ class LanguageKaa extends Language {
        function ucfirst( $string ) {
                if ( substr( $string, 0, 1 ) === 'i' ) {
                        return 'İ' . substr( $string, 1 );
-               } else {
-                       return parent::ucfirst( $string );
                }
+               return parent::ucfirst( $string );
        }
 
        /**
@@ -72,9 +71,8 @@ class LanguageKaa extends Language {
        function lcfirst( $string ) {
                if ( substr( $string, 0, 1 ) === 'I' ) {
                        return 'ı' . substr( $string, 1 );
-               } else {
-                       return parent::lcfirst( $string );
                }
+               return parent::lcfirst( $string );
        }
 
        /**
index b8af885..0f60889 100644 (file)
@@ -422,13 +422,13 @@ class LanguageKk extends LanguageKk_cyrl {
         * @return string
         */
        function ucfirst( $string ) {
-               $variant = $this->getPreferredVariant();
-               if ( ( $variant == 'kk-latn' || $variant == 'kk-tr' ) && $string[0] == 'i' ) {
-                       $string = 'İ' . substr( $string, 1 );
-               } else {
-                       $string = parent::ucfirst( $string );
+               if ( $string[0] == 'i' ) {
+                       $variant = $this->getPreferredVariant();
+                       if ( $variant == 'kk-latn' || $variant == 'kk-tr' ) {
+                               return 'İ' . substr( $string, 1 );
+                       }
                }
-               return $string;
+               return parent::ucfirst( $string );
        }
 
        /**
@@ -439,13 +439,13 @@ class LanguageKk extends LanguageKk_cyrl {
         * @return string
         */
        function lcfirst( $string ) {
-               $variant = $this->getPreferredVariant();
-               if ( ( $variant == 'kk-latn' || $variant == 'kk-tr' ) && $string[0] == 'I' ) {
-                       $string = 'ı' . substr( $string, 1 );
-               } else {
-                       $string = parent::lcfirst( $string );
+               if ( $string[0] == 'I' ) {
+                       $variant = $this->getPreferredVariant();
+                       if ( $variant == 'kk-latn' || $variant == 'kk-tr' ) {
+                               return 'ı' . substr( $string, 1 );
+                       }
                }
-               return $string;
+               return parent::lcfirst( $string );
        }
 
        /**
index 8431c3f..a4f05f8 100644 (file)
@@ -40,9 +40,8 @@ class LanguageTr extends Language {
        function ucfirst( $string ) {
                if ( strlen( $string ) && $string[0] == 'i' ) {
                        return 'İ' . substr( $string, 1 );
-               } else {
-                       return parent::ucfirst( $string );
                }
+               return parent::ucfirst( $string );
        }
 
        /**
@@ -52,9 +51,8 @@ class LanguageTr extends Language {
        function lcfirst( $string ) {
                if ( strlen( $string ) && $string[0] == 'I' ) {
                        return 'ı' . substr( $string, 1 );
-               } else {
-                       return parent::lcfirst( $string );
                }
+               return parent::lcfirst( $string );
        }
 
 }
index 0e802b8..e70469e 100644 (file)
        "cannotdelete-title": "НэкIубгъоу \"$1\" тегъэкIыгъэн лъэкIырэп",
        "badtitle": "ЦӀэ дэгъуэп",
        "badtitletext": "УзкIэупчIэрэ нэкIубгъом ыцIэр къуанчэ, е нэкIы, е бзэзэпыщэ е интервики гъэнэфагъэп.\nМыхъущт символ агъэфедагъэнкIи мэхъу.",
-       "title-invalid-characters": "УзыкIэупчIэрэ нэкIубгъуацIэм символ фыкъуагъэ хэт: \"$1\".",
+       "title-invalid-characters": "УзкIэупчIэрэ нэкIубгъуацIэм символ фыкъуагъэ хэт: \"$1\".",
        "viewsource": "Еплъ лъапсэм",
        "viewsource-title": "Еплъ лъапсэм $1 пае",
        "protectedpagetext": "ЕIэзэнхэм ыкIи нэмыкI шIэнмэ яягъэ къэмыкIынэу мы нэкIубгъор ухъумагъэу щыт.",
index 0f7a605..a7122b5 100644 (file)
        "noemailcreate": "باید یک موتبرین ایمیل ادرسئ داخل بکنیت",
        "passwordsent": "نوکین چیهرگال بئ سبت بوته ئین ایمیل ادرس ئا په «$1» ئا دیم داته بوت.\nمهربانی بکنیت شه آیی گیپتین ئا پد لوگین به ییت.",
        "blocked-mailpassword": "شمئ آی پي ادرس شه دستکاری کورتینا بلاک بوته، په ای خاطرا شه پاسوردا پایده گ زورته ئه نه کنیت شه سوءاستفاده ئی دیمگیري ئی خاطرا.",
+       "eauthentsent": "یک تائیدین ایمیل په مورد نظرین ایمیل آدرسا دیم داته بوت.\nدیم شه ایشی که دیگه ایمیلئ په ای ایمیل ادرسا دیم داتینئ وڑ بیت، شما باید بعضی دستورانه که په ای مسئله ئی خاتیرا انت، اجرا کنێت.",
        "mailerror": "خطا بی ایمیلی داتینی تا: $1",
        "emailauthenticated": "شمی ایمیلی ادرس بی $2 سائت $3  تا تصدیق ئه بیئت.",
        "emailnotauthenticated": "شمی ایمیلئ ادرس تا انون قبول نه بوته.\nایمیل په هیچ یک شه ویژه گی ئان دیم داته ئه نه بیئت.",
        "accountcreatedtext": "کار زوروکئ حساب په [[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|هبر و گپ]]) جوڑ بوته.",
        "createaccount-title": "کار زوروکئ حساب جوڑ کورتین بی {{SITENAME}}",
        "createaccount-text": "یک نفری په شمی ایمیلا یک کار زوروکین حسابئ  بی {{SITENAME}} تا گو «$2» ناما جوڑ کورته ($4)، که آیی پاسورد ایش اینت : $3\nشما باید همی انون وتئ حسابئ تا داخل بئیت و وتئ پاسوردا تغیر بدهیت .\n\nاگه ای حساب اشتباهی جوڑ بوته ، ای پیام ئا نادیستگ بگیریت.",
+       "login-throttled": "شما بیخی باز وار په لوگین بوتینا کوشش کورته ایت.\nمهرباني بکنێت دیم شه آیی که پدا کوشش بکنیت $1 صبر کنیت.",
        "login-abort-generic": "شمی لوگین ناکام ات - ساکت بوت",
        "login-migrated-generic": "شمی کار زوروکین نام انتقال بوته، و شمی کار زوروکین نام دیگه بی ای ویکی ئی تا وجود نداریت.",
        "loginlanguagelabel": "زبان: $1",
index f8d3a0a..9b1f159 100644 (file)
        "enhancedrc-history": "истори",
        "recentchanges": "Улшăнусем",
        "recentchanges-legend": "Çĕнĕ улшăнусен ĕнерлевĕ",
+       "recentchanges-label-newpage": "Çĕнĕ страница тунă пулнă",
+       "recentchanges-label-minor": "Нумайах мар улăштарни",
        "recentchanges-label-bot": "Ку улшăнăва бот тунă",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (пăхăр [[Special:NewPages|çĕнĕ страницăсен списокĕ]])",
        "recentchanges-legend-plusminus": "(<em>±123</em>)",
index f160f09..dd2549f 100644 (file)
        "nextn-title": "Hurrengo $1 {{PLURAL:$1|emaitza|emaitzak}}",
        "shown-title": "Erakutsi {{PLURAL:$1|emaitza $1|$1 emaitza}} orrialdeko",
        "viewprevnext": "Ikusi ($1 {{int:pipe-separator}} $2) ($3).",
-       "searchmenu-exists": "'''«[[:$1]]» izena duen orri bat bada wiki honetan.''' {{PLURAL:$2|0=|Ikus, gainera, aurkitutako beste bilaketa emaitzak.}}",
-       "searchmenu-new": "<strong>Sortu «[[:$1]]» orria wiki honetan!<strong> {{PLURAL:$2|0=|Ikus, gainera, zure bilaketarekin aurkitutako orria.|Ikus, gainera, bilaketaren emaitzak.}}",
+       "searchmenu-exists": "<strong>«[[:$1]]» izena duen orri bat bada wiki honetan.</strong> {{PLURAL:$2|0=|Ikus, gainera, aurkitutako beste bilaketa emaitzak.}}",
+       "searchmenu-new": "<strong>Sortu «[[:$1]]» orria wiki honetan!</strong> {{PLURAL:$2|0=|Ikus, gainera, zure bilaketarekin aurkitutako orria.|Ikus, gainera, bilaketaren emaitzak.}}",
        "searchprofile-articles": "Eduki-orriak",
        "searchprofile-images": "Multimedia",
        "searchprofile-everything": "Guztia",
        "articleexists": "Izen hori duen artikulu bat badago edo hautatutako izena ez da baliozkoa. Mesedez, beste izen bat aukeratu.",
        "cantmove-titleprotected": "Ezin duzu orrialde bat leku honetara mugitu izenburu berri hori sor ez dadin babesa duelako",
        "movetalk": "Eztabaida orrialdea ere mugitu, ahal bada.",
-       "move-subpages": "Azpiorrialde guztiak ($1-tik gora) mugitu",
-       "move-talk-subpages": "Azpiorrialdeen eztabaida orrialde guztiak ($1-tik gora) mugitu",
+       "move-subpages": "Mugitu azpiorri guztiak ($1 gehienez)",
+       "move-talk-subpages": "Mugitu azpiorrien eztabaida orri guztiak (gehienez $1)",
        "movepage-page-exists": "$1 orrialdea jada badago eta ezin da automatikoki gainetik idatzi.",
        "movepage-page-moved": "«$1» orria «$2» izenera aldatu da.",
        "movepage-page-unmoved": "$1 orrialdea ezin da $2(e)ra mugitu.",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|wikilariak}} «$3» orria «$4» izenera aldatu du, birzuzenketarik utzi gabe",
        "logentry-move-move_redir": "$1 {{GENDER:$2|wikilariak}} «$3» orria «$4» izenera aldatu du, birzuzenketaren gainetik",
        "logentry-move-move_redir-noredirect": "$1 {{GENDER:wikilariak}} «$3» orria «$4» izenera aldatu du, birbideratze bat gainidatzita, birbideratzerik utzi gabe",
-       "logentry-patrol-patrol": "$1(e)k $3 orrialdearen $4 berrikuzpena patruilatutzat {{GENDER:$2|markatu}} du",
+       "logentry-patrol-patrol": "$1(e)k $3 orrialdearen $4 berrikuspena patruilatutzat {{GENDER:$2|markatu}} du",
        "logentry-newusers-newusers": "$1 erabiltzaile kontua sortu da",
        "logentry-newusers-create": "$1 erabiltzaile kontua {{GENDER:$2|sortu da}}",
        "logentry-newusers-create2": "$1 wikilariak $3 erabiltzaile kontua sortu du",
index 39bdce7..77ffa1e 100644 (file)
        "missingarticle-rev": "(شمارهٔ نسخه: $1)",
        "missingarticle-diff": "(تفاوت: $1، $2)",
        "readonly_lag": "پایگاه داده به طور خودکار قفل شده‌است تا نسخه‌های پشتیبان با نسخهٔ اصلی هماهنگ شوند",
+       "nonwrite-api-promise-error": "سرآیند اچ‌تی‌تی‌پی 'Promise-Non-Write-API-Action' ارسال شد ولی درخواست به یک رابط برنامه‌نویسی پودمان نوشتن بود.",
        "internalerror": "خطای داخلی",
        "internalerror_info": "خطای داخلی: $1",
        "internalerror-fatal-exception": "نوع استثنای مخرب \"$1\"",
        "showingresultsinrange": "نمایش در پائین تا {{PLURAL:$1|<strong>1</strong> نتیجه|<strong>$1</strong> نتیجه}} در محدودهٔ #<strong>$2</strong> تا #<strong>$3</strong>.",
        "search-showingresults": "{{PLURAL:$4|نتایج <strong>$1</strong> از <strong>$3</strong>|نتایج <strong>$1 - $2</strong> از <strong>$3</strong>}}",
        "search-nonefound": "نتیجه‌ای منطبق با درخواست پیدا نشد.",
+       "search-nonefound-thiswiki": "نتیجه‌ای منطبق با این درخواست در این وب‌گاه موجود نبود.",
        "powersearch-legend": "جستجوی پیشرفته",
        "powersearch-ns": "جستجو در فضاهای نام:",
        "powersearch-togglelabel": "بررسی:",
index 6e882aa..24ad65b 100644 (file)
        "missingarticle-rev": "(numero della versione: $1)",
        "missingarticle-diff": "(Diff: $1, $2)",
        "readonly_lag": "Il database è stato bloccato automaticamente per consentire ai server con i database slave di sincronizzarsi con il master",
+       "nonwrite-api-promise-error": "È stata inviata l'intestazione HTTP 'Promise-Non-Write-API-Action', ma la richiesta era di un modulo API in scrittura.",
        "internalerror": "Errore interno",
        "internalerror_info": "Errore interno: $1",
        "internalerror-fatal-exception": "Errore irreversibile di tipo \"$1\"",
index 685422f..ac66678 100644 (file)
        "missingarticle-rev": "(версия № $1)",
        "missingarticle-diff": "(разность: $1, $2)",
        "readonly_lag": "База данных автоматически заблокирована от изменений на время, пока вторичные сервера базы данных не синхронизируются с первичным.",
+       "nonwrite-api-promise-error": "Был отправлен HTTP-заголовок 'Promise-Non-Write-API-Action', но запрос был к API-модулю записи.",
        "internalerror": "Внутренняя ошибка",
        "internalerror_info": "Внутренняя ошибка: $1",
        "internalerror-fatal-exception": "Неустранимое исключение типа «$1»",
        "listredirects": "Список перенаправлений",
        "listduplicatedfiles": "Список файлов с дубликатами",
        "listduplicatedfiles-summary": "Это список файлов, где самая последняя версия файла считается дубликатом последней версии некоторых других файлов. Учитываются только локальные файлы.",
-       "listduplicatedfiles-entry": "У файла [[:File:$1|$1]] — [[$3|{{PLURAL:$2|один дубликат|$2 дубликата|$2 дубликатов}}]].",
+       "listduplicatedfiles-entry": "У файла [[:File:$1|$1]] — [[$3|{{PLURAL:$2|$2 дубликат|$2 дубликата|$2 дубликатов}}]].",
        "unusedtemplates": "Неиспользуемые шаблоны",
        "unusedtemplatestext": "Здесь перечислены все страницы пространства имён «{{ns:template}}», не включённые ни в какие другие страницы.\nНе забывайте проверять отсутствие ссылок на шаблон перед его удалением.",
        "unusedtemplateswlh": "другие ссылки",
        "tags-edit-manage-link": "Управление тегами",
        "tags-edit-revision-selected": "{{PLURAL:$1|Выбранная версия|Выбранные версии}} [[:$2]]:",
        "tags-edit-logentry-selected": "{{PLURAL:$1|1=Выбранная запись|Выбранные записи}} журнала:",
-       "tags-edit-revision-legend": "Добавить или удалить теги из {{PLURAL:$1|this revision|all $1 revisions}}",
+       "tags-edit-revision-legend": "Добавить или удалить теги из {{PLURAL:$1|1=этой версии|всех $1 версий}}",
        "tags-edit-logentry-legend": "Добавить или удалить теги из {{PLURAL:$1|this log entry|all $1 log entries}}",
        "tags-edit-existing-tags": "Существующие метки:",
        "tags-edit-existing-tags-none": "''Нет''",
index 0144f6f..470c93b 100644 (file)
        "watchlist-details": "Ырытыы сирэйдэрин аахпатахха {{PLURAL:$1|$1 сирэйи кэтиигин|$1 сирэй эн кэтэбилгэр сылдьар}}.",
        "wlheader-enotif": "Эл. почтанан биллэрии холбоммут.",
        "wlheader-showupdated": "Бүтэһик киирииҥ кэннэ уларыйбыт сирэйдэр '''модьу''' бичигинэн бэлиэтэннилэр.",
-       "wlnote": "Манна кэлиҥҥи {{PLURAL:$2|чаас|'''$2''' чаас}} иһигэр оҥоһуллубут бүтэһик $1 уларытыы көрдөрүлүннэ, бу кэмҥэ туругунан $3 $4.",
+       "wlnote": "Манна кэлиҥҥи {{PLURAL:$2|чаас|<strong>$2</strong> чаас}} иһигэр оҥоһуллубут бүтэһик <strong>$1</strong> уларытыы көрдөрүлүннэ, бу кэминээҕи туругунан $3, $4.",
        "wlshowlast": "Бүтэһик $2 күҥҥэ $1 чааска көрдөр",
        "watchlist-options": "Кэтээн көрүү туруоруутун уларытыы",
        "watching": "Кэтээ...",
        "changecontentmodel-nodirectediting": "$1 иһинээҕитин киэбин быһа уларытар сатаммат эбит",
        "log-name-contentmodel": "Иһинээҕитин киэбин уларытыы сурунаала",
        "log-description-contentmodel": "Сирэй иһинээҕитин киэбин кытта ситимнээх",
-       "logentry-contentmodel-change": "$1 кыттааччы $3 сирэй иһинээҕитин мадьыалын мантан «$4» манна «$5» уларыппыт",
+       "logentry-contentmodel-change": "$1 кыттааччы $3 сирэй иһинээҕитин мадьыалын мантан «$4» манна «$5» {{GENDER:$2|уларыппыт}}",
        "logentry-contentmodel-change-revertlink": "төннөрүү",
        "logentry-contentmodel-change-revert": "төннөрүү",
        "protectlogpage": "Харысхал сурунаала (көннөрүүттэн көмүскэммит билэлэр испииһэктэрэ)",
        "logentry-managetags-activate": "$1 \"$4\" тиэги кыттааччылар уонна буоттар тутталларыгар  {{GENDER:$2|арыйбыт}}",
        "logentry-managetags-deactivate": "$1 \"$4\" тиэги кыттааччылар уонна буоттар туһамматтарын курдук {{GENDER:$2|гыммыт}}",
        "log-name-tag": "Тиэк сурунаала",
+       "log-description-tag": "Бу сирэй кыттааччылар [[Special:Tags|тиэктэри]] хаһан эппиттэрин биитэр соппуттарын көрдөрөр. Тиэги атын уларытыы, сотуу <strong>иһинэн</strong> оҥордоххо манна көстүбэт.",
        "rightsnone": "(суох)",
        "revdelete-summary": "уларытыылар туһунан",
        "feedback-adding": "Сирэй туһунан санаа этии...",
index 52f8b6b..0e796d2 100644 (file)
        "deletepage": "Izbrišite stranicu",
        "confirm": "Potvrdi / Потврди",
        "excontent": "sadržaj je bio: '$1'",
-       "excontentauthor": "sadržaj je bio: „$1“, a jedini urednik [[Special:Contributions/$2|$2]] ([[User talk:$2|razgovor]])",
+       "excontentauthor": "sadržaj je bio: \"$1\", a jedini urednik \"[[Special:Contributions/$2|$2]]\" ([[User talk:$2|razgovor]])",
        "exbeforeblank": "sadržaj prije brisanja je bio: '$1'",
        "delete-confirm": "Brisanje \"$1\"",
        "delete-legend": "Obriši",
index 9edc36a..b29d16d 100644 (file)
        "tog-hideminor": "حالیہ تبدیلیوں میں معمولی ترامیم چھپائیں",
        "tog-hidepatrolled": "حالیہ تبدیلیوں میں گشتی ترامیم چھپائیں",
        "tog-newpageshidepatrolled": "جدید صفحات کی فہرست میں مراجعت شدہ صفحات چھپائیں",
-       "tog-extendwatchlist": "زÛ\8cر Ù\86ظر Ù\81Û\81رست Ú©Û\8c ØªÙ\88سÛ\8cع Ú©Û\8cجئÛ\92 ØªØ§Ú©Û\81 ØªÙ\85اÙ\85 ØªØ¨Ø¯Û\8cÙ\84Û\8cاںØ\8c Ù\86Û\81 Ú©Û\81 ØµØ±Ù\81 Ø³Ø¨ Ø³Û\92 Ø­Ø§Ù\84Û\8cÛ\81 ØªØ¯Ø§Ù\88Û\8cÙ\86 Ø¯Û\8cÚ©Ú¾Û\8c Ú©Ø¬Ø§ Ø³Ú©یں",
-       "tog-usenewrc": "حالیہ تبدیلیوں میں اور زیر نظر فہرست میں تبدیلیوں کو بلحاظ صفحہ گروہ بند کیجئے",
-       "tog-numberheadings": "سرخیوں کو خودکار نمبر دیجئے",
-       "tog-showtoolbar": "تدÙ\88Û\8cÙ\86Û\8c Ø§Ù\88زارداÙ\86 Ø¯Ú©Ú¾Ø§Ø¦Û\8cÛ\92",
-       "tog-editondblclick": "Ø·Ù\82Û\8cÙ\86 Ù¾Ø± ØµÙ\81حات Ú©Û\8c ØªØ±Ù\85Û\8cÙ\85 Ú©Û\8cجئÛ\92",
-       "tog-editsectiononrightclick": "سطری عنوانات پر دایاں طق (رائیٹ کلک) کے ذریعے سطری ترمیم کاری فعال بناؤ",
-       "tog-watchcreations": "میرے تخلیق کردہ صفحات اور میری زبر اثقال کردہ ملفات کو میری زیر نظر فہرست میں شامل کیا کیجئے",
-       "tog-watchdefault": "Ù\85Û\8cرÛ\92 ØªØ¯Ù\88Û\8cÙ\86 Ø´Ø¯Û\81 ØµÙ\81حات Ø§Ù\88ر Ù\85Ù\84Ù\81ات Ú©Ù\88 Ù\85Û\8cرÛ\8c Ø²Û\8cر Ù\86ظر Ù\81Û\81رست Ù\85Û\8cÚº Ø´Ø§Ù\85Ù\84 Ú©Û\8cا Ú©Û\8cجئÛ\92",
-       "tog-watchmoves": "Ù\85Û\8cرÛ\92 Ù\85Ù\86تÙ\82Ù\84 Ú©Ø±Ø¯Û\81 ØµÙ\81حات Ø§Ù\88ر Ù\85Ù\84Ù\81ات Ú©Ù\88 Ù\85Û\8cرÛ\8c Ø²Û\8cر Ù\86ظر Ù\81Û\81رست Ù\85Û\8cÚº Ø´Ø§Ù\85Ù\84 Ú©Û\8cا Ú©Û\8cجئÛ\92",
-       "tog-watchdeletion": "Ù\85Û\8cرÛ\92 Ø­Ø°Ù\81 Ú©Ø±Ø¯Û\81 ØµÙ\81حات Ø§Ù\88ر Ù\85Ù\84Ù\81ات Ú©Ù\88 Ù\85Û\8cرÛ\8c Ø²Û\8cر Ù\86ظر Ù\81Û\81رست Ù\85Û\8cÚº Ø´Ø§Ù\85Ù\84 Ú©Û\8cا Ú©Û\8cجئÛ\92",
-       "tog-watchrollback": "میں جن صفحات کو استرجع کروں وہ میری زیر نظر فہرست میں شامل کیا کریں",
-       "tog-minordefault": "تمام ترمیمات کو ہمیشہ بطورِ معمولی ترمیم نشانزد کیا کرو",
-       "tog-previewontop": "تدÙ\88Û\8cÙ\86Û\8c Ø®Ø§Ù\86Û\81 Ø³Û\92 Ù¾Û\81Ù\84Û\92 Ù\86Ù\85ائش Ø¯Ú©Ú¾Ø§Ø¤",
-       "tog-previewonfirst": "Ù¾Û\81Ù\84Û\8c ØªØ±Ù\85Û\8cÙ\85 Ù¾Ø± Ù\86Ù\85ائش Ø¯Ú©Ú¾Ø§Ø¤",
-       "tog-enotifwatchlistpages": "جب Ù\85Û\8cرÛ\92 Ø²Û\8cر Ù\86ظر ØµÙ\81Ø­Û\81 Û\8cا Ù\85Ù\84Ù\81 Ù\85Û\8cÚº Ú©Ù\88ئÛ\8c ØªØ¨Ø¯Û\8cÙ\84Û\8c Û\81Ù\88 ØªÙ\88 Ù\85جھÛ\92 Ø¨Ø±Ù\82Û\8c Ú\88اک Ø¨Ú¾Û\8cجئÛ\92",
-       "tog-enotifusertalkpages": "جب میرا تبادلۂ خیال صفحہ میں تبدیلی واقع ہو تو مجھے برقی ڈاک بھیجو",
-       "tog-enotifminoredits": "Ù\85جھÛ\92 ØµÙ\81حات Ø§Ù\88ر Ù\85Ù\84Ù\81ات Ù\85Û\8cÚº Ú©Û\8c Ø¬Ø§Ù\86Û\92 Ù\88اÙ\84Û\8c Ù\85عÙ\85Ù\88Ù\84Û\8c ØªØ±Ø§Ù\85Û\8cÙ\85 Ú©Û\8c Ø®Ø¨Ø± Ø¨Ú¾Û\8c Ø¨Ø°Ø±Û\8cعÛ\81 Ø¨Ø±Ù\82Û\8c Ú\88اک Ø¯Û\8cجئÛ\92",
-       "tog-enotifrevealaddr": "خبردارÛ\8c Ø¨Ø±Ù\82Û\8c Ø®Ø·Ù\88Ø· Ù\85Û\8cÚº Ù\85Û\8cرا Ø¨Ø±Ù\82Û\8c Ú\88اک Ù¾ØªÛ\81 Ø¸Ø§Û\81ر Ú©Ø±Ù\88",
-       "tog-shownumberswatching": "دÛ\8cÚ©Ú¾Ù\86Û\92 Ù\88اÙ\84Û\92 ØµØ§Ø±Ù\81Û\8cÙ\86 Ú©Û\8c ØªØ¹Ø¯Ø§Ø¯ Ø¯Ú©Ú¾Ø§Ø¤",
+       "tog-extendwatchlist": "حاÙ\84Û\8cÛ\81 ØªØ±Û\8cÙ\86 ØªØ¨Ø¯Û\8cÙ\84Û\8cÙ\88Úº Ú©Û\92 Ø¨Ø¬Ø§Ø¦Û\92 Ø¬Ù\85Ù\84Û\81 ØªØ¨Ø¯Û\8cÙ\84Û\8cاں Ø¯Û\8cÚ©Ú¾Ù\86Û\92 Ú©Û\92 Ù\84Û\8cÛ\92 Ø²Û\8cر Ù\86ظر Ù\81Û\81رست Ú©Û\8c ØªÙ\88سÛ\8cع Ú©Ø±یں",
+       "tog-usenewrc": "حالیہ تبدیلیاں اور زیر نظر فہرست میں تبدیلیوں کو بلحاظ صفحہ گروہ بند کیجئے",
+       "tog-numberheadings": "سرخیوں کو خودکار نمبر دیں",
+       "tog-showtoolbar": "Ø¢Ù\84ات ØªØ±Ù\85Û\8cÙ\85 Ø¯Ú©Ú¾Ø§Ø¦Û\8cÚº",
+       "tog-editondblclick": "دÙ\88 Ú©Ù\84Ú© Ù¾Ø± ØµÙ\81حات Ú©Û\8c ØªØ±Ù\85Û\8cÙ\85 Ú©Ø±Û\8cÚº",
+       "tog-editsectiononrightclick": "قطعہ کے عنوانات پر رائیٹ کلک کے ذریعے قطعہ کی ترمیم کاری فعال کریں",
+       "tog-watchcreations": "میرے تخلیق کردہ صفحات اور اپلوڈ کردہ فائلوں کو میری زیر نظر فہرست میں شامل کریں",
+       "tog-watchdefault": "Ù\85Û\8cرÛ\92 ØªØ±Ù\85Û\8cÙ\85 Ø´Ø¯Û\81 ØµÙ\81حات Ø§Ù\88ر Ù\81ائÙ\84Ù\88Úº Ú©Ù\88 Ù\85Û\8cرÛ\8c Ø²Û\8cر Ù\86ظر Ù\81Û\81رست Ù\85Û\8cÚº Ø´Ø§Ù\85Ù\84 Ú©Ø±Û\8cÚº",
+       "tog-watchmoves": "Ù\85Û\8cرÛ\92 Ù\85Ù\86تÙ\82Ù\84 Ú©Ø±Ø¯Û\81 ØµÙ\81حات Ø§Ù\88ر Ù\81ائÙ\84Ù\88Úº Ú©Ù\88 Ù\85Û\8cرÛ\8c Ø²Û\8cر Ù\86ظر Ù\81Û\81رست Ù\85Û\8cÚº Ø´Ø§Ù\85Ù\84 Ú©Ø±Û\8cÚº",
+       "tog-watchdeletion": "Ù\85Û\8cرÛ\92 Ø­Ø°Ù\81 Ú©Ø±Ø¯Û\81 ØµÙ\81حات Ø§Ù\88ر Ù\81ائÙ\84Ù\88Úº Ú©Ù\88 Ù\85Û\8cرÛ\8c Ø²Û\8cر Ù\86ظر Ù\81Û\81رست Ù\85Û\8cÚº Ø´Ø§Ù\85Ù\84 Ú©Ø±Û\8cÚº",
+       "tog-watchrollback": "میرے استرجع کردہ صفحات کو میری زیر نظر فہرست میں شامل کریں",
+       "tog-minordefault": "ہمیشہ میری تمام ترامیم کو معمولی ترمیم کے طور پر نشان زد کریں",
+       "tog-previewontop": "خاÙ\86Û\81 ØªØ±Ù\85Û\8cÙ\85 Ø³Û\92 Ù¾Û\81Ù\84Û\92 Ù\86Ù\85ائش Ø¯Ú©Ú¾Ø§Ø¦Û\8cÚº",
+       "tog-previewonfirst": "Ù¾Û\81Ù\84Û\8c ØªØ±Ù\85Û\8cÙ\85 Ù¾Ø± Ù\86Ù\85ائش Ø¯Ú©Ú¾Ø§Ø¦Û\8cÚº",
+       "tog-enotifwatchlistpages": "جب Ù\85Û\8cرÛ\92 Ø²Û\8cر Ù\86ظر ØµÙ\81Ø­Û\81 Û\8cا Ù\81ائÙ\84 Ù\85Û\8cÚº Ú©Ù\88ئÛ\8c ØªØ¨Ø¯Û\8cÙ\84Û\8c Û\81Ù\88 ØªÙ\88 Ù\85جھÛ\92 Ø¨Ø±Ù\82Û\8c Ú\88اک Ø¨Ú¾Û\8cجÛ\8cÚº",
+       "tog-enotifusertalkpages": "جب میرے تبادلۂ خیال صفحہ میں تبدیلی ہو تو مجھے برقی ڈاک بھیجیں",
+       "tog-enotifminoredits": "Ù\85جھÛ\92 ØµÙ\81حات Ø§Ù\88ر Ù\81ائÙ\84Ù\88Úº Ù\85Û\8cÚº Ú©Û\8c Ø¬Ø§Ù\86Û\92 Ù\88اÙ\84Û\8c Ù\85عÙ\85Ù\88Ù\84Û\8c ØªØ±Ø§Ù\85Û\8cÙ\85 Ú©Û\8c Ø®Ø¨Ø± Ø¨Ú¾Û\8c Ø¨Ø°Ø±Û\8cعÛ\81 Ø¨Ø±Ù\82Û\8c Ú\88اک Ø¨Ú¾Û\8cجÛ\8cÚº",
+       "tog-enotifrevealaddr": "اطÙ\84اعاتÛ\8c Ø¨Ø±Ù\82Û\8c Ø®Ø·Ù\88Ø· Ù\85Û\8cÚº Ù\85Û\8cرا Ø¨Ø±Ù\82Û\8c Ú\88اک Ù¾ØªÛ\81 Ø¸Ø§Û\81ر Ú©Ø±Û\8cÚº",
+       "tog-shownumberswatching": "دÛ\8cÚ©Ú¾Ù\86Û\92 Ù\88اÙ\84Û\92 ØµØ§Ø±Ù\81Û\8cÙ\86 Ú©Û\8c ØªØ¹Ø¯Ø§Ø¯ Ø¯Ú©Ú¾Ø§Ø¦Û\8cÚº",
        "tog-oldsig": "موجودہ دستخط:",
-       "tog-fancysig": "(سادہ دستخط بلا خودکار ربط)",
-       "tog-uselivepreview": "براÛ\81 Ø±Ø§Ø³Øª Ù\86Ù\85ائش (آزÙ\85ائشÛ\8c) Ø§Ø³ØªØ¹Ù\85اÙ\84 Ú©Û\8cجئÛ\92",
-       "tog-forceeditsummary": "جب Ù\85Û\8cÚº ØªØ±Ù\85Û\8cÙ\85Û\8c Ø®Ù\84اصÛ\81 Ø®Ø§Ù\84Û\8c Ú\86Ú¾Ù\88Ú\91Ù\88Úº ØªÙ\88 Ù\85جھÛ\92 Ø¢Ú¯Ø§Û\81 Ú©Ø±Ù\88",
-       "tog-watchlisthideown": "زیرِنظرفہرست سے میری ترمیمات چھپاؤ",
-       "tog-watchlisthidebots": "زیرِنظرفہرست میں سے روبالی ترمیمات چھپاؤ",
-       "tog-watchlisthideminor": "زیرِنظرفہرست سے معمولی ترمیمات چھپاؤ",
-       "tog-watchlisthideliu": "زیرِنظرفہرست میں سے داخلِ نوشتہ شدہ صارفین کی ترمیمات چھپاؤ",
-       "tog-watchlisthideanons": "زیرِنظرفہرست میں سے نامعلوم صارفین کی ترمیمات چھپاؤ",
-       "tog-watchlisthidepatrolled": "زیرِنظرفہرست میں سے گشت شدہ ترمیمات چھپاؤ",
-       "tog-ccmeonemails": "دیگر صارفین کو ارسال کردہ برقی خطوط کی نقول مجھے ارسال کریں۔",
-       "tog-diffonly": "Ù\85ختÙ\84Ù\81ات Ú©Û\92 Ù\86Û\8cÚ\86Û\92 ØµÙ\81Ø­Û\92 Ú©Û\8c Ù\85Ø´Ù\85Ù\88Ù\84ات Ù\85ت Ø¯Ú©Ú¾Ø§Ø¤",
-       "tog-showhiddencats": "Ù¾Ù\88Ø´Û\8cدÛ\81 Ø²Ù\85رÛ\81 Ø¬Ø§Øª Ø¯Ú©Ú¾Ø§Ø¤",
-       "tog-norollbackdiff": "استرجع کی انجام دہی کے بعد فرق ترک کیجئے",
-       "tog-useeditwarning": "خبردار Ù\85جھÛ\92 Ø¬Ø¨ Ù\85Û\8cÚº ØºÛ\8cر Ù\85Ø­Ù\81Ù\88ظ Ú©Ø±Ø¯Û\81 ØªØ¨Ø¯Û\8cÙ\84Û\8cÙ\88Úº Ú©Û\92 Ø³Ø§ØªÚ¾ Ø§Û\8cÚ© ØªØ±Ù\85Û\8cÙ\85 Ú©Û\92 ØµÙ\81Ø­Û\92 Ú©Ù\88 Ú\86Ú¾Ù\88Ú\91 Ø¯Ù\88",
-       "tog-prefershttps": "دخول نوشتہ کے دوران محفوظ کنکشن استعمال کیجئے",
+       "tog-fancysig": "سادہ دستخط (بلا خودکار ربط)",
+       "tog-uselivepreview": "راست Ù\86Ù\85ائش Ø§Ø³ØªØ¹Ù\85اÙ\84 Ú©Ø±Û\8cÚº",
+       "tog-forceeditsummary": "Ø®Ù\84اصÛ\81 ØªØ±Ù\85Û\8cÙ\85 Ø®Ø§Ù\84Û\8c Ú\86Ú¾Ù\88Ú\91Ù\86Û\92 Ù¾Ø± Ù\85جھÛ\92 Ø¢Ú¯Ø§Û\81 Ú©Ø±Û\8cÚº",
+       "tog-watchlisthideown": "زیرِنظر فہرست سے میری ترامیم چھپائیں",
+       "tog-watchlisthidebots": "زیرِنظر فہرست سے روبہ جاتی ترامیم چھپائیں",
+       "tog-watchlisthideminor": "زیرِنظر فہرست سے معمولی ترامیم چھپائیں",
+       "tog-watchlisthideliu": "زیرِنظر فہرست سے داخلِ نوشتہ شدہ صارفین کی ترامیم چھپائیں",
+       "tog-watchlisthideanons": "زیرِنظر فہرست سے نامعلوم صارفین کی ترامیم چھپائیں",
+       "tog-watchlisthidepatrolled": "زیرِنظر فہرست سے مراجع شدہ ترامیم چھپائیں",
+       "tog-ccmeonemails": "دÛ\8cگر ØµØ§Ø±Ù\81Û\8cÙ\86 Ú©Ù\88 Ø§Ø±Ø³Ø§Ù\84 Ú©Ø±Ø¯Û\81 Ø¨Ø±Ù\82Û\8c Ø®Ø·Ù\88Ø· Ú©Û\8c Ù\86Ù\82Ù\88Ù\84 Ù\85جھÛ\92 Ø¨Ú¾Û\8c Ø§Ø±Ø³Ø§Ù\84 Ú©Ø±Û\8cÚºÛ\94",
+       "tog-diffonly": "Ù\81رÙ\82 Ú©Û\92 Ù\86Û\8cÚ\86Û\92 ØµÙ\81Ø­Û\92 Ú©Û\92 Ù\85Ø´Ù\85Ù\88Ù\84ات Ù\86Û\81 Ø¯Ú©Ú¾Ø§Ø¦Û\8cÚº",
+       "tog-showhiddencats": "Ù¾Ù\88Ø´Û\8cدÛ\81 Ø²Ù\85رÛ\81 Ø¬Ø§Øª Ø¯Ú©Ú¾Ø§Ø¦Û\8cÚº",
+       "tog-norollbackdiff": "استرجع کی انجام دہی کے بعد فرق ترک کریں",
+       "tog-useeditwarning": "غÛ\8cر Ù\85Ø­Ù\81Ù\88ظ ØªØ¨Ø¯Û\8cÙ\84Û\8cاں Ú\86Ú¾Ù\88Ú\91Ù\86Û\92 Ù¾Ø± Ù\85جھÛ\92 Ø¢Ú¯Ø§Û\81 Ú©Ø±Û\8cÚº",
+       "tog-prefershttps": "لاگ ان رہنے کے دوران ہمیشہ محفوظ کنیکشن استعمال کریں",
        "underline-always": "ہمیشہ",
        "underline-never": "کبھی نہیں",
-       "underline-default": "جلد یا متصفح کا ڈیفالٹ",
-       "editfont-style": "خاÙ\86Û\82 ØªØ¯Ù\88Û\8cÙ\86 Ú©Ø§ Ø§Ù\86دازÙ\90 ØªØ­Ø±Û\8cر:",
-       "editfont-default": "متصفح کا طے شدہ",
-       "editfont-monospace": "یکفضائی نویسہ",
-       "editfont-sansserif": "بÛ\92Ø­Ù\84Û\8cÛ\81 Ù\86Ù\88Û\8cسÛ\81",
-       "editfont-serif": "Ø­Ù\84Û\8cÛ\81 Ù\86Ù\88Û\8cسÛ\81",
+       "underline-default": "جلد یا براؤزر کا ڈیفالٹ",
+       "editfont-style": "خاÙ\86Û\82 ØªØ±Ù\85Û\8cÙ\85 Ú©Ø§ Ù\81اÙ\86Ù¹:",
+       "editfont-default": "براؤزر کا ڈیفالٹ",
+       "editfont-monospace": "مونوسپیسڈ فونٹ",
+       "editfont-sansserif": "سÙ\86س Ø³Û\8cرÙ\81 Ù\81Ù\88Ù\86Ù¹",
+       "editfont-serif": "سÛ\8cرÙ\81 Ù\81Ù\88Ù\86Ù¹",
        "sunday": "اتوار",
        "monday": "پير",
        "tuesday": "منگل",
        "november-date": "$1 نومبر",
        "december-date": "$1 دسمبر",
        "pagecategories": "{{PLURAL:$1|زمرہ|زمرہ جات}}",
-       "category_header": "زمرہ \"$1\" میں مضامین",
+       "category_header": "زمرہ \"$1\" میں صفحات",
        "subcategories": "ذیلی زمرہ جات",
-       "category-media-header": "زÙ\85رÛ\81 \"$1\" Ù\85Û\8cÚº Ù\88سÛ\8cØ·",
-       "category-empty": "‘‘اِس زمرہ میں ابھی کوئی صفحات یا وسیط موجود نہیں.’’",
+       "category-media-header": "زÙ\85رÛ\81 \"$1\" Ù\85Û\8cÚº Ù\85Û\8cÚ\88Û\8cا",
+       "category-empty": "<em>اس زمرہ میں ابھی کوئی صفحہ یا میڈیا موجود نہیں ہے۔</em>",
        "hidden-categories": "{{PLURAL:$1|پوشیدہ زمرہ|پوشیدہ زمرہ جات}}",
        "hidden-category-category": "پوشیدہ زمرہ جات",
-       "category-subcat-count": "{{PLURAL:$2|اِس زمرہ میں صرف درج ذیل ذیلی زمرہ ہے.|اِس زمرہ میں درج ذیل {{PLURAL:$1|ذیلی زمرہ|$1 ذیلی زمرہ جات}}, کل $2 میں سے.}}",
+       "category-subcat-count": "{{PLURAL:$2|اِس زمرہ میں محض درج ذیل ذیلی زمرہ موجود ہے.|اِس زمرہ میں کل $2 میں سے درج ذیل {{PLURAL:$1|ذیلی زمرہ|$1 ذیلی زمرہ جات}} موجود ہیں۔}}",
        "category-subcat-count-limited": "اِس زمرہ میں درج ذیل {{PLURAL:$1|ذیلی زمرہ ہے|$1 ذیلی زمرہ جات ہیں}}.",
-       "category-article-count": "{{PLURAL:$2|اس زمرہ میں صرف درج ذیل صفحہ شامل کیا گیا ہے۔|اس زمرہ کے کل $2 صفحات میں سے $1 {{PLURAL:$1|صفحہ|صفحات}} درج ذیل {{PLURAL:$1|ہے|ہیں}}۔",
-       "category-article-count-limited": "یہ درج ذیل {{PLURAL:$1|صفحہ|$1 صفحات}} اس زمرہ میں مشمول {{PLURAL:$1|ہے|ہیں}}۔",
-       "category-file-count": "{{PLURAL:$2|اس Ø²Ù\85رÛ\81 Ù\85Û\8cÚº ØµØ±Ù\81 Ø¯Ø±Ø¬ Ø°Û\8cÙ\84 Ù\85Ù\84Ù\81 Ø´Ø§Ù\85Ù\84 Ú©Û\8c Ú¯Ø¦Û\8c Û\81Û\92Û\94|اس Ø²Ù\85رÛ\81 Ú©Û\8c Ú©Ù\84 $2 Ù\85Ù\84Ù\81ات Ù\85Û\8cÚº Ø³Û\92 $1 {{PLURAL:$1|Ù\85Ù\84Ù\81\85Ù\84Ù\81ات}} Ø¯Ø±Ø¬ Ø°Û\8cÙ\84 {{PLURAL:$1|Û\81Û\92\81Û\8cÚº}}۔",
-       "category-file-count-limited": "یہ درج ذیل {{PLURAL:$1|صفحہ|$1 صفحات}} اس زمرہ میں شامل {{PLURAL:$1|ہے|ہیں}}۔",
-       "listingcontinuesabbrev": "۔جاری",
+       "category-article-count": "{{PLURAL:$2|اس زمرہ میں محض درج ذیل صفحہ موجود ہے۔|اس زمرہ کے کل $2 صفحات میں سے $1 {{PLURAL:$1|صفحہ|صفحات}} درج ذیل {{PLURAL:$1|ہے|ہیں}}}}۔",
+       "category-article-count-limited": "درج ذیل {{PLURAL:$1|صفحہ|$1 صفحات}} اس زمرہ میں شامل {{PLURAL:$1|ہے|ہیں}}۔",
+       "category-file-count": "{{PLURAL:$2|اس Ø²Ù\85رÛ\81 Ù\85Û\8cÚº ØµØ±Ù\81 Ø¯Ø±Ø¬ Ø°Û\8cÙ\84 Ù\81ائÙ\84 Ù\85Ù\88جÙ\88د Û\81Û\92Û\94|اس Ø²Ù\85رÛ\81 Ú©Û\8c Ú©Ù\84 $2 Ù\81ائÙ\84Ù\88Úº Ù\85Û\8cÚº Ø³Û\92 $1 {{PLURAL:$1|Ù\81ائÙ\84\81ائÙ\84Û\8cÚº}} Ø¯Ø±Ø¬ Ø°Û\8cÙ\84 {{PLURAL:$1|Û\81Û\92\81Û\8cÚº}}}}۔",
+       "category-file-count-limited": "درج ذیل {{PLURAL:$1|فائل|$1 فائلیں}} اس زمرہ میں شامل {{PLURAL:$1|ہے|ہیں}}۔",
+       "listingcontinuesabbrev": "جاری۔",
        "index-category": "فہرست شدہ صفحات",
-       "noindex-category": "غÛ\8cر Ù\85Ù\86درج صفحات",
-       "broken-file-category": "صفحات بمعہ شکستہ فائل روابط",
+       "noindex-category": "غÛ\8cر Ù\81Û\81رست Ø´Ø¯Û\81 صفحات",
+       "broken-file-category": "صفحات مع شکستہ فائل روابط",
        "categoryviewer-pagedlinks": "($1) ($2)",
        "about": "تعارف",
-       "article": "صÙ\81Ø­Û\82 Ù\85Ø´Ù\85Ù\88Ù\84",
-       "newwindow": "(نـئی ونـڈو میـں)",
+       "article": "صÙ\81Ø­Û\81 Ù\85Ù\88اد",
+       "newwindow": "(نـئی ونـڈو میـں کھولیں)",
        "cancel": "منسوخ",
-       "moredotdotdot": "اور...",
+       "moredotdotdot": "مزید...",
        "morenotlisted": "یہ فہرست مکمل نہیں ہے۔",
-       "mypage": "میرا صفحہ",
+       "mypage": "صفحہ",
        "mytalk": "تبادلۂ خیال",
-       "anontalk": "اس IP کیلیے بات چیت",
+       "anontalk": "اس آئی پی پتہ کا تبادلۂ خیال",
        "navigation": "رہنمائی",
        "and": "&#32;اور",
        "qbfind": "ڈھونڈ",
        "nstab-template": "سانچہ",
        "nstab-help": "معاونت",
        "nstab-category": "زمرہ",
+       "mainpage-nstab": "صفحۂ اول",
        "nosuchaction": "کوئی سا عمل نہیں",
        "nosuchactiontext": "URL کی جانب سے مختص کیا گیا عمل درست نہیں.\nآپ نے شاید URL غلط لکھا، یا کسی غیر صحیح ربط کی پیروی کی ہے.\n{{اِس سے SITENAME کے زیرِ استعمال مصنع لطیف میں کھٹمل کی نشاندہی کا بھی اندیشہ ہے}}.",
        "nosuchspecialpage": "کوئی ایسا خاص صفحہ نہیں",
        "createacct-reason": "وجہ",
        "createacct-reason-ph": "آپ دوسرا کھاتہ کیوں تخلیق کررہے ہیں",
        "createacct-submit": "آپ کا کھاتا بنائیں",
-       "createacct-another-submit": "دوسرا کھاتہ تخلیق کریں",
+       "createacct-another-submit": "کھاتہ بنائیں",
        "createacct-benefit-heading": "{{SITENAME}} آپ جیسے لوگوں کی طرف سے بنایا گیا ہے ۔",
        "createacct-benefit-body1": "{{PLURAL:$1|ترمیم|ترامیم}}",
        "createacct-benefit-body2": "$1 {{PLURAL:$1|صفحہ|صفحات}}",
        "nocreate-loggedin": "آپ کو نئے صفحات تخلیق کرنے کی اجازت نہیں ہے.",
        "sectioneditnotsupported-title": "قطعہ کی تدوین حمایت شدہ نہیں ہے",
        "sectioneditnotsupported-text": "اِس صفحہ میں قطعہ کی تدوین حمایت شدہ نہیں ہے.",
-       "permissionserrors": "اخطائÛ\92 Ø§Ø¬Ø§Ø²Øª",
+       "permissionserrors": "خطائے اجازت",
        "permissionserrorstext": "درج ذیل {{PLURAL:$1|وجہ|وجوہات}} کی بناء پر آپ کو ایسا کرنے کی اجازت نہیں ہے:",
        "permissionserrorstext-withaction": "درج ذیل {{PLURAL:$1|وجہ|وجوہات}} کی بناء پر آپ کو $2 کرنے کی اجازت نہیں ہے:",
        "recreate-moveddeleted-warn": "''' انتباہ: آپ ایک گزشتہ حذف شدہ صفحہ دوبارہ تخلیق کررہے ہیں. '''\n\nآپ کو اِس بات پر غور کرنا چاہئے کہ آیا اِس صفحہ کی تدوین جاری رکھنا موزوں ہے یا نہیں.\nصفحہ کا نوشتۂ حذف شدگی و منتقلی یہاں سہولت کی خاطر مہیّا کیا جارہا ہے:",
        "revdelete-legend": "رویتی پابندیاں لگائیں",
        "revdelete-hide-text": "نظرثانی متن چھپاؤ",
        "revdelete-hide-image": "مشمولاتِ ملف چھپاؤ",
-       "revdelete-hide-name": "عمل اور ہدف کو چھپاؤ",
+       "revdelete-hide-name": "ہدف اور پیرامیٹرز کو چھپائیں",
        "revdelete-hide-comment": "ترمیمی تبصرہ چھپاؤ",
        "revdelete-hide-user": "ترمیم کار کا اسمِ صارف / آئی.پی پتہ چُھپاؤ",
        "revdelete-radio-same": "(تبدیل مت کرو)",
        "badsig": "ناقص خام دستخط.\nHTML tags جانچئے.",
        "badsiglength": "آپ کا دستخط کافی طویل ہے.\nیہ $1 {{PLURAL:$1|حرف|حروف}} سے زیادہ نہیں ہونا چاہئے.",
        "yourgender": "جنس:",
-       "gender-unknown": "غÛ\8cرÙ\85ختص Ø´Ø¯Û\81",
+       "gender-unknown": "آپ Ú©Û\92 ØªØ°Ú©Ø±Û\81 Ú©Û\92 Ù\88Ù\82تØ\8c Ø³Ù\88Ù\81Ù¹Ù\88Û\8cئر ØºÛ\8cر Ø¬Ø§Ù\86بدار Ø¬Ù\86سÛ\8c Ø§Ù\84Ù\81اظ Ø§Ø³ØªØ¹Ù\85اÙ\84 Ú©Ø±Û\92 Ú¯Ø§ Ø§Ú¯Ø± Ù\85Ù\85Ú©Ù\86 Û\81Ù\88",
        "gender-male": "مرد",
        "gender-female": "عورت",
        "prefs-help-gender": "اختیاری: مصنع‌لطیف کی طرف سے صحیح‌الجنس تخاطب کیلئے استعمال ہوتا ہے. یہ معلومات عام ہوگی.",
        "userrights-lookup-user": "گروہائے صارف کا انتظام",
        "userrights-user-editname": "کوئی اسم‌صارف داخل کیجئے:",
        "editusergroup": "ترمیم گروہائے صارف",
-       "editinguser": "تبدیلئ اختیارات برائے صارف '''[[صارف:$1|$1]]''' $2",
+       "editinguser": "تبدیلی اختیارات صارف برائے {{GENDER:$1|صارف}} <strong>[[صارف:$1|$1]]</strong> $2",
        "userrights-editusergroup": "ترمیم گروہائے صارف",
        "saveusergroups": "گروہائے صارف محفوظ",
        "userrights-groupsmember": "رکنِ:",
        "boteditletter": " خودکار",
        "rc_categories_any": "کوئی بھی",
        "rc-change-size-new": "$1 {{PLURAL:$1|بائٹ|بائٹس}} تبدیلی کے بعد",
-       "rc-enhanced-expand": "تفصیلات دِکھائیں (JavaScript درکار)",
+       "rc-enhanced-expand": "تفصیلات دکھائیں",
        "rc-enhanced-hide": "تفصیلات چھپائیے",
        "recentchangeslinked": "متعلقہ تبدیلیاں",
        "recentchangeslinked-feed": "متعلقہ تبدیلیاں",
        "uploadbtn": "زبراثقال ملف (اپ لوڈ فائل)",
        "reuploaddesc": "زبراثقال ورقہ (فارم) کیجانب واپس۔",
        "uploadnologin": "آپ داخل شدہ حالت میں نہیں",
-       "uploadnologintext": "زبراثقال ملف (فائل اپ لوڈ) کے لیۓ آپکو  [[Special:UserLogin|داخل شدہ]] حالت میں ہونا لازم ہے۔",
-       "uploadtext": "\n'''یادآوری''': اگر آپ اپنی ملف (فائل) زبراثقال کرتے وقت ، خلاصہ کے خانے میں ،  درج ذیل دو باتوں کی وضاحت نہیں کرتے تو ملف کو حذف کیا جاسکتا ہے:\n#ملف یا فائل کا '''مـاخـذ''' ، یعنی:\n#*اگر یہ آپ نے خود تخلیق کی ہے تو بیان کردیجیۓ۔\n#*اگر یہ روۓ خط (آن لائن) دستیاب ہے ، تو اس وقوع یعنی سائٹ کا  '''رابطہ (لنک)''' دیجیۓ۔\n#*اگر آپ نے اسے کسی دوسری زبان کے {{SITENAME}} سے لیا ہے تو اسکا نام تحریر کردیجیۓ۔\n#صاحب ِحق ِطبع و نشر اور ملف کے اجازہ (لائسنس) کے بارے میں:\n#*ملف کے اجازہ کے بارے میں یہ تحریر کیجیۓ کہ اسکی موجودہ حیثیت کیا ہے۔\n#*اگر آپ خود اسکا حق ِطبع و نشر رکھتے ہیں تو آپ پر لازم ہے کہ آپ اسے ٹ [[دائرۂ عام]] ن (پبلک ڈومین) میں بھی آذاد کردیں۔\n\nجب کوئی صارف مستقل ایسی ملف زبراثقال کرتا رہے کہ جس کے اجازہ کے بارے میں غلط بیانی کی گئی ہو یا وہ مستقل ایسے عکس زبراثقال کرتا رہے کہ جنکے بارے میں کوئی بیان تحریر نہ کیا گیا ہو تو ایسی صورت میں پابندی لگاۓ جانے کا قوی امکان موجود ہے۔\n\nمِلَف (فائل) بھیجنے کیلیے درج ذیل ورقہ (فارم) استعمال کیجیے، اگر آپ اب تک ارسال کردہ تصاویر کو دیکھنا یا تلاش کرنا چاہتے ہیں تو [[Special:FileList|ارسال کردہ تصاویر]] کے ربط پر جائیے۔ <br /> تمام ارسال و حذف کی گئی تصاویر کو [[Special:Log/upload|نوشتۂ منتقلی]] میں درج کر لیا جاتا ہے۔\n\nتصویر کی منتقلی کے بعد، اسکو کسی صفحہ پر رکھنے کیلیے مندرجہ ذیل صورت میں رمـز (کوڈ) استعمال کیجیۓ۔\n\n'''<nowiki>[[تصویر:ملف کا نام|متبادل متن]]</nowiki>'''\n\n* مندرجہ بالا رموز آپ  انگریزی میں بھی درج کرسکتے ہیں، یعنی\n<nowiki>[[Image:File name|Alt.text]]</nowiki>\n* ملف کے ساتھ براہ راست رابطہ کیلیے\nکی طرز میں ربط استعمال کیجیۓ۔ '''<code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:File.ogg]]</nowiki></code>'''\n* ملف کا نام ؛ حرف ابجد کے لیۓ حساس ہے لہذا اگر زبراثقال کرتے وقت ملف کا نام -- name:JPG  ہے اور آپ رابطہ رکھتے وقت name:jpg یــا Name:jpg رکھتے ہیں تو ربط کام نہیں کرے گا",
+       "uploadnologintext": "فائلیں اپلوڈ کرنے کے لیے براہ کرم $1 ہوں",
+       "uploadtext": "\n'''اطلاع''': اگر آپ اپنی فائل اپلوڈ کرتے وقت خلاصہ کے خانے میں درج ذیل دو باتوں کی وضاحت نہیں کریں گے تو اس فائل کو حذف کیا جاسکتا ہے:\n# فائل کا '''مـاخـذ''' ، یعنی:\n#*اگر یہ آپ نے خود تخلیق کی ہے تو اسے بیان کریں۔\n#*اگر یہ آن لائن دستیاب ہے تو اس سائٹ کا  '''ربط''' درج کریں۔\n#*اگر آپ نے اسے کسی دوسری زبان کے {{SITENAME}} سے لیا ہے تو اسکا نام تحریر کریں۔\n#صاحب حق طبع و نشر اور فائل کے اجازت نامہ کے بارے میں:\n#* فائل کے اجازت نامہ کے متعلق یہ درج کریں کہ اس کی موجودہ حیثیت کیا ہے۔\n#*اگر آپ خود اسکا حق طبع و نشر رکھتے ہیں تو آپ پر لازم ہے کہ آپ اسے [[دائرۂ عام]] (پبلک ڈومین) میں بھی شائع کریں۔\n\nجب کوئی صارف مستقل ایسی فائل اپلوڈ کرتا رہے جس کے اجازت نامہ کے بارے میں غلط بیانی کی گئی ہو یا وہ مستقل ایسی تصاویر اپلوڈ کرے جن کے بارے میں کوئی وضاحت موجود نہ ہو تو ایسی صورت میں اس صارف پر پابندی لگائے جانے کا قوی امکان موجود ہے۔\n\nفائل اپلوڈ کرنے کے لیے ذیل میں موجود فارم استعمال کریں، اگر آپ جملہ اپلوڈ کردہ تصاویر کو دیکھنا یا تلاش کرنا چاہتے ہیں تو [[Special:FileList|اس فہرست]] کو ملاحظہ فرمائیں۔ <br /> تمام اپلوڈ کردہ و حذف شدہ تصاویر کو [[Special:Log/upload|نوشتۂ منتقلی]] میں درج کر لیا جاتا ہے۔\n\nتصویر کی منتقلی کے بعد، اسکو کسی صفحہ پر رکھنے کیلیے مندرجہ ذیل طریقہ سے استعمال کریں۔\n\n'''<nowiki>[[تصویر:فائل کا نام|متبادل متن]]</nowiki>'''\n\n* مندرجہ بالا رموز آپ انگریزی میں بھی درج کرسکتے ہیں، یعنی\n<nowiki>[[Image:File name|Alt.text]]</nowiki>\n* فائل کا ربط درج کرنے کے لیے۔ '''<code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:File.ogg]]</nowiki></code>'''\n* ملف کا نام؛ حرف ابجد کے لیے حساس ہے لہذا اگر اپلوڈ کرتے وقت فائل کا نام -- name:JPG  ہے اور آپ name:jpg یــا Name:jpg کا ربط درج کرتے ہیں تو ربط کام نہیں کرے گا۔",
        "uploadlogpage": "نوشتۂ زبراثقال (اپ لوڈ لاگ)",
        "uploadlogpagetext": "درج ذیل میں حالیہ زبراثقال (اپ لوڈ) کی گئی املاف (فائلوں) کی فہرست دی گئی ہے۔",
        "filedesc": "خلاصہ",
        "wlnote": "نیچےآخری $1 تبدیلیاں ہیں جو کے پیچھلے <b>$2</b> گھنٹوں میں کی گئیں۔",
        "wlshowlast": "دکھائیں آخری $1 گھنٹے $2 دن",
        "watchlist-options": "اختیارات برائے زیرِنظرفہرست",
+       "enotif_body": "جناب $WATCHINGUSERNAME!\n\n$PAGEINTRO $NEWPAGE\n\nخلاصہ ترمیم: $PAGESUMMARY $PAGEMINOREDIT\n\nصارف سے رابطہ کریں:\nبذریعہ برقی خط: $PAGEEDITOR_EMAIL\nبذریعہ ویکی: $PAGEEDITOR_WIKI\n\nاس صفحہ میں آئندہ ہونے والی تبدیلیوں کی اطلاعات آپ کو موصول نہیں ہوگی جب تک آپ لاگ ان ہو کر اس صفحہ کو ملاحظہ نہ کر لیں. نیز آپ اپنی زیر نظر فہرست میں موجود تمام صفحات سے اطلاعی علامتیں بھی ختم کر سکتے ہیں.\n\nنظام {{SITENAME}} برائے اطلاعات\n\n--\nاطلاعات بذریعہ برقی خط کی ترتیبات تبدیل کرنے کے لیے\n{{canonicalurl:{{#special:Preferences}}}} ملاحظہ فرمائیں\n\nاپنی زیر نظر فہرست کی ترتیبات میں تبدیلی کے لیے\n{{canonicalurl:{{#special:EditWatchlist}}}} ملاحظہ فرمائیں\n\nاس صفحہ کو اپنی زیر نظر فہرست سے حذف کرنے کے لیے\n$UNWATCHURL ملاحظہ کریں\n\nتجاویز اور مزید معاونت کے لیے ملاحظہ فرمائیں:\n$HELPPAGE",
        "created": "بنا دیا گیا",
        "changed": "تبدیل کردیاگیا",
        "deletepage": "صفحہ ضائع کریں",
        "sp-contributions-uploads": "اثقالات",
        "sp-contributions-logs": "نوشتہ جات",
        "sp-contributions-talk": "گفتگو",
-       "sp-contributions-userrights": "صارÙ\81 Ú©Û\92 Ø­Ù\82Ù\88Ù\82 Ú©Ø§ Ø§Ù\86تظاÙ\85",
+       "sp-contributions-userrights": "اÙ\86تظاÙ\85 Ø§Ø®ØªÛ\8cارات ØµØ§Ø±Ù\81",
        "sp-contributions-search": "تلاش برائے مساہمات",
        "sp-contributions-username": "آئی.پی پتہ یا اسمِ صارف:",
        "sp-contributions-toponly": "صرف حالیہ ترین نظرثانی ترمیمات دِکھاؤ",
        "contribslink": "شراکت",
        "blocklogpage": "نوشتۂ پابندی",
        "block-log-flags-nocreate": "کھاتے کی تخلیق غیرفعال",
-       "move-page": "منتقلی",
+       "move-page": "منتقلی $1",
        "move-page-legend": "منتقلئ صفحہ",
-       "movepagetext": "نیچے دیا گیا تشکیلہ (فـارم) استعمال کرکے اس صفحہ کا عنوان دوبارہ منتخب کیا جاسکتا ہے، ساتھ ہی اس سے منسلک تاریخچہ بھی نۓ نام پر منتقل ہوجاۓ گا۔ اسکے بعد سے اس صفحے کا پرانا نام ، نۓ نام کی جانب -- لوٹایا گیا صفحہ -- کی حیثیت اختیار کرلے گا۔ لیکن یادآوری کرلیجیۓ دیگر صفحات پر ، پرانے صفحہ کی جانب دیۓ گۓ روابط (لنکس) تبدیل نہیں ہونگے؛ اس بات کو یقینی بنانا ضروری ہے کہ کوئی دوہرا یا شکستہ -- پلٹایا گیا ربط -- نہ رہ جاۓ۔\n\nلہذا یہ یقینی بنانا آپکی ذمہ داری ہے کہ تمام روابط درست صفحات کی جانب رہنمائی کرتے رہیں۔\n\nیہ بات بھی ذہن نشین کرلیجیۓ کہ اگر نۓ منتخب کردہ نام کا صفحہ پہلے سے ہی موجود ہو تو ہوسکتا ہے کہ صفحہ منتقل نہ ہو ، ؛ ہاں اگر پہلے سے موجود صفحہ خالی ہے ، یا وہ صرف ایک -- لوٹایا گیا صفحہ -- ہو اور اس سے کوئی تاریخچہ منسلک نہ ہو تو منتقلی ہوجاۓ گی۔ گویا ، کسی خامی کی صورت میں آپ صفحہ کو دوبارہ اسی پرانے نام کی جانب منتقل کرسکتے ہیں اور اس طرح پہلے سے موجود کسی صفحہ میں کوئی حذف و خامی نہیں ہوگی۔\n\n''' انـتـبـاہ !'''\n کسی اہم اور مقبول صفحہ کی منتقلی ، غیرمتوقع اور پریشان کن بھی ہی ہوسکتی ہے اس لیۓ ؛ منتقلی سے قبل براہ کرم یقین کرلیجۓ کہ آپ اسکے منطقی نتائج سے باخبر ہیں۔",
+       "movepagetext": "درج ذیل فارم کے ذریعہ صفحہ کو نیا نام دیا جاسکتا ہے، اس کے ساتھ صفحہ کا تاریخچہ بھی منتقل ہو جائے گا اور\nنئے عنوان کے جانب قدیم عنوان کو رجوع مکرر کردیا جائے گا۔\n\nاس بات کا یقین کر لیں کہ [[Special:DoubleRedirects|دوہرے]] یا [[Special:BrokenRedirects|شکستہ رجوع مکررات]] موجود نہ ہوں۔\n\nنیز آپ اس بات کو بھی یقینی بنانے کے ذمہ دار ہیں کہ روابط انہیں جگہوں سے مربوط رہیں جہاں ہونا چاہیے۔\n\nخیال رہے کہ یہ صفحہ منتقل '''نہیں''' ہوگا اگر نئے عنوان کے ساتھ صفحہ پہلے سے موجود ہو، ہاں اگر صفحہ خالی ہو اور اس کا گذشتہ ترمیمی تاریخچہ موجود نہ ہو تو منتقل کیا جا سکتا ہے۔\nاس کا مطلب ہے آپ سے اگر غلطی ہوجائے تو آپ صفحہ کو اسی جگہ لوٹا سکتے ہیں، تاہم موجود صفحہ پر برتحریر (overwrite) نہیں کرسکتے۔\n\n'''انتباہ!'''\nکسی اہم اور مقبول صفحہ کی منتقلی، غیرمتوقع اور پریشان کن بھی ہی ہوسکتی ہے اس لیے \nمنتقلی سے قبل براہ کرم یقین کرلیں کہ آپ اس کے منطقی نتائج سے باخبر ہیں۔",
        "movepagetext-noredirectfixer": "درج ذیل ورقہ کے ذریعہ صفحہ کو نیا نام دیا جاسکتا ہے، اس کے ساتھ صفحہ کا تاریخچہ بھی منتقل ہوجائیگا۔\nنئے عنوان کے جانب قدیم عنوان کو رجوع مکرر کردیا جائیگا۔\n\nیقین کرلیں کہ [[Special:DoubleRedirects|مکرر]] یا [[Special:BrokenRedirects|شکستہ رجوع مکررات]] موجود نہیں ہیں۔\nآپ اس بات کو یقینی بنانے کے ذمہ دار ہیں کہ روابط انہیں جگہوں سے مربوط ہیں جن کو فرض کیا گیا ہے۔\n\nخیال رہے کہ یہ صفحہ منتقل '''نہیں''' ہوگا اگر نئے عنوان کے ساتھ صفحہ پہلے سے موجود ہو، سوائے اس کے کہ صفحہ خالی ہو اور اس کا گذشتہ ترمیمی تاریخچہ موجود نہ ہو۔\nاس کا مطلب ہے آپ سے اگر غلطی ہوجائے تو آپ صفحہ کو اسی جگہ لوٹا سکتے ہیں، تاہم موجود صفحہ پر برتحریر (overwrite) نہیں کرسکتے۔\n\n'''انتباہ!'''\nکسی اہم اور مقبول صفحہ کی منتقلی، غیرمتوقع اور پریشان کن بھی ہی ہوسکتی ہے اس لیۓ؛ \nمنتقلی سے قبل براہ کرم یقین کرلیجۓ کہ آپ اسکے منطقی نتائج سے باخبر ہیں۔",
-       "newtitle": "نـیــا عـنــوان",
+       "newtitle": "نـیــا عـنــوان:",
        "move-watch": "صفحہ زیر نظر",
        "movepagebtn": "مـنـتـقـل",
        "pagemovedsub": "انتقال کامیاب",
        "delete_and_move": "حذف اور منتقل",
        "delete_and_move_text": "==حذف شدگی لازم==\n\nمنتقلی کے سلسلے میں انتخاب کردہ مضمون \"[[:$1]]\" پہلے ہی موجود ہے۔ کیا آپ اسے حذف کرکے منتقلی کیلیۓ راستہ بنانا چاہتے ہیں؟",
        "delete_and_move_confirm": "ہاں، صفحہ حذف کر دیا جائے",
-       "delete_and_move_reason": "منتقلی کے سلسلے میں حذف",
+       "delete_and_move_reason": "[[$1]] سے منتقلی کے سلسلے میں حذف",
        "export": "برآمد صفحات",
        "allmessages": "نظامی پیغامات",
        "allmessagesname": "نام",
index eac277a..41650bd 100644 (file)
@@ -55,37 +55,41 @@ class FindOrphanedFiles extends Maintenance {
                        $this->error( "Could not get file listing.", 1 );
                }
 
-               $nameBatch = array();
+               $pathBatch = array();
                foreach ( $list as $path ) {
                        if ( preg_match( '#^(thumb|deleted)/#', $path ) ) {
                                continue; // handle ugly nested containers on stock installs
                        }
 
-                       $nameBatch[] = basename( $path );
-                       if ( count( $nameBatch ) >= $this->mBatchSize ) {
-                               $this->checkFiles( $repo, $nameBatch, $verbose );
-                               $nameBatch = array();
+                       $pathBatch[] = $path;
+                       if ( count( $pathBatch ) >= $this->mBatchSize ) {
+                               $this->checkFiles( $repo, $pathBatch, $verbose );
+                               $pathBatch = array();
                        }
                }
-               $this->checkFiles( $repo, $nameBatch, $verbose );
+               $this->checkFiles( $repo, $pathBatch, $verbose );
        }
 
-       protected function checkFiles( LocalRepo $repo, array $names, $verbose ) {
-               if ( !count( $names ) ) {
+       protected function checkFiles( LocalRepo $repo, array $paths, $verbose ) {
+               if ( !count( $paths ) ) {
                        return;
                }
 
                $dbr = $repo->getSlaveDB();
 
+               $curNames = array();
+               $oldNames = array();
                $imgIN = array();
                $oiWheres = array();
-               foreach ( $names as $name ) {
-                       if ( strpos( $name, '!' ) !== false ) {
+               foreach ( $paths as $path ) {
+                       $name = basename( $path );
+                       if ( preg_match( '#^archive/#', $path ) ) {
                                if ( $verbose ) {
                                        $this->output( "Checking old file $name\n" );
                                }
 
-                               list( , $base ) = explode( '!', $name ); // <TS_MW>!<img_name>
+                               $oldNames[] = $name;
+                               list( , $base ) = explode( '!', $name, 2 ); // <TS_MW>!<img_name>
                                $oiWheres[] = $dbr->makeList(
                                        array( 'oi_name' => $base, 'oi_archive_name' => $name ),
                                        LIST_AND
@@ -95,6 +99,7 @@ class FindOrphanedFiles extends Maintenance {
                                        $this->output( "Checking current file $name\n" );
                                }
 
+                               $curNames[] = $name;
                                $imgIN[] = $name;
                        }
                }
@@ -104,12 +109,12 @@ class FindOrphanedFiles extends Maintenance {
                                array(
                                        $dbr->selectSQLText(
                                                'image',
-                                               array( 'name' => 'img_name' ),
+                                               array( 'name' => 'img_name', 'old' => 0 ),
                                                $imgIN ? array( 'img_name' => $imgIN ) : '1=0'
                                        ),
                                        $dbr->selectSQLText(
                                                'oldimage',
-                                               array( 'name' => 'oi_archive_name' ),
+                                               array( 'name' => 'oi_archive_name', 'old' => 1 ),
                                                $oiWheres ? $dbr->makeList( $oiWheres, LIST_OR ) : '1=0'
                                        )
                                ),
@@ -118,20 +123,30 @@ class FindOrphanedFiles extends Maintenance {
                        __METHOD__
                );
 
-               $namesFound = array();
+               $curNamesFound = array();
+               $oldNamesFound = array();
                foreach ( $res as $row ) {
-                       $namesFound[] = $row->name;
+                       if ( $row->old ) {
+                               $oldNamesFound[] = $row->name;
+                       } else {
+                               $curNamesFound[] = $row->name;
+                       }
                }
 
-               $namesOrphans = array_diff( $names, $namesFound );
-               foreach ( $namesOrphans as $name ) {
+               foreach ( array_diff( $curNames, $curNamesFound ) as $name ) {
+                       $file = $repo->newFile( $name );
                        // Print name and public URL to ease recovery
-                       if ( strpos( $name, '!' ) !== false ) {
-                               list( , $base ) = explode( '!', $name ); // <TS_MW>!<img_name>
-                               $file = $repo->newFromArchiveName( Title::makeTitle( NS_FILE, $base ), $name );
+                       if ( $file ) {
+                               $this->output( $name . "\n" . $file->getCanonicalUrl() . "\n\n" );
                        } else {
-                               $file = $repo->newFile( $name );
+                               $this->error( "Cannot get URL for bad file title '$name'" );
                        }
+               }
+
+               foreach ( array_diff( $oldNames, $oldNamesFound ) as $name ) {
+                       list( , $base ) = explode( '!', $name, 2 ); // <TS_MW>!<img_name>
+                       $file = $repo->newFromArchiveName( Title::makeTitle( NS_FILE, $base ), $name );
+                       // Print name and public URL to ease recovery
                        $this->output( $name . "\n" . $file->getCanonicalUrl() . "\n\n" );
                }
        }
index 7b7b761..6b8487f 100644 (file)
@@ -35,7 +35,7 @@ if ( $help ) {
        mccShowUsage();
        exit( 0 );
 }
-$mcc = new MWMemcached( array(
+$mcc = new MemcachedClient( array(
        'persistent' => true,
        'debug' => $debug,
 ) );
index 08c695d..fa04e41 100644 (file)
@@ -826,6 +826,7 @@ return array(
                'scripts' => array(
                        'resources/lib/phpjs-sha1/sha1.js',
                        'resources/src/mediawiki/mediawiki.js',
+                       'resources/src/mediawiki/mediawiki.requestIdleCallback.js',
                        'resources/src/mediawiki/mediawiki.errorLogger.js',
                ),
                'debugScripts' => 'resources/src/mediawiki/mediawiki.log.js',
index 162790e..d41efdb 100644 (file)
@@ -18,5 +18,6 @@
        "ooui-dialog-process-dismiss": "Odbaci",
        "ooui-dialog-process-retry": "Pokušajte ponovo",
        "ooui-dialog-process-continue": "Nastavi",
+       "ooui-selectfile-button-select": "Izaberi datoteku",
        "ooui-selectfile-placeholder": "Nijedna datoteka nije izabrana"
 }
index fb93e3a..734b738 100644 (file)
@@ -11,7 +11,8 @@
                        "Polda18",
                        "Tchoř",
                        "ශ්වෙත",
-                       "Vojtěch Dostál"
+                       "Vojtěch Dostál",
+                       "Matěj Suchánek"
                ]
        },
        "ooui-outline-control-move-down": "Přesunout položku dolů",
@@ -28,5 +29,6 @@
        "ooui-dialog-process-continue": "Pokračovat",
        "ooui-selectfile-button-select": "Vybrat soubor",
        "ooui-selectfile-not-supported": "Výběr souboru není podporován",
-       "ooui-selectfile-placeholder": "Nebyl vybrán žádný soubor"
+       "ooui-selectfile-placeholder": "Nebyl vybrán žádný soubor",
+       "ooui-selectfile-dragdrop-placeholder": "Umístěte soubor sem"
 }
index 61e3d01..6481624 100644 (file)
@@ -2,8 +2,9 @@
        "@metadata": {
                "authors": [
                        "Malafaya",
-                       "十弌"
+                       "十弌",
+                       "飞舞回堂前"
                ]
        },
-       "ooui-toolbar-more": "多"
+       "ooui-toolbar-more": "多"
 }
index 9abc847..15cd4be 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.13.1
+ * OOjs UI v0.13.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-11-03T21:42:28Z
+ * Date: 2015-11-10T23:33:07Z
  */
 @-webkit-keyframes oo-ui-progressBarWidget-slide {
        from {
 }
 .oo-ui-progressBarWidget {
        max-width: 50em;
+       background-color: #ffffff;
        border: 1px solid #cccccc;
        border-radius: 0.25em;
        overflow: hidden;
        overflow: hidden;
 }
 .oo-ui-popupWidget-popup {
+       background-color: #ffffff;
        border: 1px solid #cccccc;
        border-radius: 0.25em;
-       background-color: #ffffff;
        box-shadow: 0 0.15em 0.5em 0 rgba(0, 0, 0, 0.2);
 }
 .oo-ui-popupWidget-anchored .oo-ui-popupWidget-popup {
                box-sizing: border-box;
 }
 .oo-ui-dropdownInputWidget select {
-       background: #ffffff;
+       background-color: #ffffff;
        height: 2.5em;
        padding: 0.5em;
        font-size: inherit;
 }
 .oo-ui-menuSelectWidget {
        position: absolute;
-       background: #ffffff;
+       background-color: #ffffff;
        margin-top: -1px;
        border: 1px solid #cccccc;
        border-radius: 0 0 0.25em 0.25em;
        position: relative;
        width: 100%;
        max-width: 50em;
+       background-color: #ffffff;
        margin-right: 0.5em;
 }
 .oo-ui-dropdownWidget-handle {
 }
 .oo-ui-selectFileWidget-info {
        height: 2.4em;
+       background-color: #ffffff;
        border: 1px solid rgba(0, 0, 0, 0.1);
        border-radius: 0.25em;
 }
 }
 .oo-ui-selectFileWidget-dropTarget {
        line-height: 3.5em;
+       background-color: #ffffff;
        border: 1px dashed #aaaaaa;
        padding: 0.5em 1em;
        margin-bottom: 0.5em;
-       background: #ffffff;
        text-align: center;
        vertical-align: middle;
 }
 .oo-ui-capsuleItemWidget.oo-ui-widget-disabled > .oo-ui-indicatorElement-indicator {
        opacity: 0.2;
 }
-.oo-ui-comboBoxWidget {
+.oo-ui-comboBoxInputWidget {
        display: inline-block;
        position: relative;
        width: 100%;
        max-width: 50em;
        margin-right: 0.5em;
 }
-.oo-ui-comboBoxWidget > .oo-ui-menuSelectWidget {
+.oo-ui-comboBoxInputWidget > .oo-ui-menuSelectWidget {
        z-index: 1;
        width: 100%;
 }
-.oo-ui-comboBoxWidget > .oo-ui-textInputWidget.oo-ui-widget-enabled > .oo-ui-indicatorElement-indicator {
+.oo-ui-comboBoxInputWidget.oo-ui-widget-enabled > .oo-ui-indicatorElement-indicator {
        cursor: pointer;
 }
-.oo-ui-comboBoxWidget:last-child {
-       margin-right: 0;
+.oo-ui-comboBoxInputWidget-php input::-webkit-calendar-picker-indicator {
+       opacity: 0 !important;
+       position: absolute;
+       right: 0;
+       top: 0;
+       height: 2.5em;
+       width: 2.5em;
+       padding: 0;
 }
-.oo-ui-comboBoxWidget-handle {
-       border: 1px solid rgba(0, 0, 0, 0.1);
-       border-radius: 0.25em;
+.oo-ui-comboBoxInputWidget-php > .oo-ui-indicatorElement-indicator {
+       pointer-events: none;
 }
-.oo-ui-comboBoxWidget-handle:hover {
-       border-color: rgba(0, 0, 0, 0.2);
+.oo-ui-comboBoxInputWidget:last-child {
+       margin-right: 0;
 }
-.oo-ui-comboBoxWidget.oo-ui-widget-disabled .oo-ui-textInputWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator,
-.oo-ui-comboBoxWidget-empty .oo-ui-textInputWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
+.oo-ui-comboBoxInputWidget.oo-ui-widget-disabled .oo-ui-textInputWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator,
+.oo-ui-comboBoxInputWidget-empty .oo-ui-textInputWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
        cursor: default;
        opacity: 0.2;
 }
-.oo-ui-comboBoxWidget > .oo-ui-selectWidget {
+.oo-ui-comboBoxInputWidget > .oo-ui-selectWidget {
        margin-top: -3px;
 }
 .oo-ui-searchWidget-query {
index 00a0966..145bca3 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.13.1
+ * OOjs UI v0.13.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-11-03T21:42:20Z
+ * Date: 2015-11-10T23:32:59Z
  */
 /**
  * @class
index fdb06e5..f5b431c 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.13.1
+ * OOjs UI v0.13.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-11-03T21:42:28Z
+ * Date: 2015-11-10T23:33:07Z
  */
 @-webkit-keyframes oo-ui-progressBarWidget-slide {
        from {
 }
 .oo-ui-progressBarWidget {
        max-width: 50em;
+       background-color: #ffffff;
        border: 1px solid #cccccc;
        border-radius: 0.1em;
        overflow: hidden;
        overflow: hidden;
 }
 .oo-ui-popupWidget-popup {
+       background-color: #ffffff;
        border: 1px solid #aaaaaa;
        border-radius: 0.2em;
-       background-color: #ffffff;
        box-shadow: 0 0.15em 0 0 rgba(0, 0, 0, 0.15);
 }
 .oo-ui-popupWidget-anchored .oo-ui-popupWidget-popup {
                box-sizing: border-box;
 }
 .oo-ui-dropdownInputWidget select {
-       background: #ffffff;
+       background-color: #ffffff;
        height: 2.275em;
        font-size: inherit;
        font-family: inherit;
 }
 .oo-ui-menuSelectWidget {
        position: absolute;
-       background: #ffffff;
+       background-color: #ffffff;
        margin-top: -1px;
        border: 1px solid #aaaaaa;
        border-radius: 0 0 0.2em 0.2em;
        position: relative;
        width: 100%;
        max-width: 50em;
-       background: #ffffff;
+       background-color: #ffffff;
        margin-right: 0.5em;
 }
 .oo-ui-dropdownWidget-handle {
 .oo-ui-dropdownWidget-handle {
        padding: 0.5em 0;
        height: 2.275em;
+       line-height: 1.275;
        border: 1px solid #cccccc;
        border-radius: 0.1em;
 }
 }
 .oo-ui-selectFileWidget-info {
        height: 2.4em;
+       background-color: #ffffff;
        border: 1px solid #cccccc;
        border-radius: 0.1em;
 }
 }
 .oo-ui-selectFileWidget-dropTarget {
        line-height: 3.5em;
+       background-color: #ffffff;
        border: 1px dashed #cccccc;
        padding: 0.5em 1em;
        margin-bottom: 0.5em;
-       background: #ffffff;
        text-align: center;
        vertical-align: middle;
 }
 .oo-ui-capsuleItemWidget.oo-ui-widget-disabled > .oo-ui-indicatorElement-indicator {
        opacity: 0.2;
 }
-.oo-ui-comboBoxWidget {
+.oo-ui-comboBoxInputWidget {
        display: inline-block;
        position: relative;
        width: 100%;
        max-width: 50em;
        margin-right: 0.5em;
 }
-.oo-ui-comboBoxWidget > .oo-ui-menuSelectWidget {
+.oo-ui-comboBoxInputWidget > .oo-ui-menuSelectWidget {
        z-index: 1;
        width: 100%;
 }
-.oo-ui-comboBoxWidget > .oo-ui-textInputWidget.oo-ui-widget-enabled > .oo-ui-indicatorElement-indicator {
+.oo-ui-comboBoxInputWidget.oo-ui-widget-enabled > .oo-ui-indicatorElement-indicator {
        cursor: pointer;
 }
-.oo-ui-comboBoxWidget:last-child {
+.oo-ui-comboBoxInputWidget-php input::-webkit-calendar-picker-indicator {
+       opacity: 0 !important;
+       position: absolute;
+       right: 0;
+       top: 0;
+       height: 2.5em;
+       width: 2.5em;
+       padding: 0;
+}
+.oo-ui-comboBoxInputWidget-php > .oo-ui-indicatorElement-indicator {
+       pointer-events: none;
+}
+.oo-ui-comboBoxInputWidget:last-child {
        margin-right: 0;
 }
-.oo-ui-comboBoxWidget .oo-ui-textInputWidget input,
-.oo-ui-comboBoxWidget .oo-ui-textInputWidget textarea {
+.oo-ui-comboBoxInputWidget input,
+.oo-ui-comboBoxInputWidget textarea {
        height: 2.35em;
 }
 .oo-ui-searchWidget-query {
index d9a17a2..757a965 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.13.1
+ * OOjs UI v0.13.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-11-03T21:42:20Z
+ * Date: 2015-11-10T23:32:59Z
  */
 /**
  * @class
index b4dc48c..511ec94 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.13.1
+ * OOjs UI v0.13.2
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-11-03T21:42:20Z
+ * Date: 2015-11-10T23:32:59Z
  */
 ( function ( OO ) {
 
@@ -5908,10 +5908,12 @@ OO.ui.mixin.LabelElement.prototype.setLabelContent = function ( label ) {
  * @cfg {jQuery} [$container=this.$element] The container element. The lookup menu is rendered beneath the specified element.
  * @cfg {boolean} [allowSuggestionsWhenEmpty=false] Request and display a lookup menu when the text input is empty.
  *  By default, the lookup menu is not generated and displayed until the user begins to type.
+ * @cfg {boolean} [highlightFirst=true] Whether the first lookup result should be highlighted (so, that the user can
+ *  take it over into the input with simply pressing return) automatically or not.
  */
 OO.ui.mixin.LookupElement = function OoUiMixinLookupElement( config ) {
        // Configuration initialization
-       config = config || {};
+       config = $.extend( { highlightFirst: true }, config );
 
        // Properties
        this.$overlay = config.$overlay || this.$element;
@@ -5928,6 +5930,7 @@ OO.ui.mixin.LookupElement = function OoUiMixinLookupElement( config ) {
        this.lookupRequest = null;
        this.lookupsDisabled = false;
        this.lookupInputFocused = false;
+       this.lookupHighlightFirstItem = config.highlightFirst;
 
        // Events
        this.$input.on( {
@@ -6115,13 +6118,13 @@ OO.ui.mixin.LookupElement.prototype.populateLookupMenu = function () {
 };
 
 /**
- * Highlight the first selectable item in the menu.
+ * Highlight the first selectable item in the menu, if configured.
  *
  * @private
  * @chainable
  */
 OO.ui.mixin.LookupElement.prototype.initializeLookupMenuSelection = function () {
-       if ( !this.lookupMenu.getSelectedItem() ) {
+       if ( this.lookupHighlightFirstItem && !this.lookupMenu.getSelectedItem() ) {
                this.lookupMenu.highlightItem( this.lookupMenu.getFirstSelectableItem() );
        }
 };
@@ -13223,7 +13226,7 @@ OO.ui.ToggleButtonWidget.prototype.setButtonElement = function ( $button ) {
 };
 
 /**
- * CapsuleMultiSelectWidgets are something like a {@link OO.ui.ComboBoxWidget combo box widget}
+ * CapsuleMultiSelectWidgets are something like a {@link OO.ui.ComboBoxInputWidget combo box widget}
  * that allows for selecting multiple values.
  *
  * For more information about menus and options, please see the [OOjs UI documentation on MediaWiki][1].
@@ -16397,7 +16400,7 @@ OO.ui.TextInputWidget.prototype.restorePreInfuseState = function ( state ) {
 };
 
 /**
- * ComboBoxWidgets combine a {@link OO.ui.TextInputWidget text input} (where a value
+ * ComboBoxInputWidgets combine a {@link OO.ui.TextInputWidget text input} (where a value
  * can be entered manually) and a {@link OO.ui.MenuSelectWidget menu of options} (from which
  * a value can be chosen instead). Users can choose options from the combo box in one of two ways:
  *
@@ -16406,13 +16409,15 @@ OO.ui.TextInputWidget.prototype.restorePreInfuseState = function ( state ) {
  * - by choosing a value from the menu. The value of the chosen option will then appear in the text
  *   input field.
  *
+ * This widget can be used inside a HTML form, such as a OO.ui.FormLayout.
+ *
  * For more information about menus and options, please see the [OOjs UI documentation on MediaWiki][1].
  *
  *     @example
- *     // Example: A ComboBoxWidget.
- *     var comboBox = new OO.ui.ComboBoxWidget( {
- *         label: 'ComboBoxWidget',
- *         input: { value: 'Option One' },
+ *     // Example: A ComboBoxInputWidget.
+ *     var comboBox = new OO.ui.ComboBoxInputWidget( {
+ *         label: 'ComboBoxInputWidget',
+ *         value: 'Option 1',
  *         menu: {
  *             items: [
  *                 new OO.ui.MenuOptionWidget( {
@@ -16443,60 +16448,47 @@ OO.ui.TextInputWidget.prototype.restorePreInfuseState = function ( state ) {
  * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Menu_selects_and_options
  *
  * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.mixin.TabIndexedElement
+ * @extends OO.ui.TextInputWidget
  *
  * @constructor
  * @param {Object} [config] Configuration options
+ * @cfg {Object[]} [options=[]] Array of menu options in the format `{ data: …, label: … }`
  * @cfg {Object} [menu] Configuration options to pass to the {@link OO.ui.FloatingMenuSelectWidget menu select widget}.
- * @cfg {Object} [input] Configuration options to pass to the {@link OO.ui.TextInputWidget text input widget}.
  * @cfg {jQuery} [$overlay] Render the menu into a separate layer. This configuration is useful in cases where
  *  the expanded menu is larger than its containing `<div>`. The specified overlay layer is usually on top of the
  *  containing `<div>` and has a larger area. By default, the menu uses relative positioning.
  */
-OO.ui.ComboBoxWidget = function OoUiComboBoxWidget( config ) {
+OO.ui.ComboBoxInputWidget = function OoUiComboBoxInputWidget( config ) {
        // Configuration initialization
-       config = config || {};
+       config = $.extend( {
+               indicator: 'down'
+       }, config );
+       // For backwards-compatibility with ComboBoxWidget config
+       $.extend( config, config.input );
 
        // Parent constructor
-       OO.ui.ComboBoxWidget.parent.call( this, config );
-
-       // Properties (must be set before TabIndexedElement constructor call)
-       this.$indicator = this.$( '<span>' );
-
-       // Mixin constructors
-       OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$indicator } ) );
+       OO.ui.ComboBoxInputWidget.parent.call( this, config );
 
        // Properties
        this.$overlay = config.$overlay || this.$element;
-       this.input = new OO.ui.TextInputWidget( $.extend(
-               {
-                       indicator: 'down',
-                       $indicator: this.$indicator,
-                       disabled: this.isDisabled()
-               },
-               config.input
-       ) );
-       this.input.$input.eq( 0 ).attr( {
-               role: 'combobox',
-               'aria-autocomplete': 'list'
-       } );
        this.menu = new OO.ui.FloatingMenuSelectWidget( $.extend(
                {
                        widget: this,
-                       input: this.input,
-                       $container: this.input.$element,
+                       input: this,
+                       $container: this.$element,
                        disabled: this.isDisabled()
                },
                config.menu
        ) );
+       // For backwards-compatibility with ComboBoxWidget
+       this.input = this;
 
        // Events
        this.$indicator.on( {
-               click: this.onClick.bind( this ),
-               keypress: this.onKeyPress.bind( this )
+               click: this.onIndicatorClick.bind( this ),
+               keypress: this.onIndicatorKeyPress.bind( this )
        } );
-       this.input.connect( this, {
+       this.connect( this, {
                change: 'onInputChange',
                enter: 'onInputEnter'
        } );
@@ -16507,15 +16499,23 @@ OO.ui.ComboBoxWidget = function OoUiComboBoxWidget( config ) {
        } );
 
        // Initialization
-       this.$element.addClass( 'oo-ui-comboBoxWidget' ).append( this.input.$element );
+       this.$input.attr( {
+               role: 'combobox',
+               'aria-autocomplete': 'list'
+       } );
+       // Do not override options set via config.menu.items
+       if ( config.options !== undefined ) {
+               this.setOptions( config.options );
+       }
+       // Extra class for backwards-compatibility with ComboBoxWidget
+       this.$element.addClass( 'oo-ui-comboBoxInputWidget oo-ui-comboBoxWidget' );
        this.$overlay.append( this.menu.$element );
        this.onMenuItemsChange();
 };
 
 /* Setup */
 
-OO.inheritClass( OO.ui.ComboBoxWidget, OO.ui.Widget );
-OO.mixinClass( OO.ui.ComboBoxWidget, OO.ui.mixin.TabIndexedElement );
+OO.inheritClass( OO.ui.ComboBoxInputWidget, OO.ui.TextInputWidget );
 
 /* Methods */
 
@@ -16523,7 +16523,7 @@ OO.mixinClass( OO.ui.ComboBoxWidget, OO.ui.mixin.TabIndexedElement );
  * Get the combobox's menu.
  * @return {OO.ui.FloatingMenuSelectWidget} Menu widget
  */
-OO.ui.ComboBoxWidget.prototype.getMenu = function () {
+OO.ui.ComboBoxInputWidget.prototype.getMenu = function () {
        return this.menu;
 };
 
@@ -16531,8 +16531,8 @@ OO.ui.ComboBoxWidget.prototype.getMenu = function () {
  * Get the combobox's text input widget.
  * @return {OO.ui.TextInputWidget} Text input widget
  */
-OO.ui.ComboBoxWidget.prototype.getInput = function () {
-       return this.input;
+OO.ui.ComboBoxInputWidget.prototype.getInput = function () {
+       return this;
 };
 
 /**
@@ -16541,7 +16541,7 @@ OO.ui.ComboBoxWidget.prototype.getInput = function () {
  * @private
  * @param {string} value New value
  */
-OO.ui.ComboBoxWidget.prototype.onInputChange = function ( value ) {
+OO.ui.ComboBoxInputWidget.prototype.onInputChange = function ( value ) {
        var match = this.menu.getItemFromData( value );
 
        this.menu.selectItem( match );
@@ -16560,10 +16560,10 @@ OO.ui.ComboBoxWidget.prototype.onInputChange = function ( value ) {
  * @private
  * @param {jQuery.Event} e Mouse click event
  */
-OO.ui.ComboBoxWidget.prototype.onClick = function ( e ) {
+OO.ui.ComboBoxInputWidget.prototype.onIndicatorClick = function ( e ) {
        if ( !this.isDisabled() && e.which === 1 ) {
                this.menu.toggle();
-               this.input.$input[ 0 ].focus();
+               this.$input[ 0 ].focus();
        }
        return false;
 };
@@ -16574,10 +16574,10 @@ OO.ui.ComboBoxWidget.prototype.onClick = function ( e ) {
  * @private
  * @param {jQuery.Event} e Key press event
  */
-OO.ui.ComboBoxWidget.prototype.onKeyPress = function ( e ) {
+OO.ui.ComboBoxInputWidget.prototype.onIndicatorKeyPress = function ( e ) {
        if ( !this.isDisabled() && ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER ) ) {
                this.menu.toggle();
-               this.input.$input[ 0 ].focus();
+               this.$input[ 0 ].focus();
                return false;
        }
 };
@@ -16587,7 +16587,7 @@ OO.ui.ComboBoxWidget.prototype.onKeyPress = function ( e ) {
  *
  * @private
  */
-OO.ui.ComboBoxWidget.prototype.onInputEnter = function () {
+OO.ui.ComboBoxInputWidget.prototype.onInputEnter = function () {
        if ( !this.isDisabled() ) {
                this.menu.toggle( false );
        }
@@ -16599,8 +16599,8 @@ OO.ui.ComboBoxWidget.prototype.onInputEnter = function () {
  * @private
  * @param {OO.ui.OptionWidget} item Chosen item
  */
-OO.ui.ComboBoxWidget.prototype.onMenuChoose = function ( item ) {
-       this.input.setValue( item.getData() );
+OO.ui.ComboBoxInputWidget.prototype.onMenuChoose = function ( item ) {
+       this.setValue( item.getData() );
 };
 
 /**
@@ -16608,25 +16608,22 @@ OO.ui.ComboBoxWidget.prototype.onMenuChoose = function ( item ) {
  *
  * @private
  */
-OO.ui.ComboBoxWidget.prototype.onMenuItemsChange = function () {
-       var match = this.menu.getItemFromData( this.input.getValue() );
+OO.ui.ComboBoxInputWidget.prototype.onMenuItemsChange = function () {
+       var match = this.menu.getItemFromData( this.getValue() );
        this.menu.selectItem( match );
        if ( this.menu.getHighlightedItem() ) {
                this.menu.highlightItem( match );
        }
-       this.$element.toggleClass( 'oo-ui-comboBoxWidget-empty', this.menu.isEmpty() );
+       this.$element.toggleClass( 'oo-ui-comboBoxInputWidget-empty', this.menu.isEmpty() );
 };
 
 /**
  * @inheritdoc
  */
-OO.ui.ComboBoxWidget.prototype.setDisabled = function ( disabled ) {
+OO.ui.ComboBoxInputWidget.prototype.setDisabled = function ( disabled ) {
        // Parent method
-       OO.ui.ComboBoxWidget.parent.prototype.setDisabled.call( this, disabled );
+       OO.ui.ComboBoxInputWidget.parent.prototype.setDisabled.call( this, disabled );
 
-       if ( this.input ) {
-               this.input.setDisabled( this.isDisabled() );
-       }
        if ( this.menu ) {
                this.menu.setDisabled( this.isDisabled() );
        }
@@ -16634,6 +16631,31 @@ OO.ui.ComboBoxWidget.prototype.setDisabled = function ( disabled ) {
        return this;
 };
 
+/**
+ * Set the options available for this input.
+ *
+ * @param {Object[]} options Array of menu options in the format `{ data: …, label: … }`
+ * @chainable
+ */
+OO.ui.ComboBoxInputWidget.prototype.setOptions = function ( options ) {
+       this.getMenu()
+               .clearItems()
+               .addItems( options.map( function ( opt ) {
+                       return new OO.ui.MenuOptionWidget( {
+                               data: opt.data,
+                               label: opt.label !== undefined ? opt.label : opt.data
+                       } );
+               } ) );
+
+       return this;
+};
+
+/**
+ * @class
+ * @deprecated Use OO.ui.ComboBoxInputWidget instead.
+ */
+OO.ui.ComboBoxWidget = OO.ui.ComboBoxInputWidget;
+
 /**
  * LabelWidgets help identify the function of interface elements. Each LabelWidget can
  * be configured with a `label` option that is set to a string, a label node, or a function:
@@ -18962,7 +18984,7 @@ OO.mixinClass( OO.ui.RadioSelectWidget, OO.ui.mixin.TabIndexedElement );
 /**
  * MenuSelectWidget is a {@link OO.ui.SelectWidget select widget} that contains options and
  * is used together with OO.ui.MenuOptionWidget. It is designed be used as part of another widget.
- * See {@link OO.ui.DropdownWidget DropdownWidget}, {@link OO.ui.ComboBoxWidget ComboBoxWidget},
+ * See {@link OO.ui.DropdownWidget DropdownWidget}, {@link OO.ui.ComboBoxInputWidget ComboBoxInputWidget},
  * and {@link OO.ui.mixin.LookupElement LookupElement} for examples of widgets that contain menus.
  * MenuSelectWidgets themselves are not instantiated directly, rather subclassed
  * and customized to be opened, closed, and displayed as needed.
@@ -18987,7 +19009,7 @@ OO.mixinClass( OO.ui.RadioSelectWidget, OO.ui.mixin.TabIndexedElement );
  * @constructor
  * @param {Object} [config] Configuration options
  * @cfg {OO.ui.TextInputWidget} [input] Text input used to implement option highlighting for menu items that match
- *  the text the user types. This config is used by {@link OO.ui.ComboBoxWidget ComboBoxWidget}
+ *  the text the user types. This config is used by {@link OO.ui.ComboBoxInputWidget ComboBoxInputWidget}
  *  and {@link OO.ui.mixin.LookupElement LookupElement}
  * @cfg {jQuery} [$input] Text input used to implement option highlighting for menu items that match
  *  the text the user types. This config is used by {@link OO.ui.CapsuleMultiSelectWidget CapsuleMultiSelectWidget}
@@ -19276,7 +19298,7 @@ OO.ui.MenuSelectWidget.prototype.toggle = function ( visible ) {
  * The menu's position is automatically calculated and maintained when the menu
  * is toggled or the window is resized.
  *
- * See OO.ui.ComboBoxWidget for an example of a widget that uses this class.
+ * See OO.ui.ComboBoxInputWidget for an example of a widget that uses this class.
  *
  * @class
  * @extends OO.ui.MenuSelectWidget
index f7b3862..a6abce5 100644 (file)
@@ -13,7 +13,7 @@
                        "ltr": "images/icons/find-ltr.svg",
                        "rtl": "images/icons/find-rtl.svg"
                } },
-               "insert": { "file": "images/icons/insert.svg" },
+               "insert": { "file": "images/icons/add.svg" },
                "layout": { "file": {
                        "ltr": "images/icons/layout-ltr.svg",
                        "rtl": "images/icons/layout-rtl.svg"
index 730d102..c0d04e9 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/add.png and b/resources/lib/oojs-ui/themes/apex/images/icons/add.png differ
index 85e39fe..62e5496 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
     <g id="add">
-        <path id="plus" d="M13 8h-2v3H8v2h3v3h2v-3h3v-2h-3z"/>
+        <path id="plus" d="M13 6h-2v5H6v2h5v5h2v-5h5v-2h-5z"/>
     </g>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/insert.png b/resources/lib/oojs-ui/themes/apex/images/icons/insert.png
deleted file mode 100644 (file)
index 97927a8..0000000
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/insert.png and /dev/null differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/insert.svg b/resources/lib/oojs-ui/themes/apex/images/icons/insert.svg
deleted file mode 100644 (file)
index d1e9ba3..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <g id="insert">
-        <path d="M13 5h-2v6H5v2h6v6h2v-6h6v-2h-6z" id="plus"/>
-    </g>
-</svg>
index 8b15525..875958b 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/noWikiText-ltr.png and b/resources/lib/oojs-ui/themes/apex/images/icons/noWikiText-ltr.png differ
index e0e4fc0..22c04ca 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <g>
-        <path d="M16 14l2 2V5h-4v2h2zm0 2L9 9 7 7 6 6 5 5 2 2 1 3l2 2H2v14h4v-2H4V7h1l2 2v10h4v-2H9v-6l6 6h-1v2h3l4 4 1-1-4-4zm-5-9V5H7l2 2zm8-2v2h2v10h-2l2 2h2V5z"/>
+    <g id="noWikiText-rtl">
+        <path d="M15 13l2 2V5h-3v2h1zM3 3L2 4l1 1v14h3v-2H5V7l2 2v10h3v-2H9v-6l6 6h-1v2h3l3 3 1-1-3-3zm7 4V5H7l2 2zm8-2v2h1v10l2 2V5z"/>
     </g>
 </svg>
index f7bab38..6ebb399 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/noWikiText-rtl.png and b/resources/lib/oojs-ui/themes/apex/images/icons/noWikiText-rtl.png differ
index a7be5ab..d717265 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <g id="g484">
-        <path d="M8 14l-2 2V5h4v2H8zm0 2l7-7 2-2 1-1 1-1 3-3 1 1-2 2h1v14h-4v-2h2V7h-1l-2 2v10h-4v-2h2v-6l-6 6h1v2H7l-4 4-1-1 4-4zm5-9V5h4l-2 2zM5 5v2H3v10h2l-2 2H1V5z" id="path486"/>
+    <g id="noWikiText-rtl">
+        <path d="M9 13l-2 2V5h3v2H9zM21 3l1 1-1 1v14h-3v-2h1V7l-2 2v10h-3v-2h1v-6l-6 6h1v2H7l-3 3-1-1 3-3zm-7 4V5h3l-2 2zM6 5v2H5v10l-2 2V5z"/>
     </g>
 </svg>
index 4b15161..850f157 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/wikiText.png and b/resources/lib/oojs-ui/themes/apex/images/icons/wikiText.png differ
index ded0fe1..4512cf5 100644 (file)
@@ -1,15 +1,9 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <g>
-        <path d="M23 5h-4v2h2v10h-2v2h4z"/>
-    </g>
-    <g>
-        <path d="M18 5h-4v2h2v10h-2v2h4z"/>
-    </g>
-    <g>
-        <path d="M2 5h4v2H4v10h2v2H2z"/>
-    </g>
-    <g>
-        <path d="M7 5h4v2H9v10h2v2H7z"/>
+    <g id="wikiText">
+        <path id="opening-bracket-inner" d="M7 19h3v-2H9V7h1V5H7z"/>
+        <path id="closing-bracket-inner" d="M17 19h-3v-2h1V7h-1V5h3z"/>
+        <path id="closing-bracket-outer" d="M21 19h-3v-2h1V7h-1V5h3z"/>
+        <path id="opening-bracket-outer" d="M3 19h3v-2H5V7h1V5H3z"/>
     </g>
 </svg>
index ef368c2..d981728 100644 (file)
@@ -19,7 +19,7 @@
                        "ltr": "images/icons/find-ltr.svg",
                        "rtl": "images/icons/find-rtl.svg"
                } },
-               "insert": { "file": "images/icons/insert.svg" },
+               "insert": { "file": "images/icons/add.svg" },
                "layout": { "file": {
                        "ltr": "images/icons/layout-ltr.svg",
                        "rtl": "images/icons/layout-rtl.svg"
index c65a5c1..e81206c 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/add-constructive.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/add-constructive.png differ
index ef2ac5c..dab0bea 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #00AF89 }</style>
     <g id="add">
-        <path id="plus" d="M13 8h-2v3H8v2h3v3h2v-3h3v-2h-3z"/>
+        <path id="plus" d="M13 6h-2v5H6v2h5v5h2v-5h5v-2h-5z"/>
     </g>
 </svg>
index d6144e3..34a293f 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/add-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/add-invert.png differ
index 2ad5f60..35322d0 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
     <g id="add">
-        <path id="plus" d="M13 8h-2v3H8v2h3v3h2v-3h3v-2h-3z"/>
+        <path id="plus" d="M13 6h-2v5H6v2h5v5h2v-5h5v-2h-5z"/>
     </g>
 </svg>
index 730d102..c0d04e9 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/add.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/add.png differ
index 85e39fe..62e5496 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
     <g id="add">
-        <path id="plus" d="M13 8h-2v3H8v2h3v3h2v-3h3v-2h-3z"/>
+        <path id="plus" d="M13 6h-2v5H6v2h5v5h2v-5h5v-2h-5z"/>
     </g>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/insert-invert.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/insert-invert.png
deleted file mode 100644 (file)
index dd08bc2..0000000
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/insert-invert.png and /dev/null differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/insert-invert.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/insert-invert.svg
deleted file mode 100644 (file)
index 54b8ffd..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
-    <g id="insert">
-        <path d="M13 5h-2v6H5v2h6v6h2v-6h6v-2h-6z" id="plus"/>
-    </g>
-</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/insert.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/insert.png
deleted file mode 100644 (file)
index 97927a8..0000000
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/insert.png and /dev/null differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/insert.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/insert.svg
deleted file mode 100644 (file)
index d1e9ba3..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <g id="insert">
-        <path d="M13 5h-2v6H5v2h6v6h2v-6h6v-2h-6z" id="plus"/>
-    </g>
-</svg>
index 72bbf34..0400c19 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/menu-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/menu-invert.png differ
index 3bda245..1081b4c 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
     <g id="menu">
-        <path id="lines" d="M6 15.5h12c.6 0 1 .4 1 1v1c0 .6-.4 1-1 1H6c-.6 0-1-.4-1-1v-1c0-.6.4-1 1-1zm-1-4v1c0 .6.4 1 1 1h12c.6 0 1-.4 1-1v-1c0-.6-.4-1-1-1H6c-.6 0-1 .4-1 1zm0-5v1c0 .6.4 1 1 1h12c.6 0 1-.4 1-1v-1c0-.6-.4-1-1-1H6c-.6 0-1 .4-1 1z"/>
+        <path id="lines" d="M6 15h12c.553 0 1 .447 1 1v1c0 .553-.447 1-1 1H6c-.553 0-1-.447-1-1v-1c0-.553.447-1 1-1zm-1-4v1c0 .553.447 1 1 1h12c.553 0 1-.447 1-1v-1c0-.553-.447-1-1-1H6c-.553 0-1 .447-1 1zm0-5v1c0 .553.447 1 1 1h12c.553 0 1-.447 1-1V6c0-.553-.447-1-1-1H6c-.553 0-1 .447-1 1z"/>
     </g>
 </svg>
index 45daeab..0a78119 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/menu.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/menu.png differ
index b84ab86..ce25178 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
     <g id="menu">
-        <path id="lines" d="M6 15.5h12c.6 0 1 .4 1 1v1c0 .6-.4 1-1 1H6c-.6 0-1-.4-1-1v-1c0-.6.4-1 1-1zm-1-4v1c0 .6.4 1 1 1h12c.6 0 1-.4 1-1v-1c0-.6-.4-1-1-1H6c-.6 0-1 .4-1 1zm0-5v1c0 .6.4 1 1 1h12c.6 0 1-.4 1-1v-1c0-.6-.4-1-1-1H6c-.6 0-1 .4-1 1z"/>
+        <path id="lines" d="M6 15h12c.553 0 1 .447 1 1v1c0 .553-.447 1-1 1H6c-.553 0-1-.447-1-1v-1c0-.553.447-1 1-1zm-1-4v1c0 .553.447 1 1 1h12c.553 0 1-.447 1-1v-1c0-.553-.447-1-1-1H6c-.553 0-1 .447-1 1zm0-5v1c0 .553.447 1 1 1h12c.553 0 1-.447 1-1V6c0-.553-.447-1-1-1H6c-.553 0-1 .447-1 1z"/>
     </g>
 </svg>
index dcd35ce..39e00b1 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-ltr-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-ltr-invert.png differ
index 770932b..e82e168 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
-    <g>
-        <path d="M16 14l2 2V5h-4v2h2zm0 2L9 9 7 7 6 6 5 5 2 2 1 3l2 2H2v14h4v-2H4V7h1l2 2v10h4v-2H9v-6l6 6h-1v2h3l4 4 1-1-4-4zm-5-9V5H7l2 2zm8-2v2h2v10h-2l2 2h2V5z"/>
+    <g id="noWikiText-rtl">
+        <path d="M15 13l2 2V5h-3v2h1zM3 3L2 4l1 1v14h3v-2H5V7l2 2v10h3v-2H9v-6l6 6h-1v2h3l3 3 1-1-3-3zm7 4V5H7l2 2zm8-2v2h1v10l2 2V5z"/>
     </g>
 </svg>
index 8b15525..875958b 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-ltr.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-ltr.png differ
index e0e4fc0..22c04ca 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <g>
-        <path d="M16 14l2 2V5h-4v2h2zm0 2L9 9 7 7 6 6 5 5 2 2 1 3l2 2H2v14h4v-2H4V7h1l2 2v10h4v-2H9v-6l6 6h-1v2h3l4 4 1-1-4-4zm-5-9V5H7l2 2zm8-2v2h2v10h-2l2 2h2V5z"/>
+    <g id="noWikiText-rtl">
+        <path d="M15 13l2 2V5h-3v2h1zM3 3L2 4l1 1v14h3v-2H5V7l2 2v10h3v-2H9v-6l6 6h-1v2h3l3 3 1-1-3-3zm7 4V5H7l2 2zm8-2v2h1v10l2 2V5z"/>
     </g>
 </svg>
index eda918c..6cd62a1 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-rtl-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-rtl-invert.png differ
index 2ecb331..5011541 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
-    <g id="g484">
-        <path d="M8 14l-2 2V5h4v2H8zm0 2l7-7 2-2 1-1 1-1 3-3 1 1-2 2h1v14h-4v-2h2V7h-1l-2 2v10h-4v-2h2v-6l-6 6h1v2H7l-4 4-1-1 4-4zm5-9V5h4l-2 2zM5 5v2H3v10h2l-2 2H1V5z" id="path486"/>
+    <g id="noWikiText-rtl">
+        <path d="M9 13l-2 2V5h3v2H9zM21 3l1 1-1 1v14h-3v-2h1V7l-2 2v10h-3v-2h1v-6l-6 6h1v2H7l-3 3-1-1 3-3zm-7 4V5h3l-2 2zM6 5v2H5v10l-2 2V5z"/>
     </g>
 </svg>
index f7bab38..6ebb399 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-rtl.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/noWikiText-rtl.png differ
index a7be5ab..d717265 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <g id="g484">
-        <path d="M8 14l-2 2V5h4v2H8zm0 2l7-7 2-2 1-1 1-1 3-3 1 1-2 2h1v14h-4v-2h2V7h-1l-2 2v10h-4v-2h2v-6l-6 6h1v2H7l-4 4-1-1 4-4zm5-9V5h4l-2 2zM5 5v2H3v10h2l-2 2H1V5z" id="path486"/>
+    <g id="noWikiText-rtl">
+        <path d="M9 13l-2 2V5h3v2H9zM21 3l1 1-1 1v14h-3v-2h1V7l-2 2v10h-3v-2h1v-6l-6 6h1v2H7l-3 3-1-1 3-3zm-7 4V5h3l-2 2zM6 5v2H5v10l-2 2V5z"/>
     </g>
 </svg>
index bdf5238..5e53c45 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/wikiText-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/wikiText-invert.png differ
index e050baa..ec12d0e 100644 (file)
@@ -1,15 +1,9 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
-    <g>
-        <path d="M23 5h-4v2h2v10h-2v2h4z"/>
-    </g>
-    <g>
-        <path d="M18 5h-4v2h2v10h-2v2h4z"/>
-    </g>
-    <g>
-        <path d="M2 5h4v2H4v10h2v2H2z"/>
-    </g>
-    <g>
-        <path d="M7 5h4v2H9v10h2v2H7z"/>
+    <g id="wikiText">
+        <path id="opening-bracket-inner" d="M7 19h3v-2H9V7h1V5H7z"/>
+        <path id="closing-bracket-inner" d="M17 19h-3v-2h1V7h-1V5h3z"/>
+        <path id="closing-bracket-outer" d="M21 19h-3v-2h1V7h-1V5h3z"/>
+        <path id="opening-bracket-outer" d="M3 19h3v-2H5V7h1V5H3z"/>
     </g>
 </svg>
index 4b15161..850f157 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/wikiText.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/wikiText.png differ
index ded0fe1..4512cf5 100644 (file)
@@ -1,15 +1,9 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <g>
-        <path d="M23 5h-4v2h2v10h-2v2h4z"/>
-    </g>
-    <g>
-        <path d="M18 5h-4v2h2v10h-2v2h4z"/>
-    </g>
-    <g>
-        <path d="M2 5h4v2H4v10h2v2H2z"/>
-    </g>
-    <g>
-        <path d="M7 5h4v2H9v10h2v2H7z"/>
+    <g id="wikiText">
+        <path id="opening-bracket-inner" d="M7 19h3v-2H9V7h1V5H7z"/>
+        <path id="closing-bracket-inner" d="M17 19h-3v-2h1V7h-1V5h3z"/>
+        <path id="closing-bracket-outer" d="M21 19h-3v-2h1V7h-1V5h3z"/>
+        <path id="opening-bracket-outer" d="M3 19h3v-2H5V7h1V5H3z"/>
     </g>
 </svg>
index 9c18a91..c472ac7 100644 (file)
@@ -1,4 +1,4 @@
-/**
+/*!
  * HTML5 placeholder emulation for jQuery plugin
  *
  * This will automatically use the HTML5 placeholder attribute if supported, or emulate this behavior if not.
@@ -13,6 +13,7 @@
  * @version 2.1.0
  * @license MIT
  */
+
 ( function ( $ ) {
 
        var isInputSupported = 'placeholder' in document.createElement( 'input' ),
index c39dc55..2318b33 100644 (file)
@@ -1,4 +1,4 @@
-/**
+/*!
  * TableSorter for MediaWiki
  *
  * Written 2011 Leo Koppelkamm
  * and mw.language.months.
  *
  * Uses 'tableSorterCollation' in mw.config (if available)
- */
-/**
  *
- * @description Create a sortable table with multi-column sorting capabilities
+ * Create a sortable table with multi-column sorting capabilities
  *
- * @example $( 'table' ).tablesorter();
- * @desc Create a simple tablesorter interface.
+ *      // Create a simple tablesorter interface
+ *      $( 'table' ).tablesorter();
  *
- * @example $( 'table' ).tablesorter( { sortList: [ { 0: 'desc' }, { 1: 'asc' } ] } );
- * @desc Create a tablesorter interface initially sorting on the first and second column.
+ *      // Create a tablesorter interface, initially sorting on the first and second column
+ *      $( 'table' ).tablesorter( { sortList: [ { 0: 'desc' }, { 1: 'asc' } ] } );
  *
- * @option String cssHeader ( optional ) A string of the class name to be appended
- *         to sortable tr elements in the thead of the table. Default value:
- *         "header"
+ * @param {string} [cssHeader="header"] A string of the class name to be appended to sortable
+ *         tr elements in the thead of the table.
  *
- * @option String cssAsc ( optional ) A string of the class name to be appended to
- *         sortable tr elements in the thead on a ascending sort. Default value:
- *         "headerSortUp"
+ * @param {string} [cssAsc="headerSortUp"] A string of the class name to be appended to
+ *         sortable tr elements in the thead on a ascending sort.
  *
- * @option String cssDesc ( optional ) A string of the class name to be appended
- *         to sortable tr elements in the thead on a descending sort. Default
- *         value: "headerSortDown"
+ * @param {string} [cssDesc="headerSortDown"] A string of the class name to be appended to
+ *         sortable tr elements in the thead on a descending sort.
  *
- * @option String sortMultisortKey ( optional ) A string of the multi-column sort
- *         key. Default value: "shiftKey"
+ * @param {string} [sortMultisortKey="shiftKey"] A string of the multi-column sort key.
  *
- * @option Boolean cancelSelection ( optional ) Boolean flag indicating if
- *         tablesorter should cancel selection of the table headers text.
- *         Default value: true
+ * @param {boolean} [cancelSelection=true] Boolean flag indicating iftablesorter should cancel
+ *         selection of the table headers text.
  *
- * @option Array sortList ( optional ) An array containing objects specifying sorting.
- *         By passing more than one object, multi-sorting will be applied. Object structure:
+ * @param {Array} [sortList] An array containing objects specifying sorting. By passing more
+ *         than one object, multi-sorting will be applied. Object structure:
  *         { <Integer column index>: <String 'asc' or 'desc'> }
- *         Default value: []
  *
  * @event sortEnd.tablesorter: Triggered as soon as any sorting has been applied.
  *
- * @type jQuery
- *
- * @name tablesorter
- *
- * @cat Plugins/Tablesorter
- *
  * @author Christian Bach/christian.bach@polyester.se
  */
-
 ( function ( $, mw ) {
        var ts,
                parsers = [];
index b901642..5e93ba6 100644 (file)
@@ -96,7 +96,7 @@
                         * Inserts text at the beginning and end of a text selection, optionally
                         * inserting text at the caret when selection is empty.
                         *
-                        * @fixme document the options parameters
+                        * FIXME document the options parameters
                         */
                        encapsulateSelection: function ( options ) {
                                return this.each( function () {
                         *
                         * Will focus the textarea in some browsers (IE/Opera)
                         *
-                        * @fixme document the options parameters
+                        * FIXME document the options parameters
                         */
                        getCaretPosition: function ( options ) {
                                function getCaret( e ) {
                                return getCaret( this.get( 0 ) );
                        },
                        /**
-                        * @fixme document the options parameters
+                        * FIXME document the options parameters
                         */
                        setSelection: function ( options ) {
                                return this.each( function () {
                         * @param {boolean} options Whether to force a scroll even if the caret position
                         *  is already visible. Defaults to false
                         *
-                        * @fixme document the options parameters (function body suggests options.force is a boolean, not options itself)
+                        * FIXME document the options parameters (function body suggests options.force is a boolean, not options itself)
                         */
                        scrollToCaretPosition: function ( options ) {
                                function getLineLength( e ) {
index 8807616..ce565e0 100644 (file)
@@ -153,11 +153,6 @@ img {
        vertical-align: middle;
 }
 
-/* math */
-span.texhtml {
-       font-family: serif;
-}
-
 /**
  * Links
  */
index 59f5592..07d9780 100644 (file)
@@ -121,20 +121,6 @@ span.comment {
        font-style: italic;
 }
 
-/* Math */
-.texvc {
-       direction: ltr;
-       unicode-bidi: embed;
-}
-
-img.tex {
-       vertical-align: middle;
-}
-
-span.texhtml {
-       font-family: serif;
-}
-
 /**
  * Add a bit of margin space between the preview and the toolbar.
  * This replaces the ugly <p><br /></p> we used to insert into the page source
index d5e5b96..7a7b9cd 100644 (file)
         * internally and for dates accepted by #setDate and returned by #getDate.
         *
         * @private
-        * @returns {string} Format
+        * @return {string} Format
         */
        mw.widgets.CalendarWidget.prototype.getDateFormat = function () {
                return {
         * Get the date precision this calendar uses, 'day' or 'month'.
         *
         * @private
-        * @returns {string} Precision, 'day' or 'month'
+        * @return {string} Precision, 'day' or 'month'
         */
        mw.widgets.CalendarWidget.prototype.getPrecision = function () {
                return this.precision;
         * Get list of possible display layers.
         *
         * @private
-        * @returns {string[]} Layers
+        * @return {string[]} Layers
         */
        mw.widgets.CalendarWidget.prototype.getDisplayLayers = function () {
                return [ 'month', 'year', 'duodecade' ].slice( this.precision === 'month' ? 1 : 0 );
         * Get current date, in the format 'YYYY-MM-DD' or 'YYYY-MM', depending on precision. Digits will
         * not be localised.
         *
-        * @returns {string|null} Date string
+        * @return {string|null} Date string
         */
        mw.widgets.CalendarWidget.prototype.getDate = function () {
                return this.date;
index b1e5151..1ffcc66 100644 (file)
         * @private
         * @param {string} date Date string, to be valid, must be in 'YYYY-MM-DD' or 'YYYY-MM' format or
         *     (unless the field is required) empty
-        * @returns {boolean}
+        * @return {boolean}
         */
        mw.widgets.DateInputWidget.prototype.validateDate = function ( date ) {
                var isValid;
        /**
         * @private
         * @param {string} date Date string, to be valid, must be in 'YYYY-MM-DD' or 'YYYY-MM' format
-        * @returns {boolean}
+        * @return {boolean}
         */
        mw.widgets.DateInputWidget.prototype.isValidDate = function ( date ) {
                // "Half-strict mode": for example, for the format 'YYYY-MM-DD', 2015-1-3 instead of 2015-01-03
         * @private
         * @param {string} date Date string, to be valid, must be empty (no date selected) or in
         *     'YYYY-MM-DD' or 'YYYY-MM' format to be valid
-        * @returns {boolean}
+        * @return {boolean}
         */
        mw.widgets.DateInputWidget.prototype.isInRange = function ( date ) {
                var momentDate = moment( date, 'YYYY-MM-DD' ),
index 0e2546f..546fbf8 100644 (file)
        mw.widgets.TitleSearchWidget.prototype.onQueryChange = function () {
                var widget = this;
 
-               this.getSuggestionsPromise().done( function ( response ) {
+               if ( this.currentRequest ) {
+                       this.currentRequest.abort();
+               }
+
+               this.currentRequest = this.getSuggestionsPromise();
+               this.currentRequest.done( function ( response ) {
                        // Parent method
                        mw.widgets.TitleSearchWidget.parent.prototype.onQueryChange.call( widget );
 
                        widget.results.addItems( widget.getOptionsFromData( response.query || {} ) );
+
+                       widget.currentRequest = false;
                } );
        };
 
index 76fe959..67f3e01 100644 (file)
         * Get option widgets from the server response
         *
         * @param {Object} data Query result
-        * @returns {OO.ui.OptionWidget[]} Menu items
+        * @return {OO.ui.OptionWidget[]} Menu items
         */
        mw.widgets.TitleWidget.prototype.getOptionsFromData = function ( data ) {
                var i, len, index, pageExists, pageExistsExact, suggestionPage, page, redirect, redirects,
         * Get title object corresponding to given value, or #getQueryValue if not given.
         *
         * @param {string} [value] Value to get a title for
-        * @returns {mw.Title|null} Title object, or null if value is invalid
+        * @return {mw.Title|null} Title object, or null if value is invalid
         */
        mw.widgets.TitleWidget.prototype.getTitle = function ( value ) {
                var title = value !== undefined ? value : this.getQueryValue(),
index 0d0fb73..164fd20 100644 (file)
@@ -99,7 +99,7 @@
         * Get list of menu items from a server response.
         *
         * @param {Object} data Query result
-        * @returns {OO.ui.MenuOptionWidget[]} Menu items
+        * @return {OO.ui.MenuOptionWidget[]} Menu items
         */
        mw.widgets.UserInputWidget.prototype.getLookupMenuOptionsFromData = function ( data ) {
                var len, i, user,
index c616829..4ce2c1a 100644 (file)
         *
         * @protected
         * @fires fileSaved
-        * @returns {jQuery.Promise} Rejects the promise with an
+        * @return {jQuery.Promise} Rejects the promise with an
         * {@link OO.ui.Error error}, or resolves if the upload was successful.
         */
        mw.Upload.BookletLayout.prototype.saveFile = function () {
         * state and state details.
         *
         * @protected
-        * @returns {OO.ui.Error} Error to display for given state and details.
+        * @return {OO.ui.Error} Error to display for given state and details.
         */
        mw.Upload.BookletLayout.prototype.getErrorMessageForStateDetails = function () {
                var message,
         *
         * @protected
         * @fires selectFile
-        * @returns {OO.ui.FormLayout}
+        * @return {OO.ui.FormLayout}
         */
        mw.Upload.BookletLayout.prototype.renderUploadForm = function () {
                var fieldset;
         * property.
         *
         * @protected
-        * @returns {OO.ui.FormLayout}
+        * @return {OO.ui.FormLayout}
         */
        mw.Upload.BookletLayout.prototype.renderInfoForm = function () {
                var fieldset;
         * sets the {@link #insertForm insertForm} property.
         *
         * @protected
-        * @returns {OO.ui.FormLayout}
+        * @return {OO.ui.FormLayout}
         */
        mw.Upload.BookletLayout.prototype.renderInsertForm = function () {
                var fieldset;
         * {@link #uploadForm upload form}.
         *
         * @protected
-        * @returns {File|null}
+        * @return {File|null}
         */
        mw.Upload.BookletLayout.prototype.getFile = function () {
                return this.selectFileWidget.getValue();
         * {@link #infoForm information form}.
         *
         * @protected
-        * @returns {string}
+        * @return {string}
         */
        mw.Upload.BookletLayout.prototype.getFilename = function () {
                return this.filenameWidget.getValue();
         * {@link #infoForm information form}.
         *
         * @protected
-        * @returns {string}
+        * @return {string}
         */
        mw.Upload.BookletLayout.prototype.getText = function () {
                return this.descriptionWidget.getValue();
index 53afca8..5f2569c 100644 (file)
                                this.uploadBooklet.clear();
                        }, this );
        };
-
 }( jQuery, mediaWiki ) );
index 9d29b4c..835b423 100644 (file)
@@ -68,9 +68,8 @@
                if ( val === undefined || val === null || val === '' ) {
                        return '';
                }
-               /* jshint latedef:false */
+
                return pre + ( raw ? val : mw.Uri.encode( val ) ) + post;
-               /* jshint latedef:true */
        }
 
        /**
                }() );
 
                /**
-                * @class mw.Uri
-                * @constructor
-                *
                 * Construct a new URI object. Throws error if arguments are illegal/impossible, or
                 * otherwise don't parse.
                 *
+                * @class mw.Uri
+                * @constructor
                 * @param {Object|string} [uri] URI string, or an Object with appropriate properties (especially
                 *  another URI object to clone). Object must have non-blank `protocol`, `host`, and `path`
                 *  properties. If omitted (or set to `undefined`, `null` or empty string), then an object
                 * @param {boolean} [options.overrideKeys=false] Whether to let duplicate query parameters
                 *  override each other (`true`) or automagically convert them to an array (`false`).
                 */
-               /* jshint latedef:false */
                function Uri( uri, options ) {
                        var prop,
                                defaultUri = getDefaultUri();
index b62e8d1..ad1069f 100644 (file)
@@ -76,7 +76,7 @@
                 *  that the user will be assigned to that bucket
                 * @param {string} token A token that uniquely identifies the user for the
                 *  duration of the experiment
-                * @returns {string} The bucket
+                * @return {string} The bucket
                 */
                getBucket: function ( experiment, token ) {
                        var buckets = experiment.buckets,
index d226ed9..68818be 100644 (file)
        /**
         * Get the bug report link
         *
-        * @returns {string} Link to the external bug report form
+        * @return {string} Link to the external bug report form
         */
        mw.Feedback.Dialog.prototype.getBugReportLink = function () {
                return this.bugReportLink;
index b598211..765e92f 100644 (file)
@@ -1,7 +1,7 @@
 /* HTMLForm styles */
 
 table.mw-htmlform-nolabel td.mw-label {
-       width: 1px !important;
+       display: none;
 }
 
 .mw-htmlform-invalid-input td.mw-input input {
index 3f7b8fd..36b45f1 100644 (file)
         * The underscore in the name is to avoid a bug <https://github.com/senchalabs/jsduck/issues/304>.
         * It is not part of the actual class name.
         *
+        * The constructor is not publicly accessible; use mw.notification#notify instead.
+        * This does not insert anything into the document (see #start).
+        *
         * @class mw.Notification_
         * @alternateClassName mw.Notification
-        *
-        * @constructor The constructor is not publicly accessible; use mw.notification#notify instead.
-        *  This does not insert anything into the document (see #start).
+        * @constructor
         * @private
         */
        function Notification( message, options ) {
diff --git a/resources/src/mediawiki/mediawiki.requestIdleCallback.js b/resources/src/mediawiki/mediawiki.requestIdleCallback.js
new file mode 100644 (file)
index 0000000..796639f
--- /dev/null
@@ -0,0 +1,50 @@
+/*!
+ * An interface for scheduling background tasks.
+ *
+ * Loosely based on https://w3c.github.io/requestidlecallback/
+ */
+( function ( mw, $ ) {
+       var tasks = [],
+               maxIdleDuration = 50,
+               timeout = null;
+
+       function schedule( trigger ) {
+               clearTimeout( timeout );
+               timeout = setTimeout( trigger, 700 );
+       }
+
+       function triggerIdle() {
+               var elapsed,
+                       start = mw.now();
+
+               while ( tasks.length ) {
+                       elapsed = mw.now() - start;
+                       if ( elapsed < maxIdleDuration ) {
+                               tasks.shift().callback();
+                       } else {
+                               // Idle moment expired, try again later
+                               schedule( triggerIdle );
+                               break;
+                       }
+               }
+       }
+
+       mw.requestIdleCallbackInternal = function ( callback ) {
+               var task = { callback: callback };
+               tasks.push( task );
+
+               $( function () { schedule( triggerIdle ); } );
+       };
+
+       /**
+        * Schedule a deferred task to run in the background.
+        *
+        * @member mw
+        * @param {Function} callback
+        */
+       mw.requestIdleCallback = window.requestIdleCallback
+               ? function ( callback ) {
+                       window.requestIdleCallback( callback );
+               }
+               : mw.requestIdleCallbackInternal;
+}( mediaWiki, jQuery ) );
index 4e6c591..58860e9 100644 (file)
@@ -14,9 +14,8 @@
 # Plus any combination of these:
 #
 # cat           add category links
-#               (ignored by Parsoid, since it emits <link>s)
 # ill           add inter-language links
-#               (ignored by Parsoid, since it emits <links>s)
+#               (ignored by Parsoid, since it emits <link>s)
 # subpage       enable subpages (disabled by default)
 # noxml         don't check for XML well-formedness
 # title=[[XXX]] run test using article title XXX
@@ -3330,18 +3329,14 @@ parsoid=wt2html,wt2wt
 <span about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":" [[Category:foo]]"}},"i":0}}]}'> </span><link rel="mw:PageProp/Category" href="./Category:Foo" about="#mwt1"> <!-- No pre&#x2D;wrapping -->
 !! end
 
-## We used to, but no longer wt2wt this test since the default serializer
-## will normalize all categories to serialize on their own line.
-## This wikitext usage is going to be fairly uncommon in production and
-## selser will take care of preserving formatting in those scenarios.
 !! test
 7b. Indent-pre and category links
 !! options
-parsoid=wt2html
+parsoid=wt2html,wt2wt
 !! wikitext
  [[Category:foo]] a
  [[Category:foo]] {{echo|b}}
-!! html/parsoid
+!! html
 <pre><link rel="mw:PageProp/Category" href="./Category:Foo"> a
  <link rel="mw:PageProp/Category" href="./Category:Foo"> <span about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"b"}},"i":0}}]}'>b</span></pre>
 !! end
@@ -10865,14 +10860,8 @@ Un-closed <includeonly>
 !! html
 !! end
 
-## We used to, but no longer wt2wt this test since the default serializer
-## will normalize the include directives to serialize on their own line.
-## Selser will take care of preserving formatting in scenarios where they
-## intermingled with other wikitext.
 !! test
 Includes and comments at SOL
-!! options
-parsoid=wt2html,html2html
 !! wikitext
 <!-- comment --><noinclude><!-- comment --></noinclude><!-- comment -->== hu ==
 
@@ -11053,14 +11042,10 @@ parsoid=wt2html,wt2wt
 </tbody></table>
 !!end
 
-## We used to, but no longer wt2wt this test since the default serializer
-## will normalize the include directives to serialize on their own line.
-## Selser will take care of preserving formatting in scenarios where they
-## intermingled with other wikitext.
 !!test
 2. Table tag in SOL posn. should get reparsed correctly with valid TSR
 !!options
-parsoid=wt2html
+parsoid=wt2html,wt2wt
 !!wikitext
 <includeonly>a</includeonly>{| {{{b}}}
 |c
@@ -14275,22 +14260,15 @@ pst
 [[Category:Foo (bar)|Foo]]
 !! end
 
-## We used to, but no longer wt2wt this test since the default serializer
-## will normalize all categories to serialize on their own line.
-## This wikitext usage is going to be fairly uncommon in production and
-## selser will take care of preserving formatting in those scenarios.
 !! test
 Category with link tail
 !! options
 cat
 pst
-parsoid=wt2html
 !! wikitext
 123[[Category:Foo]]456
-!! html/php
+!! html
 123[[Category:Foo]]456
-!! html/parsoid
-<p>123<link rel="mw:PageProp/Category" href="Category:Foo"/>456</p>
 !! end
 
 !! test
@@ -14326,14 +14304,8 @@ pst
 [[Category:{{echo|Foo}}|{{echo|Bar}}]]
 !! end
 
-## We used to, but no longer wt2wt this test since the default serializer
-## will normalize all categories to serialize on their own line.
-## This wikitext usage is going to be fairly uncommon in production and
-## selser will take care of preserving formatting in those scenarios.
 !! test
 Category / paragraph interactions
-!! options
-parsoid=wt2html
 !! wikitext
 Foo [[Category:Baz]] Bar
 
@@ -14360,7 +14332,7 @@ Bar
 [[Category:Baz]]
  {{echo|[[Category:Baz]]}}
 [[Category:Baz]]
-!! html/php
+!! html
 <p>Foo Bar
 </p><p>Foo
 Bar
@@ -14370,32 +14342,20 @@ Bar
 </p><p>Foo
 Bar
 </p>
-!! html/parsoid
-<p>Foo <link rel="mw:PageProp/Category" href="Category:Baz"/> Bar</p>
-<p>Foo <link rel="mw:PageProp/Category" href="Category:Baz"/> Bar</p>
-<p>Foo <link rel="mw:PageProp/Category" href="Category:Baz"/> Bar</p>
-<p>Foo <link rel="mw:PageProp/Category" href="Category:Baz"/> Bar</p>
-<p>Foo <link rel="mw:PageProp/Category" href="Category:Baz"/> <link rel="mw:PageProp/Category" href="Category:Baz"/> <link rel="mw:PageProp/Category" href="Category:Baz"/> Bar <link rel="mw:PageProp/Category" href="Category:Baz"/> <link rel="mw:PageProp/Category" href="Category:Baz"/> <link rel="mw:PageProp/Category" href="Category:Baz"/> <link rel="mw:PageProp/Category" href="Category:Baz"/> <link rel="mw:PageProp/Category" href="Category:Baz" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"[[Category:Baz]]"}},"i":0}}]}'/></p>
-<link rel="mw:PageProp/Category" href="Category:Baz"/>
 !! end
 
-## We used to, but no longer wt2wt this test since the default serializer
-## will normalize all categories to serialize on their own line.
-## This wikitext usage is going to be fairly uncommon in production and
-## selser will take care of preserving formatting in those scenarios.
-##
 ## The whitespace on the empty line is part of the test. Please do not delete
 !! test
 1. Categories and newlines: All preceding newlines should be suppressed (courtesy bug 87)
 !! options
-parsoid=wt2html
+parsoid=wt2html,wt2wt
 !! wikitext
 This
    
 [[Category:Foo]] and this should be part of same paragraph (not an indent-pre)
    
 {{echo|[[Category:Foo]] and so should this!}}
-!! html/php
+!! html
 <p>This and this should be part of same paragraph (not an indent-pre) and so should this!
 </p>
 !! html/parsoid
@@ -14493,14 +14453,8 @@ parsoid=wt2html
 <link rel="mw:PageProp/Category" href="./Category:Foo" data-parsoid='{"stx":"simple","a":{"href":"./Category:Foo"},"sa":{"href":"Category:Foo"}}'/>
 !! end
 
-## We used to, but no longer wt2wt this test since the default serializer
-## will normalize all categories to serialize on their own line.
-## This wikitext usage is going to be fairly uncommon in production and
-## selser will take care of preserving formatting in those scenarios.
 !! test
 6. Categories and newlines: migrateTrailingCategories dom pass should not migrate categories not preceded by newlines
-!! options
-parsoid=wt2html
 !! wikitext
 * a [[Category:Foo]]
 !! html/parsoid
@@ -14516,7 +14470,7 @@ parsoid=wt2html
 * {{echo|a
 [[Category:Foo]]}}
 !! html/parsoid
-<ul><li> <span about="#mwt1" typeof="mw:Transclusion" data-parsoid='{"pi":[[{"k":"1","spc":["","","",""]}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"a\n[[Category:Foo]]"}},"i":0}}]}'>a</span><span about="#mwt1">
+<ul><li> <span about="#mwt1" typeof="mw:Transclusion" data-parsoid='{"pi":[[{"k":"1","spc":["","","",""]}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"a\n[[Category:Foo]]"}},"i":0}}]}'>a
 </span><link rel="mw:PageProp/Category" href="./Category:Foo" about="#mwt1" data-parsoid='{"stx":"simple","a":{"href":"./Category:Foo"},"sa":{"href":"Category:Foo"}}'/></li></ul>
 !! end
 
@@ -14551,20 +14505,13 @@ parsoid
 </p>
 !! end
 
-# We used to, but no longer wt2wt this test since the default serializer
-# will normalize all categories to serialize on their own line.
-# This wikitext usage is going to be fairly uncommon in production and
-# selser will take care of preventing whitespace insertion if this
-# occurs in an article.
-#
-# html2html disabled for the same reason (whitespace insertion between
-# x and y).
-#
-# html2wt disabled because it localizes the "Category" namespace.
+# html2wt localizes the "Category" namespace.
+# XXX the <link> element needs an empty data-parsoid attribute, or
+# else the html2html test fails because spaces are inserted.
 !! test
 Link prefix/suffixes aren't applied to category links
 !! options
-parsoid=wt2html
+parsoid=wt2html,wt2wt,html2html
 language=is
 !! wikitext
 x[[Category:Foo]]y
@@ -17173,8 +17120,8 @@ Special page transclusion
 !! wikitext
 {{Special:Prefixindex/Xyzzyx}}
 !! html
-<div class="mw-prefixindex-body"><ul class="mw-prefixindex-list"><li> <a href="/wiki/Xyzzyx" title="Xyzzyx">Xyzzyx</a> </li>
-</ul></div>
+<ul class="mw-prefixindex-list"><li><a href="/wiki/Xyzzyx" title="Xyzzyx">Xyzzyx</a></li>
+</ul>
 
 !! end
 
@@ -17185,10 +17132,10 @@ Special page transclusion twice (bug 5021)
 {{Special:Prefixindex/Xyzzyx}}
 {{Special:Prefixindex/Xyzzyx}}
 !! html
-<div class="mw-prefixindex-body"><ul class="mw-prefixindex-list"><li> <a href="/wiki/Xyzzyx" title="Xyzzyx">Xyzzyx</a> </li>
-</ul></div>
-<div class="mw-prefixindex-body"><ul class="mw-prefixindex-list"><li> <a href="/wiki/Xyzzyx" title="Xyzzyx">Xyzzyx</a> </li>
-</ul></div>
+<ul class="mw-prefixindex-list"><li><a href="/wiki/Xyzzyx" title="Xyzzyx">Xyzzyx</a></li>
+</ul>
+<ul class="mw-prefixindex-list"><li><a href="/wiki/Xyzzyx" title="Xyzzyx">Xyzzyx</a></li>
+</ul>
 
 !! end
 
@@ -19264,25 +19211,17 @@ Category:分類
 blah
 !! endarticle
 
-## We used to, but no longer wt2wt this test since the default serializer
-## will normalize all categories to serialize on their own line.
-## This wikitext usage is going to be fairly uncommon in production and
-## selser will take care of preserving formatting in those scenarios.
 !! test
 Don't convert blue categorylinks to another variant (bug 33210)
 !! options
-cat
-language=zh
-parsoid=wt2html
+language=zh cat
 !! wikitext
 [[A]][[Category:分类]]
-!! html/php
+!! html
 <a href="/wiki/Category:%E5%88%86%E7%B1%BB" title="Category:分类">分类</a>
-!! html/parsoid
-<p><a rel="mw:WikiLink" href="A" title="A">A</a></p>
-<link rel="mw:PageProp/Category" href="Category:分类"/>
 !! end
 
+
 !! test
 Stripping -{}- tags (language variants)
 !! options
@@ -23281,7 +23220,7 @@ parsoid=html2wt
 <p>this is not a link: http://example.com
 </p>
 !! wikitext
-<nowiki>this is not a link: http://example.com</nowiki>
+this is not a link: <nowiki>http://example.com</nowiki>
 !! end
 
 !! test
@@ -23626,8 +23565,7 @@ parsoid=html2wt
 
  __TOC__ foo
 
-__TOC__
- bar
+__TOC__ bar
 !! end
 
 #### --------------- HTML tags ---------------
@@ -25433,7 +25371,7 @@ parsoid=html2wt
 !! html/parsoid
 <p>http://example.com is not a link.</p>
 !! wikitext
-<nowiki>http://example.com is not a link.</nowiki>
+<nowiki>http://example.com</nowiki> is not a link.
 !! end
 
 !! test
@@ -25443,7 +25381,7 @@ parsoid=html2wt
 !! html/parsoid
 <p><a rel="mw:ExtLink" href="http://example.com">http://example.com</a> http://example.com is not a link.</p>
 !! wikitext
-http://example.com<nowiki> http://example.com is not a link.</nowiki>
+http://example.com <nowiki>http://example.com</nowiki> is not a link.
 !! end
 
 !! test
@@ -25686,26 +25624,6 @@ parsoid=html2wt
 # Tests spec'ing wikitext serialization norms |
 # --------------------------------------------
 
-!! test
-1. Categories should always be serialized on their own line
-!! options
-parsoid=html2wt
-!! html/parsoid
-foo<link rel="mw:PageProp/Category" href="./Category:Foo">bar
-!! wikitext
-foo
-[[Category:Foo]]
-bar
-!! end
-
-!! test
-2. Categories that are part of templates should not introduce a line break
-!! wikitext
-foo {{echo|<span>bar</span> [[Category:baz]]}} bar
-!! html/parsoid
-<p>foo <span about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"&lt;span>bar&lt;/span> [[Category:baz]]"}},"i":0}}]}'>bar</span><span about="#mwt1"> </span><link rel="mw:PageProp/Category" href="./Category:Baz" about="#mwt1" data-parsoid='{"stx":"simple","a":{"href":"./Category:Baz"},"sa":{"href":"Category:baz"}}'/> bar</p>
-!! end
-
 !! test
 Lists: Add space after bullets
 !! options
index 8c09471..31e4f5b 100644 (file)
@@ -247,4 +247,13 @@ class DatabaseMysqlBaseTest extends MediaWikiTestCase {
                );
        }
 
+       function testMasterPos() {
+               $pos1 = new MySQLMasterPos( 'db1034-bin.000976', '843431247' );
+               $pos2 = new MySQLMasterPos( 'db1034-bin.000976', '843431248' );
+
+               $this->assertTrue( $pos1->hasReached( $pos1 ) );
+               $this->assertTrue( $pos2->hasReached( $pos2 ) );
+               $this->assertTrue( $pos2->hasReached( $pos1 ) );
+               $this->assertFalse( $pos1->hasReached( $pos2 ) );
+       }
 }
index ea1a4f6..9808a55 100644 (file)
@@ -128,8 +128,6 @@ class JobQueueTest extends MediaWikiTestCase {
                $queue->flushCaches();
                if ( $recycles ) {
                        $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
-               } else {
-                       $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
                }
 
                $job2 = $queue->pop();
@@ -139,8 +137,6 @@ class JobQueueTest extends MediaWikiTestCase {
                $queue->flushCaches();
                if ( $recycles ) {
                        $this->assertEquals( 2, $queue->getAcquiredCount(), "Active job count ($desc)" );
-               } else {
-                       $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
                }
 
                $queue->ack( $job1 );
@@ -148,8 +144,6 @@ class JobQueueTest extends MediaWikiTestCase {
                $queue->flushCaches();
                if ( $recycles ) {
                        $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
-               } else {
-                       $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
                }
 
                $queue->ack( $job2 );
@@ -215,8 +209,6 @@ class JobQueueTest extends MediaWikiTestCase {
                $this->assertEquals( 0, $queue->getSize(), "Queue is empty ($desc)" );
                if ( $recycles ) {
                        $this->assertEquals( 1, $queue->getAcquiredCount(), "Active job count ($desc)" );
-               } else {
-                       $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
                }
 
                $queue->ack( $job1 );
@@ -225,6 +217,29 @@ class JobQueueTest extends MediaWikiTestCase {
                $this->assertEquals( 0, $queue->getAcquiredCount(), "Active job count ($desc)" );
        }
 
+       /**
+        * @dataProvider provider_queueLists
+        * @covers JobQueue
+        */
+       public function testDeduplicationWhileClaimed( $queue, $recycles, $desc ) {
+               $queue = $this->$queue;
+               if ( !$queue ) {
+                       $this->markTestSkipped( $desc );
+               }
+
+               $job = $this->newDedupedJob();
+               $queue->push( $job );
+
+               // De-duplication does not apply to already-claimed jobs
+               $j = $queue->pop();
+               $queue->push( $job );
+               $queue->ack( $j );
+
+               $j = $queue->pop();
+               // Make sure ack() of the twin did not delete the sibling data
+               $this->assertType( 'NullJob', $j );
+       }
+
        /**
         * @dataProvider provider_queueLists
         * @covers JobQueue
index 26c4e08..1db0eeb 100644 (file)
@@ -63,6 +63,7 @@ return array(
                        'tests/qunit/suites/resources/jquery/jquery.tablesorter.parsers.test.js',
                        'tests/qunit/suites/resources/jquery/jquery.textSelection.test.js',
                        'tests/qunit/data/mediawiki.jqueryMsg.data.js',
+                       'tests/qunit/suites/resources/mediawiki/mediawiki.requestIdleCallback.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.errorLogger.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js',
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.requestIdleCallback.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.requestIdleCallback.test.js
new file mode 100644 (file)
index 0000000..3772097
--- /dev/null
@@ -0,0 +1,121 @@
+( function ( mw ) {
+       QUnit.module( 'mediawiki.requestIdleCallback', QUnit.newMwEnvironment( {
+               setup: function () {
+                       var time = mw.now(),
+                               clock = this.clock = this.sandbox.useFakeTimers();
+
+                       this.tick = function ( forward ) {
+                               time += forward;
+                               clock.tick( forward );
+                       };
+                       this.sandbox.stub( mw, 'now', function () {
+                               return time;
+                       } );
+
+                       // Don't test the native version (if available)
+                       this.mwRIC = mw.requestIdleCallback;
+                       mw.requestIdleCallback = mw.requestIdleCallbackInternal;
+               },
+               teardown: function () {
+                       mw.requestIdleCallback = this.mwRIC;
+               }
+       } ) );
+
+       // Basic scheduling of callbacks
+       QUnit.test( 'callback', 3, function ( assert ) {
+               var sequence,
+                       tick = this.tick;
+
+               mw.requestIdleCallback( function () {
+                       sequence.push( 'x' );
+                       tick( 30 );
+               } );
+               mw.requestIdleCallback( function () {
+                       tick( 5 );
+                       sequence.push( 'y' );
+                       tick( 30 );
+               } );
+               // Task Z is not run in the first sequence because the
+               // first two tasks consumed the available 50ms budget.
+               mw.requestIdleCallback( function () {
+                       sequence.push( 'z' );
+                       tick( 30 );
+               } );
+
+               sequence = [];
+               tick( 1000 );
+               assert.deepEqual( sequence, [ 'x', 'y' ] );
+
+               sequence = [];
+               tick( 1000 );
+               assert.deepEqual( sequence, [ 'z' ] );
+
+               sequence = [];
+               tick( 1000 );
+               assert.deepEqual( sequence, [] );
+       } );
+
+       // Schedule new callbacks within a callback that tick
+       // the clock. If the budget is exceeded, the newly scheduled
+       // task is delayed until the next idle period.
+       QUnit.test( 'nest-tick', 3, function ( assert ) {
+               var sequence,
+                       tick = this.tick;
+
+               mw.requestIdleCallback( function () {
+                       sequence.push( 'x' );
+                       tick( 30 );
+               } );
+               // Task Y is a task that schedules another task.
+               mw.requestIdleCallback( function () {
+                       function other() {
+                               sequence.push( 'y' );
+                               tick( 35 );
+                       }
+                       mw.requestIdleCallback( other );
+               } );
+               mw.requestIdleCallback( function () {
+                       sequence.push( 'z' );
+                       tick( 30 );
+               } );
+
+               sequence = [];
+               tick( 1000 );
+               assert.deepEqual( sequence, [ 'x', 'z' ] );
+
+               sequence = [];
+               tick( 1000 );
+               assert.deepEqual( sequence, [ 'y' ] );
+
+               sequence = [];
+               tick( 1000 );
+               assert.deepEqual( sequence, [] );
+       } );
+
+       // Schedule new callbacks within a callback that run quickly.
+       // Note how the newly scheduled task gets to run as part of the
+       // current idle period (budget allowing).
+       QUnit.test( 'nest-quick', 2, function ( assert ) {
+               var sequence,
+                       tick = this.tick;
+
+               mw.requestIdleCallback( function () {
+                       sequence.push( 'x' );
+                       mw.requestIdleCallback( function () {
+                               sequence.push( 'x-expand' );
+                       } );
+               } );
+               mw.requestIdleCallback( function () {
+                       sequence.push( 'y' );
+               } );
+
+               sequence = [];
+               tick( 1000 );
+               assert.deepEqual( sequence, [ 'x', 'y', 'x-expand' ] );
+
+               sequence = [];
+               tick( 1000 );
+               assert.deepEqual( sequence, [] );
+       } );
+
+}( mediaWiki ) );