Merge "New parameters 'reverse' and 'sortbyvalue' for Special:PagesWithProp"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Thu, 27 Jul 2017 16:25:01 +0000 (16:25 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 27 Jul 2017 16:25:01 +0000 (16:25 +0000)
238 files changed:
RELEASE-NOTES-1.30
autoload.php
composer.json
includes/Block.php
includes/DefaultSettings.php
includes/EditPage.php
includes/Feed.php
includes/FileDeleteForm.php
includes/HistoryBlob.php
includes/LinkFilter.php
includes/Linker.php
includes/MWNamespace.php
includes/MagicWord.php
includes/MediaWiki.php
includes/MimeMagic.php
includes/Preferences.php
includes/Revision.php
includes/Sanitizer.php
includes/ServiceWiring.php
includes/Setup.php
includes/SiteStats.php
includes/Title.php
includes/WikiMap.php
includes/Xml.php
includes/actions/Action.php
includes/api/ApiBase.php
includes/api/ApiEmailUser.php
includes/api/ApiMain.php
includes/api/ApiPageSet.php
includes/api/ApiQueryImageInfo.php
includes/api/ApiQueryInfo.php
includes/api/ApiResult.php
includes/api/ApiUsageException.php
includes/api/i18n/he.json
includes/api/i18n/ko.json
includes/api/i18n/zh-hant.json
includes/auth/AuthenticationResponse.php
includes/cache/MessageCache.php
includes/changes/ChangesFeed.php
includes/changes/EnhancedChangesList.php
includes/changes/RecentChange.php
includes/changetags/ChangeTags.php
includes/collation/IcuCollation.php
includes/collation/NumericUppercaseCollation.php
includes/config/EtcdConfig.php
includes/content/ContentHandler.php
includes/context/DerivativeContext.php
includes/db/CloneDatabase.php
includes/debug/MWDebug.php
includes/deferred/LinksUpdate.php
includes/deferred/SearchUpdate.php
includes/diff/DifferenceEngine.php
includes/exception/HttpError.php
includes/export/WikiExporter.php
includes/filerepo/ForeignAPIRepo.php
includes/htmlform/HTMLForm.php
includes/http/Http.php
includes/import/ImportStreamSource.php
includes/installer/DatabaseUpdater.php
includes/installer/WebInstaller.php
includes/installer/i18n/csb.json
includes/installer/i18n/ms.json
includes/jobqueue/JobQueue.php
includes/jobqueue/JobQueueFederated.php
includes/jobqueue/JobQueueSecondTestQueue.php [new file with mode: 0644]
includes/libs/CSSMin.php
includes/libs/IP.php
includes/libs/StringUtils.php
includes/libs/filebackend/FileBackendStore.php
includes/libs/mime/MimeAnalyzer.php
includes/libs/objectcache/APCBagOStuff.php
includes/libs/objectcache/APCUBagOStuff.php
includes/libs/objectcache/MemcachedPeclBagOStuff.php
includes/libs/objectcache/MultiWriteBagOStuff.php
includes/libs/rdbms/database/Database.php
includes/logging/BlockLogFormatter.php
includes/logging/LogPage.php
includes/logging/LogPager.php
includes/mail/UserMailer.php
includes/media/DjVuImage.php
includes/media/Exif.php
includes/media/SVGMetadataExtractor.php
includes/page/Article.php
includes/page/WikiPage.php
includes/pager/IndexPager.php
includes/parser/Parser.php
includes/parser/ParserOptions.php
includes/parser/ParserOutput.php
includes/profiler/output/ProfilerOutput.php
includes/resourceloader/ResourceLoader.php
includes/resourceloader/ResourceLoaderClientHtml.php
includes/resourceloader/ResourceLoaderFileModule.php
includes/resourceloader/ResourceLoaderModule.php
includes/search/SearchDatabase.php
includes/skins/Skin.php
includes/specialpage/LoginSignupSpecialPage.php
includes/specialpage/SpecialPage.php
includes/specials/SpecialActiveusers.php
includes/specials/SpecialAllMessages.php
includes/specials/SpecialAllPages.php
includes/specials/SpecialImport.php
includes/specials/SpecialListusers.php
includes/specials/SpecialRecentchanges.php
includes/specials/SpecialRecentchangeslinked.php
includes/specials/SpecialSpecialpages.php
includes/specials/SpecialUndelete.php
includes/specials/SpecialUpload.php
includes/specials/SpecialWatchlist.php
includes/specials/pagers/UsersPager.php
includes/tidy/Balancer.php
includes/tidy/RemexCompatMunger.php
includes/user/BotPassword.php
includes/user/User.php
languages/ConverterRule.php
languages/Language.php
languages/data/Names.php
languages/i18n/ast.json
languages/i18n/ba.json
languages/i18n/be-tarask.json
languages/i18n/be.json
languages/i18n/bg.json
languages/i18n/bho.json
languages/i18n/bn.json
languages/i18n/bs.json
languages/i18n/ckb.json
languages/i18n/cs.json
languages/i18n/csb.json
languages/i18n/cy.json
languages/i18n/da.json
languages/i18n/de.json
languages/i18n/din.json
languages/i18n/en.json
languages/i18n/es.json
languages/i18n/et.json
languages/i18n/eu.json
languages/i18n/fr.json
languages/i18n/frr.json
languages/i18n/gl.json
languages/i18n/gor.json
languages/i18n/gsw.json
languages/i18n/gu.json
languages/i18n/he.json
languages/i18n/hi.json
languages/i18n/hr.json
languages/i18n/hu.json
languages/i18n/hy.json
languages/i18n/inh.json
languages/i18n/it.json
languages/i18n/jv.json
languages/i18n/ko.json
languages/i18n/lb.json
languages/i18n/li.json
languages/i18n/mk.json
languages/i18n/ms.json
languages/i18n/nap.json
languages/i18n/nb.json
languages/i18n/nl.json
languages/i18n/nn.json
languages/i18n/pl.json
languages/i18n/pnb.json
languages/i18n/pt-br.json
languages/i18n/pt.json
languages/i18n/qqq.json
languages/i18n/rif.json
languages/i18n/rm.json
languages/i18n/ro.json
languages/i18n/roa-tara.json
languages/i18n/ru.json
languages/i18n/sd.json
languages/i18n/shi.json
languages/i18n/skr-arab.json [new file with mode: 0644]
languages/i18n/sl.json
languages/i18n/sr-ec.json
languages/i18n/sr-el.json
languages/i18n/sv.json
languages/i18n/te.json
languages/i18n/tet.json
languages/i18n/tg-cyrl.json
languages/i18n/uk.json
languages/i18n/vi.json
languages/i18n/yi.json
languages/i18n/yue.json
languages/i18n/zh-hans.json
languages/i18n/zh-hant.json
languages/messages/MessagesSkr.php [new file with mode: 0644]
languages/messages/MessagesSkr_arab.php [new file with mode: 0644]
maintenance/Maintenance.php
maintenance/addRFCandPMIDInterwiki.php
maintenance/generateSitemap.php
maintenance/language/checkLanguage.inc
maintenance/populatePPSortKey.php [new file with mode: 0644]
maintenance/sqlite.inc
maintenance/userOptions.inc
phpcs.xml
resources/Resources.php
resources/lib/qunitjs/qunit.css
resources/lib/qunitjs/qunit.js
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.ItemModel.js
resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
resources/src/mediawiki.rcfilters/mw.rcfilters.init.js
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.less
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.LiveUpdateButtonWidget.less
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesListWrapperWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FormWrapperWidget.js
resources/src/mediawiki/mediawiki.hlist-allskins.less [new file with mode: 0644]
resources/src/mediawiki/mediawiki.hlist.css
tests/common/TestsAutoLoader.php
tests/parser/ParserTestRunner.php
tests/parser/parserTests.txt
tests/phpunit/MediaWikiTestCase.php
tests/phpunit/ResourceLoaderTestCase.php
tests/phpunit/includes/DeprecatedGlobalTest.php
tests/phpunit/includes/GitInfoTest.php
tests/phpunit/includes/PreferencesTest.php
tests/phpunit/includes/SanitizerTest.php
tests/phpunit/includes/SiteStatsTest.php [new file with mode: 0644]
tests/phpunit/includes/api/ApiErrorFormatterTest.php
tests/phpunit/includes/api/ApiMainTest.php
tests/phpunit/includes/changetags/ChangeTagsTest.php [new file with mode: 0644]
tests/phpunit/includes/config/ConfigFactoryTest.php
tests/phpunit/includes/config/EtcdConfigTest.php
tests/phpunit/includes/content/WikitextContentTest.php
tests/phpunit/includes/libs/CSSMinTest.php
tests/phpunit/includes/libs/objectcache/MultiWriteBagOStuffTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseDomainTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseSQLTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseTest.php
tests/phpunit/includes/page/WikiPageTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderClientHtmlTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderFileModuleTest.php
tests/phpunit/includes/site/TestSites.php
tests/phpunit/includes/specials/SpecialRecentchangesTest.php
tests/phpunit/suite.xml
tests/qunit/data/testrunner.js
tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js
tests/qunit/suites/resources/mediawiki.rcfilters/dm.FiltersViewModel.test.js

index 51f9764..c5ab81a 100644 (file)
@@ -46,6 +46,7 @@ section).
 === Languages updated in 1.30 ===
 
 * Support for kbp (Kabɩyɛ / Kabiyè) was added.
+* Support for skr (Saraiki, سرائیکی) was added.
 
 === External library changes in 1.30 ===
 
index a6128a4..510eeee 100644 (file)
@@ -679,6 +679,7 @@ $wgAutoloadLocalClasses = [
        'JobQueueMemory' => __DIR__ . '/includes/jobqueue/JobQueueMemory.php',
        'JobQueueReadOnlyError' => __DIR__ . '/includes/jobqueue/JobQueue.php',
        'JobQueueRedis' => __DIR__ . '/includes/jobqueue/JobQueueRedis.php',
+       'JobQueueSecondTestQueue' => __DIR__ . '/includes/jobqueue/JobQueueSecondTestQueue.php',
        'JobRunner' => __DIR__ . '/includes/jobqueue/JobRunner.php',
        'JobSpecification' => __DIR__ . '/includes/jobqueue/JobSpecification.php',
        'JpegHandler' => __DIR__ . '/includes/media/Jpeg.php',
@@ -1118,6 +1119,7 @@ $wgAutoloadLocalClasses = [
        'PopulateInterwiki' => __DIR__ . '/maintenance/populateInterwiki.php',
        'PopulateLogSearch' => __DIR__ . '/maintenance/populateLogSearch.php',
        'PopulateLogUsertext' => __DIR__ . '/maintenance/populateLogUsertext.php',
+       'PopulatePPSortKey' => __DIR__ . '/maintenance/populatePPSortKey.php',
        'PopulateParentId' => __DIR__ . '/maintenance/populateParentId.php',
        'PopulateRecentChangesSource' => __DIR__ . '/maintenance/populateRecentChangesSource.php',
        'PopulateRevisionLength' => __DIR__ . '/maintenance/populateRevisionLength.php',
index 83fcda0..d04e9f5 100644 (file)
@@ -53,7 +53,7 @@
                "jakub-onderka/php-parallel-lint": "0.9.2",
                "jetbrains/phpstorm-stubs": "dev-master#1b9906084d6635456fcf3f3a01f0d7d5b99a578a",
                "justinrainbow/json-schema": "~5.2",
-               "mediawiki/mediawiki-codesniffer": "0.8.1",
+               "mediawiki/mediawiki-codesniffer": "0.10.1",
                "monolog/monolog": "~1.22.1",
                "nikic/php-parser": "2.1.0",
                "nmred/kafka-php": "0.1.5",
index 2c935df..2a04879 100644 (file)
@@ -485,7 +485,7 @@ class Block {
 
                # Periodic purge via commit hooks
                if ( mt_rand( 0, 9 ) == 0 ) {
-                       Block::purgeExpired();
+                       self::purgeExpired();
                }
 
                $row = $this->getDatabaseArray();
@@ -778,12 +778,12 @@ class Block {
                # It's okay to autoblock. Go ahead and insert/update the block...
 
                # Do not add a *new* block if the IP is already blocked.
-               $ipblock = Block::newFromTarget( $autoblockIP );
+               $ipblock = self::newFromTarget( $autoblockIP );
                if ( $ipblock ) {
                        # Check if the block is an autoblock and would exceed the user block
                        # if renewed. If so, do nothing, otherwise prolong the block time...
                        if ( $ipblock->mAuto && // @todo Why not compare $ipblock->mExpiry?
-                               $this->mExpiry > Block::getAutoblockExpiry( $ipblock->mTimestamp )
+                               $this->mExpiry > self::getAutoblockExpiry( $ipblock->mTimestamp )
                        ) {
                                # Reset block timestamp to now and its expiry to
                                # $wgAutoblockExpiry in the future
@@ -810,11 +810,11 @@ class Block {
 
                if ( $this->mExpiry == 'infinity' ) {
                        # Original block was indefinite, start an autoblock now
-                       $autoblock->mExpiry = Block::getAutoblockExpiry( $timestamp );
+                       $autoblock->mExpiry = self::getAutoblockExpiry( $timestamp );
                } else {
                        # If the user is already blocked with an expiry date, we don't
                        # want to pile on top of that.
-                       $autoblock->mExpiry = min( $this->mExpiry, Block::getAutoblockExpiry( $timestamp ) );
+                       $autoblock->mExpiry = min( $this->mExpiry, self::getAutoblockExpiry( $timestamp ) );
                }
 
                # Insert the block...
@@ -870,7 +870,7 @@ class Block {
        public function updateTimestamp() {
                if ( $this->mAuto ) {
                        $this->mTimestamp = wfTimestamp();
-                       $this->mExpiry = Block::getAutoblockExpiry( $this->mTimestamp );
+                       $this->mExpiry = self::getAutoblockExpiry( $this->mTimestamp );
 
                        $dbw = wfGetDB( DB_MASTER );
                        $dbw->update( 'ipblocks',
@@ -1111,8 +1111,8 @@ class Block {
         */
        public static function newFromTarget( $specificTarget, $vagueTarget = null, $fromMaster = false ) {
                list( $target, $type ) = self::parseTarget( $specificTarget );
-               if ( $type == Block::TYPE_ID || $type == Block::TYPE_AUTO ) {
-                       return Block::newFromID( $target );
+               if ( $type == self::TYPE_ID || $type == self::TYPE_AUTO ) {
+                       return self::newFromID( $target );
 
                } elseif ( $target === null && $vagueTarget == '' ) {
                        # We're not going to find anything useful here
@@ -1122,7 +1122,7 @@ class Block {
 
                } elseif ( in_array(
                        $type,
-                       [ Block::TYPE_USER, Block::TYPE_IP, Block::TYPE_RANGE, null ] )
+                       [ self::TYPE_USER, self::TYPE_IP, self::TYPE_RANGE, null ] )
                ) {
                        $block = new Block();
                        $block->fromMaster( $fromMaster );
@@ -1189,7 +1189,7 @@ class Block {
                }
                $selectFields = array_merge(
                        [ 'ipb_range_start', 'ipb_range_end' ],
-                       Block::selectFields()
+                       self::selectFields()
                );
                $rows = $db->select( 'ipblocks',
                        $selectFields,
@@ -1350,12 +1350,12 @@ class Block {
                        # off validation checking (which would exclude IP addresses)
                        return [
                                User::newFromName( IP::sanitizeIP( $target ), false ),
-                               Block::TYPE_IP
+                               self::TYPE_IP
                        ];
 
                } elseif ( IP::isValidBlock( $target ) ) {
                        # Can't create a User from an IP range
-                       return [ IP::sanitizeRange( $target ), Block::TYPE_RANGE ];
+                       return [ IP::sanitizeRange( $target ), self::TYPE_RANGE ];
                }
 
                # Consider the possibility that this is not a username at all
@@ -1370,11 +1370,11 @@ class Block {
                        # Note that since numbers are valid usernames, a $target of "12345" will be
                        # considered a User.  If you want to pass a block ID, prepend a hash "#12345",
                        # since hash characters are not valid in usernames or titles generally.
-                       return [ $userObj, Block::TYPE_USER ];
+                       return [ $userObj, self::TYPE_USER ];
 
                } elseif ( preg_match( '/^#\d+$/', $target ) ) {
                        # Autoblock reference in the form "#12345"
-                       return [ substr( $target, 1 ), Block::TYPE_AUTO ];
+                       return [ substr( $target, 1 ), self::TYPE_AUTO ];
 
                } else {
                        # WTF?
index f35715e..74d5fa4 100644 (file)
@@ -1304,7 +1304,7 @@ $wgMimeInfoFile = 'includes/mime.info';
  * Sets an external MIME detector program. The command must print only
  * the MIME type to standard output.
  * The name of the file to process will be appended to the command given here.
- * If not set or NULL, PHP's fileinfo extension will be used if available.
+ * If not set or NULL, PHP's mime_content_type function will be used.
  *
  * @par Example:
  * @code
index 973327b..229a36a 100644 (file)
@@ -2783,7 +2783,7 @@ class EditPage {
                $wgOut->addHTML( $this->editFormTextBeforeContent );
 
                if ( !$this->isCssJsSubpage && $showToolbar && $wgUser->getOption( 'showtoolbar' ) ) {
-                       $wgOut->addHTML( EditPage::getEditToolbar( $this->mTitle ) );
+                       $wgOut->addHTML( self::getEditToolbar( $this->mTitle ) );
                }
 
                if ( $this->blankArticle ) {
@@ -3492,6 +3492,10 @@ HTML
                }
        }
 
+       /**
+        * Inserts optional text shown below edit and upload forms. Can be used to offer special characters not present on
+        * most keyboards for copying/pasting.
+        */
        protected function showEditTools() {
                global $wgOut;
                $wgOut->addHTML( '<div class="mw-editTools">' .
index 189fd9f..f76a634 100644 (file)
@@ -54,8 +54,6 @@ class FeedItem {
        public $rssIsPermalink = false;
 
        /**
-        * Constructor
-        *
         * @param string|Title $title Item's title
         * @param string $description
         * @param string $url URL uniquely designating the item.
index e7b4a1f..8a1cd35 100644 (file)
@@ -47,8 +47,6 @@ class FileDeleteForm {
        private $oldimage = '';
 
        /**
-        * Constructor
-        *
         * @param File $file File object we're deleting
         */
        public function __construct( $file ) {
index 56cf815..51bd7a9 100644 (file)
@@ -76,9 +76,6 @@ class ConcatenatedGzipHistoryBlob implements HistoryBlob {
        public $mMaxSize = 10000000;
        public $mMaxCount = 100;
 
-       /**
-        * Constructor
-        */
        public function __construct() {
                if ( !function_exists( 'gzdeflate' ) ) {
                        throw new MWException( "Need zlib support to read or write this "
index 2f50558..790e2be 100644 (file)
@@ -50,7 +50,7 @@ class LinkFilter {
 
                $text = $content->getNativeData();
 
-               $regex = LinkFilter::makeRegex( $filterEntry );
+               $regex = self::makeRegex( $filterEntry );
                return preg_match( $regex, $text );
        }
 
index f2e4ac4..4aae3ba 100644 (file)
@@ -1328,7 +1328,7 @@ class Linker {
                Title $title, $text, $wikiId = null, $options = []
        ) {
                if ( $wikiId !== null && !$title->isExternal() ) {
-                       $link = Linker::makeExternalLink(
+                       $link = self::makeExternalLink(
                                WikiMap::getForeignURL(
                                        $wikiId,
                                        $title->getNamespace() === 0
@@ -1341,7 +1341,7 @@ class Linker {
                                /* escape = */ false // Already escaped
                        );
                } else {
-                       $link = Linker::link( $title, $text, [], [], $options );
+                       $link = self::link( $title, $text, [], [], $options );
                }
 
                return $link;
@@ -2021,7 +2021,7 @@ class Linker {
                }
 
                if ( !$rev->userCan( Revision::DELETED_RESTRICTED, $user ) ) {
-                       return Linker::revDeleteLinkDisabled( $canHide ); // revision was hidden from sysops
+                       return self::revDeleteLinkDisabled( $canHide ); // revision was hidden from sysops
                } else {
                        if ( $rev->getId() ) {
                                // RevDelete links using revision ID are stable across
@@ -2040,7 +2040,7 @@ class Linker {
                                        'ids' => $rev->getTimestamp()
                                ];
                        }
-                       return Linker::revDeleteLink( $query,
+                       return self::revDeleteLink( $query,
                                $rev->isDeleted( Revision::DELETED_RESTRICTED ), $canHide );
                }
        }
index 89cb616..97dba26 100644 (file)
@@ -370,7 +370,7 @@ class MWNamespace {
         */
        public static function getSubjectNamespaces() {
                return array_filter(
-                       MWNamespace::getValidNamespaces(),
+                       self::getValidNamespaces(),
                        'MWNamespace::isSubject'
                );
        }
@@ -383,7 +383,7 @@ class MWNamespace {
         */
        public static function getTalkNamespaces() {
                return array_filter(
-                       MWNamespace::getValidNamespaces(),
+                       self::getValidNamespaces(),
                        'MWNamespace::isTalk'
                );
        }
index ee95918..1703179 100644 (file)
@@ -664,7 +664,7 @@ class MagicWord {
                $search = [];
                $replace = [];
                foreach ( $magicarr as $id => $replacement ) {
-                       $mw = MagicWord::get( $id );
+                       $mw = self::get( $id );
                        $search[] = $mw->getRegex();
                        $replace[] = $replacement;
                }
index 4df4d76..4e47184 100644 (file)
@@ -609,6 +609,7 @@ class MediaWiki {
                        $lbFactory->hasOrMadeRecentMasterChanges( INF )
                ) ? self::getUrlDomainDistance( $output->getRedirect(), $context ) : false;
 
+               $allowHeaders = !( $output->isDisabled() || headers_sent() );
                if ( $urlDomainDistance === 'local' || $urlDomainDistance === 'remote' ) {
                        // OutputPage::output() will be fast; $postCommitWork will not be useful for
                        // masking the latency of syncing DB positions accross all datacenters synchronously.
@@ -616,7 +617,7 @@ class MediaWiki {
                        $flags = $lbFactory::SHUTDOWN_CHRONPROT_ASYNC;
                        $cpPosTime = microtime( true );
                        // Client's next request should see 1+ positions with this DBMasterPos::asOf() time
-                       if ( $urlDomainDistance === 'local' ) {
+                       if ( $urlDomainDistance === 'local' && $allowHeaders ) {
                                // Client will stay on this domain, so set an unobtrusive cookie
                                $expires = time() + ChronologyProtector::POSITION_TTL;
                                $options = [ 'prefix' => '' ];
@@ -633,7 +634,7 @@ class MediaWiki {
                        // OutputPage::output() is fairly slow; run it in $postCommitWork to mask
                        // the latency of syncing DB positions accross all datacenters synchronously
                        $flags = $lbFactory::SHUTDOWN_CHRONPROT_SYNC;
-                       if ( $lbFactory->hasOrMadeRecentMasterChanges( INF ) ) {
+                       if ( $lbFactory->hasOrMadeRecentMasterChanges( INF ) && $allowHeaders ) {
                                $cpPosTime = microtime( true );
                                // Set a cookie in case the DB position store cannot sync accross datacenters.
                                // This will at least cover the common case of the user staying on the domain.
index 8670729..a2a44bb 100644 (file)
@@ -35,7 +35,7 @@ class MimeMagic extends MimeAnalyzer {
                $instance = MediaWikiServices::getInstance()->getMimeAnalyzer();
                Assert::postcondition(
                        $instance instanceof MimeMagic,
-                       __METHOD__ . ' should return an instance of ' . MimeMagic::class
+                       __METHOD__ . ' should return an instance of ' . self::class
                );
                return $instance;
        }
index 008963b..7efbef1 100644 (file)
@@ -1316,7 +1316,7 @@ class Preferences {
                $formClass = 'PreferencesForm',
                array $remove = []
        ) {
-               $formDescriptor = Preferences::getPreferences( $user, $context );
+               $formDescriptor = self::getPreferences( $user, $context );
                if ( count( $remove ) ) {
                        $removeKeys = array_flip( $remove );
                        $formDescriptor = array_diff_key( $formDescriptor, $removeKeys );
index c3782ba..537b7c1 100644 (file)
@@ -559,8 +559,6 @@ class Revision implements IDBAccessObject {
        }
 
        /**
-        * Constructor
-        *
         * @param object|array $row Either a database row or an array
         * @throws MWException
         * @access private
@@ -1004,7 +1002,7 @@ class Revision implements IDBAccessObject {
 
                return RecentChange::newFromConds(
                        [
-                               'rc_user_text' => $this->getUserText( Revision::RAW ),
+                               'rc_user_text' => $this->getUserText( self::RAW ),
                                'rc_timestamp' => $dbr->timestamp( $this->getTimestamp() ),
                                'rc_this_oldid' => $this->getId()
                        ],
@@ -1468,7 +1466,7 @@ class Revision implements IDBAccessObject {
                                ? $this->getPreviousRevisionId( $dbw )
                                : $this->mParentId,
                        'rev_sha1'       => $this->mSha1 === null
-                               ? Revision::base36Sha1( $this->mText )
+                               ? self::base36Sha1( $this->mText )
                                : $this->mSha1,
                ];
 
@@ -1557,7 +1555,7 @@ class Revision implements IDBAccessObject {
                        }
                }
 
-               $content = $this->getContent( Revision::RAW );
+               $content = $this->getContent( self::RAW );
                $prefixedDBkey = $title->getPrefixedDBkey();
                $revId = $this->mId;
 
index b08bc69..2def06a 100644 (file)
@@ -465,7 +465,7 @@ class Sanitizer {
                extract( self::getRecognizedTagData( $extratags, $removetags ) );
 
                # Remove HTML comments
-               $text = Sanitizer::removeHTMLcomments( $text );
+               $text = self::removeHTMLcomments( $text );
                $bits = explode( '<', $text );
                $text = str_replace( '>', '&gt;', array_shift( $bits ) );
                if ( !MWTidy::isEnabled() ) {
@@ -583,12 +583,12 @@ class Sanitizer {
                                                        call_user_func_array( $processCallback, [ &$params, $args ] );
                                                }
 
-                                               if ( !Sanitizer::validateTag( $params, $t ) ) {
+                                               if ( !self::validateTag( $params, $t ) ) {
                                                        $badtag = true;
                                                }
 
                                                # Strip non-approved attributes from the tag
-                                               $newparams = Sanitizer::fixTagAttributes( $params, $t );
+                                               $newparams = self::fixTagAttributes( $params, $t );
                                        }
                                        if ( !$badtag ) {
                                                $rest = str_replace( '>', '&gt;', $rest );
@@ -629,11 +629,11 @@ class Sanitizer {
                                                                call_user_func_array( $warnCallback, [ 'deprecated-self-close-category' ] );
                                                        }
                                                }
-                                               if ( !Sanitizer::validateTag( $params, $t ) ) {
+                                               if ( !self::validateTag( $params, $t ) ) {
                                                        $badtag = true;
                                                }
 
-                                               $newparams = Sanitizer::fixTagAttributes( $params, $t );
+                                               $newparams = self::fixTagAttributes( $params, $t );
                                                if ( !$badtag ) {
                                                        if ( $brace === '/>' && !isset( $htmlsingleonly[$t] ) ) {
                                                                # Interpret self-closing tags as empty tags even when
@@ -710,7 +710,7 @@ class Sanitizer {
         * @return bool
         */
        static function validateTag( $params, $element ) {
-               $params = Sanitizer::decodeTagAttributes( $params );
+               $params = self::decodeTagAttributes( $params );
 
                if ( $element == 'meta' || $element == 'link' ) {
                        if ( !isset( $params['itemprop'] ) ) {
@@ -746,8 +746,8 @@ class Sanitizer {
         * @todo Check for unique id attribute :P
         */
        static function validateTagAttributes( $attribs, $element ) {
-               return Sanitizer::validateAttributes( $attribs,
-                       Sanitizer::attributeWhitelist( $element ) );
+               return self::validateAttributes( $attribs,
+                       self::attributeWhitelist( $element ) );
        }
 
        /**
@@ -795,12 +795,12 @@ class Sanitizer {
                        # Strip javascript "expression" from stylesheets.
                        # https://msdn.microsoft.com/en-us/library/ms537634.aspx
                        if ( $attribute == 'style' ) {
-                               $value = Sanitizer::checkCss( $value );
+                               $value = self::checkCss( $value );
                        }
 
                        # Escape HTML id attributes
                        if ( $attribute === 'id' ) {
-                               $value = Sanitizer::escapeId( $value, 'noninitial' );
+                               $value = self::escapeId( $value, 'noninitial' );
                        }
 
                        # Escape HTML id reference lists
@@ -809,7 +809,7 @@ class Sanitizer {
                                || $attribute === 'aria-labelledby'
                                || $attribute === 'aria-owns'
                        ) {
-                               $value = Sanitizer::escapeIdReferenceList( $value, 'noninitial' );
+                               $value = self::escapeIdReferenceList( $value, 'noninitial' );
                        }
 
                        // RDFa and microdata properties allow URLs, URIs and/or CURIs.
@@ -907,7 +907,7 @@ class Sanitizer {
         */
        public static function normalizeCss( $value ) {
                // Decode character references like &#123;
-               $value = Sanitizer::decodeCharReferences( $value );
+               $value = self::decodeCharReferences( $value );
 
                // Decode escape sequences and line continuation
                // See the grammar in the CSS 2 spec, appendix D.
@@ -1087,14 +1087,14 @@ class Sanitizer {
                        return '';
                }
 
-               $decoded = Sanitizer::decodeTagAttributes( $text );
-               $stripped = Sanitizer::validateTagAttributes( $decoded, $element );
+               $decoded = self::decodeTagAttributes( $text );
+               $stripped = self::validateTagAttributes( $decoded, $element );
 
                if ( $sorted ) {
                        ksort( $stripped );
                }
 
-               return Sanitizer::safeEncodeTagAttributes( $stripped );
+               return self::safeEncodeTagAttributes( $stripped );
        }
 
        /**
@@ -1124,7 +1124,7 @@ class Sanitizer {
         * @return string HTML-encoded text fragment
         */
        static function safeEncodeAttribute( $text ) {
-               $encValue = Sanitizer::encodeAttribute( $text );
+               $encValue = self::encodeAttribute( $text );
 
                # Templates and links may be expanded in later parsing,
                # creating invalid or dangerous output. Suppress this.
@@ -1186,7 +1186,7 @@ class Sanitizer {
                global $wgExperimentalHtmlIds;
                $options = (array)$options;
 
-               $id = Sanitizer::decodeCharReferences( $id );
+               $id = self::decodeCharReferences( $id );
 
                if ( $wgExperimentalHtmlIds && !in_array( 'legacy', $options ) ) {
                        $id = preg_replace( '/[ \t\n\r\f_\'"&#%]+/', '_', $id );
@@ -1238,7 +1238,7 @@ class Sanitizer {
 
                # Escape each token as an id
                foreach ( $references as &$ref ) {
-                       $ref = Sanitizer::escapeId( $ref, $options );
+                       $ref = self::escapeId( $ref, $options );
                }
 
                # Merge the array back to a space delimited list string
@@ -1275,7 +1275,7 @@ class Sanitizer {
         * @return string Escaped input
         */
        static function escapeHtmlAllowEntities( $html ) {
-               $html = Sanitizer::decodeCharReferences( $html );
+               $html = self::decodeCharReferences( $html );
                # It seems wise to escape ' as well as ", as a matter of course.  Can't
                # hurt. Use ENT_SUBSTITUTE so that incorrectly truncated multibyte characters
                # don't cause the entire string to disappear.
@@ -1317,14 +1317,14 @@ class Sanitizer {
 
                foreach ( $pairs as $set ) {
                        $attribute = strtolower( $set[1] );
-                       $value = Sanitizer::getTagAttributeCallback( $set );
+                       $value = self::getTagAttributeCallback( $set );
 
                        // Normalize whitespace
                        $value = preg_replace( '/[\t\r\n ]+/', ' ', $value );
                        $value = trim( $value );
 
                        // Decode character references
-                       $attribs[$attribute] = Sanitizer::decodeCharReferences( $value );
+                       $attribs[$attribute] = self::decodeCharReferences( $value );
                }
                return $attribs;
        }
@@ -1340,7 +1340,7 @@ class Sanitizer {
                $attribs = [];
                foreach ( $assoc_array as $attribute => $value ) {
                        $encAttribute = htmlspecialchars( $attribute );
-                       $encValue = Sanitizer::safeEncodeAttribute( $value );
+                       $encValue = self::safeEncodeAttribute( $value );
 
                        $attribs[] = "$encAttribute=\"$encValue\"";
                }
@@ -1427,11 +1427,11 @@ class Sanitizer {
        static function normalizeCharReferencesCallback( $matches ) {
                $ret = null;
                if ( $matches[1] != '' ) {
-                       $ret = Sanitizer::normalizeEntity( $matches[1] );
+                       $ret = self::normalizeEntity( $matches[1] );
                } elseif ( $matches[2] != '' ) {
-                       $ret = Sanitizer::decCharReference( $matches[2] );
+                       $ret = self::decCharReference( $matches[2] );
                } elseif ( $matches[3] != '' ) {
-                       $ret = Sanitizer::hexCharReference( $matches[3] );
+                       $ret = self::hexCharReference( $matches[3] );
                }
                if ( is_null( $ret ) ) {
                        return htmlspecialchars( $matches[0] );
@@ -1468,7 +1468,7 @@ class Sanitizer {
         */
        static function decCharReference( $codepoint ) {
                $point = intval( $codepoint );
-               if ( Sanitizer::validateCodepoint( $point ) ) {
+               if ( self::validateCodepoint( $point ) ) {
                        return sprintf( '&#%d;', $point );
                } else {
                        return null;
@@ -1481,7 +1481,7 @@ class Sanitizer {
         */
        static function hexCharReference( $codepoint ) {
                $point = hexdec( $codepoint );
-               if ( Sanitizer::validateCodepoint( $point ) ) {
+               if ( self::validateCodepoint( $point ) ) {
                        return sprintf( '&#x%x;', $point );
                } else {
                        return null;
@@ -1550,11 +1550,11 @@ class Sanitizer {
         */
        static function decodeCharReferencesCallback( $matches ) {
                if ( $matches[1] != '' ) {
-                       return Sanitizer::decodeEntity( $matches[1] );
+                       return self::decodeEntity( $matches[1] );
                } elseif ( $matches[2] != '' ) {
-                       return Sanitizer::decodeChar( intval( $matches[2] ) );
+                       return self::decodeChar( intval( $matches[2] ) );
                } elseif ( $matches[3] != '' ) {
-                       return Sanitizer::decodeChar( hexdec( $matches[3] ) );
+                       return self::decodeChar( hexdec( $matches[3] ) );
                }
                # Last case should be an ampersand by itself
                return $matches[0];
@@ -1568,7 +1568,7 @@ class Sanitizer {
         * @private
         */
        static function decodeChar( $codepoint ) {
-               if ( Sanitizer::validateCodepoint( $codepoint ) ) {
+               if ( self::validateCodepoint( $codepoint ) ) {
                        return UtfNormal\Utils::codepointToUtf8( $codepoint );
                } else {
                        return UtfNormal\Constants::UTF8_REPLACEMENT;
@@ -1601,7 +1601,7 @@ class Sanitizer {
         * @return array
         */
        static function attributeWhitelist( $element ) {
-               $list = Sanitizer::setupAttributeWhitelist();
+               $list = self::setupAttributeWhitelist();
                return isset( $list[$element] )
                        ? $list[$element]
                        : [];
@@ -1876,7 +1876,7 @@ class Sanitizer {
        static function cleanUrl( $url ) {
                # Normalize any HTML entities in input. They will be
                # re-escaped by makeExternalLink().
-               $url = Sanitizer::decodeCharReferences( $url );
+               $url = self::decodeCharReferences( $url );
 
                # Escape any control characters introduced by the above step
                $url = preg_replace_callback( '/[\][<>"\\x00-\\x20\\x7F\|]/',
index e1244e7..d048007 100644 (file)
@@ -287,7 +287,7 @@ return [
                return ObjectFactory::constructClassInstance( $conf['class'], [ $conf ] );
        },
 
-       'ParserCache' => function( MediaWikiServices $services ) {
+       'ParserCache' => function ( MediaWikiServices $services ) {
                $config = $services->getMainConfig();
                $cache = ObjectCache::getInstance( $config->get( 'ParserCacheType' ) );
                wfDebugLog( 'caches', 'parser: ' . get_class( $cache ) );
@@ -298,7 +298,7 @@ return [
                );
        },
 
-       'LinkCache' => function( MediaWikiServices $services ) {
+       'LinkCache' => function ( MediaWikiServices $services ) {
                return new LinkCache(
                        $services->getTitleFormatter(),
                        $services->getMainWANObjectCache()
index ac00fab..3d5bee2 100644 (file)
@@ -687,7 +687,7 @@ $messageMemc = wfGetMessageCacheStorage();
 /**
  * @deprecated since 1.30
  */
-$parserMemc = new DeprecatedGlobal( 'parserMemc', function() {
+$parserMemc = new DeprecatedGlobal( 'parserMemc', function () {
        return MediaWikiServices::getInstance()->getParserCache()->getCacheStorage();
 }, '1.30' );
 
index 6ce1aed..6a2d0e2 100644 (file)
@@ -34,9 +34,6 @@ class SiteStats {
        /** @var bool */
        private static $loaded = false;
 
-       /** @var int */
-       private static $jobs;
-
        /** @var int[] */
        private static $pageCount = [];
 
@@ -213,24 +210,24 @@ class SiteStats {
        }
 
        /**
+        * Total number of jobs in the job queue.
         * @return int
         */
        static function jobs() {
-               if ( !isset( self::$jobs ) ) {
-                       try{
-                               self::$jobs = array_sum( JobQueueGroup::singleton()->getQueueSizes() );
-                       } catch ( JobQueueError $e ) {
-                               self::$jobs = 0;
-                       }
-                       /**
-                        * Zero rows still do single row read for row that doesn't exist,
-                        * but people are annoyed by that
-                        */
-                       if ( self::$jobs == 1 ) {
-                               self::$jobs = 0;
-                       }
-               }
-               return self::$jobs;
+               $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
+               return $cache->getWithSetCallback(
+                       $cache->makeKey( 'SiteStats', 'jobscount' ),
+                       $cache::TTL_MINUTE,
+                       function ( $oldValue, &$ttl, array &$setOpts ) {
+                               try{
+                                       $jobs = array_sum( JobQueueGroup::singleton()->getQueueSizes() );
+                               } catch ( JobQueueError $e ) {
+                                       $jobs = 0;
+                               }
+                               return $jobs;
+                       },
+                       [ 'pcTTL' => $cache::TTL_PROC_LONG ]
+               );
        }
 
        /**
@@ -296,7 +293,6 @@ class SiteStatsInit {
        private $mUsers = null, $mFiles = null;
 
        /**
-        * Constructor
         * @param bool|IDatabase $database
         * - boolean: Whether to use the master DB
         * - IDatabase: Database connection to use
index 083a725..edfdaca 100644 (file)
@@ -272,7 +272,7 @@ class Title implements LinkTarget {
                }
 
                try {
-                       return Title::newFromTextThrow( strval( $text ), $defaultNamespace );
+                       return self::newFromTextThrow( strval( $text ), $defaultNamespace );
                } catch ( MalformedTitleException $ex ) {
                        return null;
                }
@@ -411,7 +411,7 @@ class Title implements LinkTarget {
                        __METHOD__
                );
                if ( $row !== false ) {
-                       $title = Title::newFromRow( $row );
+                       $title = self::newFromRow( $row );
                } else {
                        $title = null;
                }
@@ -439,7 +439,7 @@ class Title implements LinkTarget {
 
                $titles = [];
                foreach ( $res as $row ) {
-                       $titles[] = Title::newFromRow( $row );
+                       $titles[] = self::newFromRow( $row );
                }
                return $titles;
        }
@@ -541,7 +541,7 @@ class Title implements LinkTarget {
                }
 
                $t = new Title();
-               $t->mDbkeyform = Title::makeName( $ns, $title, $fragment, $interwiki, true );
+               $t->mDbkeyform = self::makeName( $ns, $title, $fragment, $interwiki, true );
 
                try {
                        $t->secureAndSplit();
@@ -557,10 +557,10 @@ class Title implements LinkTarget {
         * @return Title The new object
         */
        public static function newMainPage() {
-               $title = Title::newFromText( wfMessage( 'mainpage' )->inContentLanguage()->text() );
+               $title = self::newFromText( wfMessage( 'mainpage' )->inContentLanguage()->text() );
                // Don't give fatal errors if the message is broken
                if ( !$title ) {
-                       $title = Title::newFromText( 'Main Page' );
+                       $title = self::newFromText( 'Main Page' );
                }
                return $title;
        }
@@ -933,7 +933,7 @@ class Title implements LinkTarget {
         */
        public function getContentModel( $flags = 0 ) {
                if ( !$this->mForcedContentModel
-                       && ( !$this->mContentModel || $flags === Title::GAID_FOR_UPDATE )
+                       && ( !$this->mContentModel || $flags === self::GAID_FOR_UPDATE )
                        && $this->getArticleID( $flags )
                ) {
                        $linkCache = LinkCache::singleton();
@@ -1096,7 +1096,7 @@ class Title implements LinkTarget {
                        if ( $canonicalName ) {
                                $localName = SpecialPageFactory::getLocalNameFor( $canonicalName, $par );
                                if ( $localName != $this->mDbkeyform ) {
-                                       return Title::makeTitle( NS_SPECIAL, $localName );
+                                       return self::makeTitle( NS_SPECIAL, $localName );
                                }
                        }
                }
@@ -1195,7 +1195,7 @@ class Title implements LinkTarget {
         * @return bool
         */
        public function isMainPage() {
-               return $this->equals( Title::newMainPage() );
+               return $this->equals( self::newMainPage() );
        }
 
        /**
@@ -1313,7 +1313,7 @@ class Title implements LinkTarget {
         * @return Title The object for the talk page
         */
        public function getTalkPage() {
-               return Title::makeTitle( MWNamespace::getTalk( $this->getNamespace() ), $this->getDBkey() );
+               return self::makeTitle( MWNamespace::getTalk( $this->getNamespace() ), $this->getDBkey() );
        }
 
        /**
@@ -1328,7 +1328,7 @@ class Title implements LinkTarget {
                if ( $this->getNamespace() == $subjectNS ) {
                        return $this;
                }
-               return Title::makeTitle( $subjectNS, $this->getDBkey() );
+               return self::makeTitle( $subjectNS, $this->getDBkey() );
        }
 
        /**
@@ -1388,7 +1388,7 @@ class Title implements LinkTarget {
                if ( !$this->hasFragment() ) {
                        return '';
                } else {
-                       return '#' . Title::escapeFragmentForURL( $this->getFragment() );
+                       return '#' . self::escapeFragmentForURL( $this->getFragment() );
                }
        }
 
@@ -1535,7 +1535,7 @@ class Title implements LinkTarget {
         * @since 1.20
         */
        public function getRootTitle() {
-               return Title::makeTitle( $this->getNamespace(), $this->getRootText() );
+               return self::makeTitle( $this->getNamespace(), $this->getRootText() );
        }
 
        /**
@@ -1575,7 +1575,7 @@ class Title implements LinkTarget {
         * @since 1.20
         */
        public function getBaseTitle() {
-               return Title::makeTitle( $this->getNamespace(), $this->getBaseText() );
+               return self::makeTitle( $this->getNamespace(), $this->getBaseText() );
        }
 
        /**
@@ -1611,7 +1611,7 @@ class Title implements LinkTarget {
         * @since 1.20
         */
        public function getSubpage( $text ) {
-               return Title::makeTitleSafe( $this->getNamespace(), $this->getText() . '/' . $text );
+               return self::makeTitleSafe( $this->getNamespace(), $this->getText() . '/' . $text );
        }
 
        /**
@@ -2847,7 +2847,7 @@ class Title implements LinkTarget {
                                        $page_id = $row->pr_page;
                                        $page_ns = $row->page_namespace;
                                        $page_title = $row->page_title;
-                                       $sources[$page_id] = Title::makeTitle( $page_ns, $page_title );
+                                       $sources[$page_id] = self::makeTitle( $page_ns, $page_title );
                                        # Add groups needed for each restriction type if its not already there
                                        # Make sure this restriction type still exists
 
@@ -3172,7 +3172,7 @@ class Title implements LinkTarget {
                if ( $limit > -1 ) {
                        $options['LIMIT'] = $limit;
                }
-               $this->mSubpages = TitleArray::newFromResult(
+               return TitleArray::newFromResult(
                        $dbr->select( 'page',
                                [ 'page_id', 'page_namespace', 'page_title', 'page_is_redirect' ],
                                $conds,
@@ -3180,7 +3180,6 @@ class Title implements LinkTarget {
                                $options
                        )
                );
-               return $this->mSubpages;
        }
 
        /**
@@ -3329,7 +3328,7 @@ class Title implements LinkTarget {
         * @return int Int or 0 if the page doesn't exist
         */
        public function getLatestRevID( $flags = 0 ) {
-               if ( !( $flags & Title::GAID_FOR_UPDATE ) && $this->mLatestID !== false ) {
+               if ( !( $flags & self::GAID_FOR_UPDATE ) && $this->mLatestID !== false ) {
                        return intval( $this->mLatestID );
                }
                if ( !$this->getArticleID( $flags ) ) {
@@ -3489,7 +3488,7 @@ class Title implements LinkTarget {
                if ( $res->numRows() ) {
                        $linkCache = LinkCache::singleton();
                        foreach ( $res as $row ) {
-                               $titleObj = Title::makeTitle( $row->page_namespace, $row->page_title );
+                               $titleObj = self::makeTitle( $row->page_namespace, $row->page_title );
                                if ( $titleObj ) {
                                        $linkCache->addGoodLinkObjFromRow( $titleObj, $row );
                                        $retVal[] = $titleObj;
@@ -3557,9 +3556,9 @@ class Title implements LinkTarget {
                $linkCache = LinkCache::singleton();
                foreach ( $res as $row ) {
                        if ( $row->page_id ) {
-                               $titleObj = Title::newFromRow( $row );
+                               $titleObj = self::newFromRow( $row );
                        } else {
-                               $titleObj = Title::makeTitle( $row->$blNamespace, $row->$blTitle );
+                               $titleObj = self::makeTitle( $row->$blNamespace, $row->$blTitle );
                                $linkCache->addBadLinkObj( $titleObj );
                        }
                        $retVal[] = $titleObj;
@@ -3615,7 +3614,7 @@ class Title implements LinkTarget {
 
                $retVal = [];
                foreach ( $res as $row ) {
-                       $retVal[] = Title::makeTitle( $row->pl_namespace, $row->pl_title );
+                       $retVal[] = self::makeTitle( $row->pl_namespace, $row->pl_title );
                }
                return $retVal;
        }
@@ -3827,7 +3826,7 @@ class Title implements LinkTarget {
                        }
                        # T16385: we need makeTitleSafe because the new page names may
                        # be longer than 255 characters.
-                       $newSubpage = Title::makeTitleSafe( $newNs, $newPageName );
+                       $newSubpage = self::makeTitleSafe( $newNs, $newPageName );
 
                        $success = $oldSubpage->moveTo( $newSubpage, $auth, $reason, $createRedirect, $changeTags );
                        if ( $success === true ) {
@@ -3989,7 +3988,7 @@ class Title implements LinkTarget {
                                        # Circular reference
                                        $stack[$parent] = [];
                                } else {
-                                       $nt = Title::newFromText( $parent );
+                                       $nt = self::newFromText( $parent );
                                        if ( $nt ) {
                                                $stack[$parent] = $nt->getParentCategoryTree( $children + [ $parent => 1 ] );
                                        }
index a03bc19..6a532e5 100644 (file)
@@ -115,7 +115,7 @@ class WikiMap {
         * @return string|int Wiki's name or $wiki_id if the wiki was not found
         */
        public static function getWikiName( $wikiID ) {
-               $wiki = WikiMap::getWiki( $wikiID );
+               $wiki = self::getWiki( $wikiID );
 
                if ( $wiki ) {
                        return $wiki->getDisplayName();
@@ -166,7 +166,7 @@ class WikiMap {
         * @return string|bool URL or false if the wiki was not found
         */
        public static function getForeignURL( $wikiID, $page, $fragmentId = null ) {
-               $wiki = WikiMap::getWiki( $wikiID );
+               $wiki = self::getWiki( $wikiID );
 
                if ( $wiki ) {
                        return $wiki->getFullUrl( $page, $fragmentId );
index d016433..16a5a9d 100644 (file)
@@ -225,7 +225,7 @@ class Xml {
                $selected = isset( $languages[$selected] ) ? $selected : $wgLanguageCode;
                $options = "\n";
                foreach ( $languages as $code => $name ) {
-                       $options .= Xml::option( "$code - $name", $code, $code == $selected ) . "\n";
+                       $options .= self::option( "$code - $name", $code, $code == $selected ) . "\n";
                }
 
                $attrs = [ 'id' => 'wpUserLanguage', 'name' => 'wpUserLanguage' ];
@@ -235,8 +235,8 @@ class Xml {
                        $msg = wfMessage( 'yourlanguage' );
                }
                return [
-                       Xml::label( $msg->text(), $attrs['id'] ),
-                       Xml::tags( 'select', $attrs, $options )
+                       self::label( $msg->text(), $attrs['id'] ),
+                       self::tags( 'select', $attrs, $options )
                ];
        }
 
@@ -400,7 +400,7 @@ class Xml {
                $value = false, $attribs = []
        ) {
                return [
-                       Xml::label( $label, $id, $attribs ),
+                       self::label( $label, $id, $attribs ),
                        self::input( $name, $size, $value, [ 'id' => $id ] + $attribs )
                ];
        }
@@ -556,11 +556,11 @@ class Xml {
                        $attribs['tabindex'] = $tabindex;
                }
 
-               return Xml::openElement( 'select', $attribs )
+               return self::openElement( 'select', $attribs )
                        . "\n"
                        . $options
                        . "\n"
-                       . Xml::closeElement( 'select' );
+                       . self::closeElement( 'select' );
        }
 
        /**
@@ -575,15 +575,15 @@ class Xml {
         * @return string
         */
        public static function fieldset( $legend = false, $content = false, $attribs = [] ) {
-               $s = Xml::openElement( 'fieldset', $attribs ) . "\n";
+               $s = self::openElement( 'fieldset', $attribs ) . "\n";
 
                if ( $legend ) {
-                       $s .= Xml::element( 'legend', null, $legend ) . "\n";
+                       $s .= self::element( 'legend', null, $legend ) . "\n";
                }
 
                if ( $content !== false ) {
                        $s .= $content . "\n";
-                       $s .= Xml::closeElement( 'fieldset' ) . "\n";
+                       $s .= self::closeElement( 'fieldset' ) . "\n";
                }
 
                return $s;
@@ -644,7 +644,7 @@ class Xml {
         */
        public static function encodeJsCall( $name, $args, $pretty = false ) {
                foreach ( $args as &$arg ) {
-                       $arg = Xml::encodeJsVar( $arg, $pretty );
+                       $arg = self::encodeJsVar( $arg, $pretty );
                        if ( $arg === false ) {
                                return false;
                        }
@@ -702,7 +702,7 @@ class Xml {
                        $text .
                        '</html>';
 
-               return Xml::isWellFormed( $html );
+               return self::isWellFormed( $html );
        }
 
        /**
@@ -736,25 +736,25 @@ class Xml {
 
                foreach ( $fields as $labelmsg => $input ) {
                        $id = "mw-$labelmsg";
-                       $form .= Xml::openElement( 'tr', [ 'id' => $id ] );
+                       $form .= self::openElement( 'tr', [ 'id' => $id ] );
 
                        // TODO use a <label> here for accessibility purposes - will need
                        // to either not use a table to build the form, or find the ID of
                        // the input somehow.
 
-                       $form .= Xml::tags( 'td', [ 'class' => 'mw-label' ], wfMessage( $labelmsg )->parse() );
-                       $form .= Xml::openElement( 'td', [ 'class' => 'mw-input' ] )
-                               . $input . Xml::closeElement( 'td' );
-                       $form .= Xml::closeElement( 'tr' );
+                       $form .= self::tags( 'td', [ 'class' => 'mw-label' ], wfMessage( $labelmsg )->parse() );
+                       $form .= self::openElement( 'td', [ 'class' => 'mw-input' ] )
+                               . $input . self::closeElement( 'td' );
+                       $form .= self::closeElement( 'tr' );
                }
 
                if ( $submitLabel ) {
-                       $form .= Xml::openElement( 'tr' );
-                       $form .= Xml::tags( 'td', [], '' );
-                       $form .= Xml::openElement( 'td', [ 'class' => 'mw-submit' ] )
-                               . Xml::submitButton( wfMessage( $submitLabel )->text(), $submitAttribs )
-                               . Xml::closeElement( 'td' );
-                       $form .= Xml::closeElement( 'tr' );
+                       $form .= self::openElement( 'tr' );
+                       $form .= self::tags( 'td', [], '' );
+                       $form .= self::openElement( 'td', [ 'class' => 'mw-submit' ] )
+                               . self::submitButton( wfMessage( $submitLabel )->text(), $submitAttribs )
+                               . self::closeElement( 'td' );
+                       $form .= self::closeElement( 'tr' );
                }
 
                $form .= "</tbody></table>";
@@ -770,10 +770,10 @@ class Xml {
         * @return string
         */
        public static function buildTable( $rows, $attribs = [], $headers = null ) {
-               $s = Xml::openElement( 'table', $attribs );
+               $s = self::openElement( 'table', $attribs );
 
                if ( is_array( $headers ) ) {
-                       $s .= Xml::openElement( 'thead', $attribs );
+                       $s .= self::openElement( 'thead', $attribs );
 
                        foreach ( $headers as $id => $header ) {
                                $attribs = [];
@@ -782,9 +782,9 @@ class Xml {
                                        $attribs['id'] = $id;
                                }
 
-                               $s .= Xml::element( 'th', $attribs, $header );
+                               $s .= self::element( 'th', $attribs, $header );
                        }
-                       $s .= Xml::closeElement( 'thead' );
+                       $s .= self::closeElement( 'thead' );
                }
 
                foreach ( $rows as $id => $row ) {
@@ -794,10 +794,10 @@ class Xml {
                                $attribs['id'] = $id;
                        }
 
-                       $s .= Xml::buildTableRow( $attribs, $row );
+                       $s .= self::buildTableRow( $attribs, $row );
                }
 
-               $s .= Xml::closeElement( 'table' );
+               $s .= self::closeElement( 'table' );
 
                return $s;
        }
@@ -809,7 +809,7 @@ class Xml {
         * @return string
         */
        public static function buildTableRow( $attribs, $cells ) {
-               $s = Xml::openElement( 'tr', $attribs );
+               $s = self::openElement( 'tr', $attribs );
 
                foreach ( $cells as $id => $cell ) {
                        $attribs = [];
@@ -818,10 +818,10 @@ class Xml {
                                $attribs['id'] = $id;
                        }
 
-                       $s .= Xml::element( 'td', $attribs, $cell );
+                       $s .= self::element( 'td', $attribs, $cell );
                }
 
-               $s .= Xml::closeElement( 'tr' );
+               $s .= self::closeElement( 'tr' );
 
                return $s;
        }
index 88382b6..e8d9a3e 100644 (file)
@@ -151,7 +151,7 @@ abstract class Action implements MessageLocalizer {
                        return 'view';
                }
 
-               $action = Action::factory( $actionName, $context->getWikiPage(), $context );
+               $action = self::factory( $actionName, $context->getWikiPage(), $context );
                if ( $action instanceof Action ) {
                        return $action->getName();
                }
@@ -388,7 +388,7 @@ abstract class Action implements MessageLocalizer {
        public function addHelpLink( $to, $overrideBaseUrl = false ) {
                global $wgContLang;
                $msg = wfMessage( $wgContLang->lc(
-                       Action::getActionName( $this->getContext() )
+                       self::getActionName( $this->getContext() )
                        ) . '-helppage' );
 
                if ( !$msg->isDisabled() ) {
index bc3def8..034d243 100644 (file)
@@ -548,7 +548,7 @@ abstract class ApiBase extends ContextSource {
                // Main module has this method overridden
                // Safety - avoid infinite loop:
                if ( $this->isMain() ) {
-                       ApiBase::dieDebug( __METHOD__, 'base method was called on main module.' );
+                       self::dieDebug( __METHOD__, 'base method was called on main module.' );
                }
 
                return $this->getMain()->lacksSameOriginSecurity();
@@ -620,7 +620,7 @@ abstract class ApiBase extends ContextSource {
                // Main module has getResult() method overridden
                // Safety - avoid infinite loop:
                if ( $this->isMain() ) {
-                       ApiBase::dieDebug( __METHOD__, 'base method was called on main module. ' );
+                       self::dieDebug( __METHOD__, 'base method was called on main module. ' );
                }
 
                return $this->getMain()->getResult();
@@ -634,7 +634,7 @@ abstract class ApiBase extends ContextSource {
                // Main module has getErrorFormatter() method overridden
                // Safety - avoid infinite loop:
                if ( $this->isMain() ) {
-                       ApiBase::dieDebug( __METHOD__, 'base method was called on main module. ' );
+                       self::dieDebug( __METHOD__, 'base method was called on main module. ' );
                }
 
                return $this->getMain()->getErrorFormatter();
@@ -660,7 +660,7 @@ abstract class ApiBase extends ContextSource {
                // Main module has getContinuationManager() method overridden
                // Safety - avoid infinite loop:
                if ( $this->isMain() ) {
-                       ApiBase::dieDebug( __METHOD__, 'base method was called on main module. ' );
+                       self::dieDebug( __METHOD__, 'base method was called on main module. ' );
                }
 
                return $this->getMain()->getContinuationManager();
@@ -674,7 +674,7 @@ abstract class ApiBase extends ContextSource {
                // Main module has setContinuationManager() method overridden
                // Safety - avoid infinite loop:
                if ( $this->isMain() ) {
-                       ApiBase::dieDebug( __METHOD__, 'base method was called on main module. ' );
+                       self::dieDebug( __METHOD__, 'base method was called on main module. ' );
                }
 
                $this->getMain()->setContinuationManager( $manager );
@@ -1059,7 +1059,7 @@ abstract class ApiBase extends ContextSource {
                if ( $type == 'boolean' ) {
                        if ( isset( $default ) && $default !== false ) {
                                // Having a default value of anything other than 'false' is not allowed
-                               ApiBase::dieDebug(
+                               self::dieDebug(
                                        __METHOD__,
                                        "Boolean param $encParamName's default is set to '$default'. " .
                                                'Boolean parameters must default to false.'
@@ -1070,13 +1070,13 @@ abstract class ApiBase extends ContextSource {
                } elseif ( $type == 'upload' ) {
                        if ( isset( $default ) ) {
                                // Having a default value is not allowed
-                               ApiBase::dieDebug(
+                               self::dieDebug(
                                        __METHOD__,
                                        "File upload param $encParamName's default is set to " .
                                                "'$default'. File upload parameters may not have a default." );
                        }
                        if ( $multi ) {
-                               ApiBase::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
+                               self::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
                        }
                        $value = $this->getMain()->getUpload( $encParamName );
                        if ( !$value->exists() ) {
@@ -1138,7 +1138,7 @@ abstract class ApiBase extends ContextSource {
 
                $allSpecifier = ( is_string( $allowAll ) ? $allowAll : self::ALL_DEFAULT_STRING );
                if ( $allowAll && $multi && is_array( $type ) && in_array( $allSpecifier, $type, true ) ) {
-                       ApiBase::dieDebug(
+                       self::dieDebug(
                                __METHOD__,
                                "For param $encParamName, PARAM_ALL collides with a possible value" );
                }
@@ -1194,13 +1194,13 @@ abstract class ApiBase extends ContextSource {
                                                if ( !isset( $paramSettings[self::PARAM_MAX] )
                                                        || !isset( $paramSettings[self::PARAM_MAX2] )
                                                ) {
-                                                       ApiBase::dieDebug(
+                                                       self::dieDebug(
                                                                __METHOD__,
                                                                "MAX1 or MAX2 are not defined for the limit $encParamName"
                                                        );
                                                }
                                                if ( $multi ) {
-                                                       ApiBase::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
+                                                       self::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
                                                }
                                                $min = isset( $paramSettings[self::PARAM_MIN] ) ? $paramSettings[self::PARAM_MIN] : 0;
                                                if ( $value == 'max' ) {
@@ -1221,7 +1221,7 @@ abstract class ApiBase extends ContextSource {
                                                break;
                                        case 'boolean':
                                                if ( $multi ) {
-                                                       ApiBase::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
+                                                       self::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
                                                }
                                                break;
                                        case 'timestamp':
@@ -1255,7 +1255,7 @@ abstract class ApiBase extends ContextSource {
                                                }
                                                break;
                                        default:
-                                               ApiBase::dieDebug( __METHOD__, "Param $encParamName's type is unknown - $type" );
+                                               self::dieDebug( __METHOD__, "Param $encParamName's type is unknown - $type" );
                                }
                        }
 
@@ -2077,19 +2077,19 @@ abstract class ApiBase extends ContextSource {
         * @return Message
         */
        public function getFinalSummary() {
-               $msg = ApiBase::makeMessage( $this->getSummaryMessage(), $this->getContext(), [
+               $msg = self::makeMessage( $this->getSummaryMessage(), $this->getContext(), [
                        $this->getModulePrefix(),
                        $this->getModuleName(),
                        $this->getModulePath(),
                ] );
                if ( !$msg->exists() ) {
                        wfDeprecated( 'API help "description" messages', '1.30' );
-                       $msg = ApiBase::makeMessage( $this->getDescriptionMessage(), $this->getContext(), [
+                       $msg = self::makeMessage( $this->getDescriptionMessage(), $this->getContext(), [
                                $this->getModulePrefix(),
                                $this->getModuleName(),
                                $this->getModulePath(),
                        ] );
-                       $msg = ApiBase::makeMessage( 'rawmessage', $this->getContext(), [
+                       $msg = self::makeMessage( 'rawmessage', $this->getContext(), [
                                preg_replace( '/\n.*/s', '', $msg->text() )
                        ] );
                }
@@ -2116,12 +2116,12 @@ abstract class ApiBase extends ContextSource {
                        $desc = (string)$desc;
                }
 
-               $summary = ApiBase::makeMessage( $this->getSummaryMessage(), $this->getContext(), [
+               $summary = self::makeMessage( $this->getSummaryMessage(), $this->getContext(), [
                        $this->getModulePrefix(),
                        $this->getModuleName(),
                        $this->getModulePath(),
                ] );
-               $extendedDescription = ApiBase::makeMessage(
+               $extendedDescription = self::makeMessage(
                        $this->getExtendedDescription(), $this->getContext(), [
                                $this->getModulePrefix(),
                                $this->getModuleName(),
@@ -2133,7 +2133,7 @@ abstract class ApiBase extends ContextSource {
                        $msgs = [ $summary, $extendedDescription ];
                } else {
                        wfDeprecated( 'API help "description" messages', '1.30' );
-                       $description = ApiBase::makeMessage( $this->getDescriptionMessage(), $this->getContext(), [
+                       $description = self::makeMessage( $this->getDescriptionMessage(), $this->getContext(), [
                                $this->getModulePrefix(),
                                $this->getModuleName(),
                                $this->getModulePath(),
@@ -2165,10 +2165,10 @@ abstract class ApiBase extends ContextSource {
 
                if ( $this->needsToken() ) {
                        $params['token'] = [
-                               ApiBase::PARAM_TYPE => 'string',
-                               ApiBase::PARAM_REQUIRED => true,
-                               ApiBase::PARAM_SENSITIVE => true,
-                               ApiBase::PARAM_HELP_MSG => [
+                               self::PARAM_TYPE => 'string',
+                               self::PARAM_REQUIRED => true,
+                               self::PARAM_SENSITIVE => true,
+                               self::PARAM_HELP_MSG => [
                                        'api-help-param-token',
                                        $this->needsToken(),
                                ],
@@ -2205,7 +2205,7 @@ abstract class ApiBase extends ContextSource {
                }
                $desc = self::escapeWikiText( $desc );
 
-               $params = $this->getFinalParams( ApiBase::GET_VALUES_FOR_HELP );
+               $params = $this->getFinalParams( self::GET_VALUES_FOR_HELP );
                $msgs = [];
                foreach ( $params as $param => $settings ) {
                        if ( !is_array( $settings ) ) {
@@ -2224,15 +2224,15 @@ abstract class ApiBase extends ContextSource {
                                $d = implode( ' ', $d );
                        }
 
-                       if ( isset( $settings[ApiBase::PARAM_HELP_MSG] ) ) {
-                               $msg = $settings[ApiBase::PARAM_HELP_MSG];
+                       if ( isset( $settings[self::PARAM_HELP_MSG] ) ) {
+                               $msg = $settings[self::PARAM_HELP_MSG];
                        } else {
                                $msg = $this->msg( "apihelp-{$path}-param-{$param}" );
                                if ( !$msg->exists() ) {
                                        $msg = $this->msg( 'api-help-fallback-parameter', $d );
                                }
                        }
-                       $msg = ApiBase::makeMessage( $msg, $this->getContext(),
+                       $msg = self::makeMessage( $msg, $this->getContext(),
                                [ $prefix, $param, $name, $path ] );
                        if ( !$msg ) {
                                self::dieDebug( __METHOD__,
@@ -2240,11 +2240,11 @@ abstract class ApiBase extends ContextSource {
                        }
                        $msgs[$param] = [ $msg ];
 
-                       if ( isset( $settings[ApiBase::PARAM_TYPE] ) &&
-                               $settings[ApiBase::PARAM_TYPE] === 'submodule'
+                       if ( isset( $settings[self::PARAM_TYPE] ) &&
+                               $settings[self::PARAM_TYPE] === 'submodule'
                        ) {
-                               if ( isset( $settings[ApiBase::PARAM_SUBMODULE_MAP] ) ) {
-                                       $map = $settings[ApiBase::PARAM_SUBMODULE_MAP];
+                               if ( isset( $settings[self::PARAM_SUBMODULE_MAP] ) ) {
+                                       $map = $settings[self::PARAM_SUBMODULE_MAP];
                                } else {
                                        $prefix = $this->isMain() ? '' : ( $this->getModulePath() . '+' );
                                        $map = [];
@@ -2282,29 +2282,29 @@ abstract class ApiBase extends ContextSource {
                                        $arr[] = $m->setContext( $this->getContext() );
                                }
                                $msgs[$param] = array_merge( $msgs[$param], $submodules, $deprecatedSubmodules );
-                       } elseif ( isset( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] ) ) {
-                               if ( !is_array( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] ) ) {
+                       } elseif ( isset( $settings[self::PARAM_HELP_MSG_PER_VALUE] ) ) {
+                               if ( !is_array( $settings[self::PARAM_HELP_MSG_PER_VALUE] ) ) {
                                        self::dieDebug( __METHOD__,
                                                'ApiBase::PARAM_HELP_MSG_PER_VALUE is not valid' );
                                }
-                               if ( !is_array( $settings[ApiBase::PARAM_TYPE] ) ) {
+                               if ( !is_array( $settings[self::PARAM_TYPE] ) ) {
                                        self::dieDebug( __METHOD__,
                                                'ApiBase::PARAM_HELP_MSG_PER_VALUE may only be used when ' .
                                                'ApiBase::PARAM_TYPE is an array' );
                                }
 
-                               $valueMsgs = $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE];
-                               $deprecatedValues = isset( $settings[ApiBase::PARAM_DEPRECATED_VALUES] )
-                                       ? $settings[ApiBase::PARAM_DEPRECATED_VALUES]
+                               $valueMsgs = $settings[self::PARAM_HELP_MSG_PER_VALUE];
+                               $deprecatedValues = isset( $settings[self::PARAM_DEPRECATED_VALUES] )
+                                       ? $settings[self::PARAM_DEPRECATED_VALUES]
                                        : [];
 
-                               foreach ( $settings[ApiBase::PARAM_TYPE] as $value ) {
+                               foreach ( $settings[self::PARAM_TYPE] as $value ) {
                                        if ( isset( $valueMsgs[$value] ) ) {
                                                $msg = $valueMsgs[$value];
                                        } else {
                                                $msg = "apihelp-{$path}-paramvalue-{$param}-{$value}";
                                        }
-                                       $m = ApiBase::makeMessage( $msg, $this->getContext(),
+                                       $m = self::makeMessage( $msg, $this->getContext(),
                                                [ $prefix, $param, $name, $path, $value ] );
                                        if ( $m ) {
                                                $m = new ApiHelpParamValueMessage(
@@ -2321,13 +2321,13 @@ abstract class ApiBase extends ContextSource {
                                }
                        }
 
-                       if ( isset( $settings[ApiBase::PARAM_HELP_MSG_APPEND] ) ) {
-                               if ( !is_array( $settings[ApiBase::PARAM_HELP_MSG_APPEND] ) ) {
+                       if ( isset( $settings[self::PARAM_HELP_MSG_APPEND] ) ) {
+                               if ( !is_array( $settings[self::PARAM_HELP_MSG_APPEND] ) ) {
                                        self::dieDebug( __METHOD__,
                                                'Value for ApiBase::PARAM_HELP_MSG_APPEND is not an array' );
                                }
-                               foreach ( $settings[ApiBase::PARAM_HELP_MSG_APPEND] as $m ) {
-                                       $m = ApiBase::makeMessage( $m, $this->getContext(),
+                               foreach ( $settings[self::PARAM_HELP_MSG_APPEND] as $m ) {
+                                       $m = self::makeMessage( $m, $this->getContext(),
                                                [ $prefix, $param, $name, $path ] );
                                        if ( $m ) {
                                                $msgs[$param][] = $m;
@@ -2614,6 +2614,7 @@ abstract class ApiBase extends ContextSource {
         * @param string $warning Warning message
         */
        public function setWarning( $warning ) {
+               wfDeprecated( __METHOD__, '1.29' );
                $msg = new ApiRawMessage( $warning, 'warning' );
                $this->getErrorFormatter()->addWarning( $this->getModulePath(), $msg );
        }
@@ -2632,6 +2633,7 @@ abstract class ApiBase extends ContextSource {
         * @throws ApiUsageException always
         */
        public function dieUsage( $description, $errorCode, $httpRespCode = 0, $extradata = null ) {
+               wfDeprecated( __METHOD__, '1.29' );
                $this->dieWithError(
                        new RawMessage( '$1', [ $description ] ),
                        $errorCode,
@@ -2651,6 +2653,7 @@ abstract class ApiBase extends ContextSource {
         * @throws MWException
         */
        public function getErrorFromStatus( $status, &$extraData = null ) {
+               wfDeprecated( __METHOD__, '1.29' );
                if ( $status->isGood() ) {
                        throw new MWException( 'Successful status passed to ApiBase::dieStatus' );
                }
@@ -2860,6 +2863,7 @@ abstract class ApiBase extends ContextSource {
         * @return [ 'code' => code, 'info' => info ]
         */
        public function parseMsg( $error ) {
+               wfDeprecated( __METHOD__, '1.29' );
                // Check whether someone passed the whole array, instead of one element as
                // documented. This breaks if it's actually an array of fallback keys, but
                // that's long-standing misbehavior introduced in r87627 to incorrectly
@@ -2889,6 +2893,7 @@ abstract class ApiBase extends ContextSource {
         * @throws ApiUsageException always
         */
        public function dieUsageMsg( $error ) {
+               wfDeprecated( __METHOD__, '1.29' );
                $this->dieWithError( $this->parseMsgInternal( $error ) );
        }
 
@@ -2901,6 +2906,7 @@ abstract class ApiBase extends ContextSource {
         * @since 1.21
         */
        public function dieUsageMsgOrDebug( $error ) {
+               wfDeprecated( __METHOD__, '1.29' );
                $this->dieWithErrorOrDebug( $this->parseMsgInternal( $error ) );
        }
 
index 72c7c35..4b4b76b 100644 (file)
@@ -71,7 +71,7 @@ class ApiEmailUser extends ApiBase {
                }
 
                $result = array_filter( [
-                       'result' => $retval->isGood() ? 'Success' : $retval->isOk() ? 'Warnings' : 'Failure',
+                       'result' => $retval->isGood() ? 'Success' : ( $retval->isOk() ? 'Warnings' : 'Failure' ),
                        'warnings' => $this->getErrorFormatter()->arrayFromStatus( $retval, 'warning' ),
                        'errors' => $this->getErrorFormatter()->arrayFromStatus( $retval, 'error' ),
                ] );
index 52f79ee..b7d4529 100644 (file)
@@ -1041,7 +1041,7 @@ class ApiMain extends ApiBase {
                        // None of the rest have any messages for non-error types
                } elseif ( $e instanceof UsageException ) {
                        // User entered incorrect parameters - generate error response
-                       $data = $e->getMessageArray();
+                       $data = MediaWiki\quietCall( [ $e, 'getMessageArray' ] );
                        $code = $data['code'];
                        $info = $data['info'];
                        unset( $data['code'], $data['info'] );
@@ -1829,7 +1829,7 @@ class ApiMain extends ApiBase {
                                ApiBase::PARAM_TYPE => 'submodule',
                        ],
                        'format' => [
-                               ApiBase::PARAM_DFLT => ApiMain::API_DEFAULT_FORMAT,
+                               ApiBase::PARAM_DFLT => self::API_DEFAULT_FORMAT,
                                ApiBase::PARAM_TYPE => 'submodule',
                        ],
                        'maxlag' => [
index 599b3de..cfac761 100644 (file)
@@ -121,7 +121,7 @@ class ApiPageSet extends ApiBase {
        public function __construct( ApiBase $dbSource, $flags = 0, $defaultNamespace = NS_MAIN ) {
                parent::__construct( $dbSource->getMain(), $dbSource->getModuleName() );
                $this->mDbSource = $dbSource;
-               $this->mAllowGenerator = ( $flags & ApiPageSet::DISABLE_GENERATORS ) == 0;
+               $this->mAllowGenerator = ( $flags & self::DISABLE_GENERATORS ) == 0;
                $this->mDefaultNamespace = $defaultNamespace;
 
                $this->mParams = $this->extractRequestParams();
@@ -166,7 +166,7 @@ class ApiPageSet extends ApiBase {
                        }
                        // Create a temporary pageset to store generator's output,
                        // add any additional fields generator may need, and execute pageset to populate titles/pageids
-                       $tmpPageSet = new ApiPageSet( $dbSource, ApiPageSet::DISABLE_GENERATORS );
+                       $tmpPageSet = new ApiPageSet( $dbSource, self::DISABLE_GENERATORS );
                        $generator->setGeneratorMode( $tmpPageSet );
                        $this->mCacheMode = $generator->getCacheMode( $generator->extractRequestParams() );
 
index b2664df..bfd5b17 100644 (file)
@@ -677,7 +677,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
                                ApiBase::PARAM_DFLT => -1,
                                ApiBase::PARAM_HELP_MSG => [
                                        'apihelp-query+imageinfo-param-urlwidth',
-                                       ApiQueryImageInfo::TRANSFORM_LIMIT,
+                                       self::TRANSFORM_LIMIT,
                                ],
                        ],
                        'urlheight' => [
index 6b8f98c..ecdebd4 100644 (file)
@@ -128,7 +128,7 @@ class ApiQueryInfo extends ApiQueryBase {
         * @deprecated since 1.24
         */
        public static function resetTokenCache() {
-               ApiQueryInfo::$cachedTokens = [];
+               self::$cachedTokens = [];
        }
 
        /**
@@ -144,11 +144,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['edit'] ) ) {
-                       ApiQueryInfo::$cachedTokens['edit'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['edit'] ) ) {
+                       self::$cachedTokens['edit'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['edit'];
+               return self::$cachedTokens['edit'];
        }
 
        /**
@@ -161,11 +161,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['delete'] ) ) {
-                       ApiQueryInfo::$cachedTokens['delete'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['delete'] ) ) {
+                       self::$cachedTokens['delete'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['delete'];
+               return self::$cachedTokens['delete'];
        }
 
        /**
@@ -178,11 +178,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['protect'] ) ) {
-                       ApiQueryInfo::$cachedTokens['protect'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['protect'] ) ) {
+                       self::$cachedTokens['protect'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['protect'];
+               return self::$cachedTokens['protect'];
        }
 
        /**
@@ -195,11 +195,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['move'] ) ) {
-                       ApiQueryInfo::$cachedTokens['move'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['move'] ) ) {
+                       self::$cachedTokens['move'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['move'];
+               return self::$cachedTokens['move'];
        }
 
        /**
@@ -212,11 +212,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['block'] ) ) {
-                       ApiQueryInfo::$cachedTokens['block'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['block'] ) ) {
+                       self::$cachedTokens['block'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['block'];
+               return self::$cachedTokens['block'];
        }
 
        /**
@@ -237,11 +237,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['email'] ) ) {
-                       ApiQueryInfo::$cachedTokens['email'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['email'] ) ) {
+                       self::$cachedTokens['email'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['email'];
+               return self::$cachedTokens['email'];
        }
 
        /**
@@ -254,11 +254,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['import'] ) ) {
-                       ApiQueryInfo::$cachedTokens['import'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['import'] ) ) {
+                       self::$cachedTokens['import'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['import'];
+               return self::$cachedTokens['import'];
        }
 
        /**
@@ -271,11 +271,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['watch'] ) ) {
-                       ApiQueryInfo::$cachedTokens['watch'] = $wgUser->getEditToken( 'watch' );
+               if ( !isset( self::$cachedTokens['watch'] ) ) {
+                       self::$cachedTokens['watch'] = $wgUser->getEditToken( 'watch' );
                }
 
-               return ApiQueryInfo::$cachedTokens['watch'];
+               return self::$cachedTokens['watch'];
        }
 
        /**
@@ -288,11 +288,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['options'] ) ) {
-                       ApiQueryInfo::$cachedTokens['options'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['options'] ) ) {
+                       self::$cachedTokens['options'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['options'];
+               return self::$cachedTokens['options'];
        }
 
        public function execute() {
index 6734740..468d878 100644 (file)
@@ -287,12 +287,12 @@ class ApiResult implements ApiSerializable {
         * @param int $flags Zero or more OR-ed flags like OVERRIDE | ADD_ON_TOP.
         */
        public static function setValue( array &$arr, $name, $value, $flags = 0 ) {
-               if ( ( $flags & ApiResult::NO_VALIDATE ) !== ApiResult::NO_VALIDATE ) {
+               if ( ( $flags & self::NO_VALIDATE ) !== self::NO_VALIDATE ) {
                        $value = self::validateValue( $value );
                }
 
                if ( $name === null ) {
-                       if ( $flags & ApiResult::ADD_ON_TOP ) {
+                       if ( $flags & self::ADD_ON_TOP ) {
                                array_unshift( $arr, $value );
                        } else {
                                array_push( $arr, $value );
@@ -301,8 +301,8 @@ class ApiResult implements ApiSerializable {
                }
 
                $exists = isset( $arr[$name] );
-               if ( !$exists || ( $flags & ApiResult::OVERRIDE ) ) {
-                       if ( !$exists && ( $flags & ApiResult::ADD_ON_TOP ) ) {
+               if ( !$exists || ( $flags & self::OVERRIDE ) ) {
+                       if ( !$exists && ( $flags & self::ADD_ON_TOP ) ) {
                                $arr = [ $name => $value ] + $arr;
                        } else {
                                $arr[$name] = $value;
@@ -403,13 +403,13 @@ class ApiResult implements ApiSerializable {
         * @since 1.21 int $flags replaced boolean $override
         */
        public function addValue( $path, $name, $value, $flags = 0 ) {
-               $arr = &$this->path( $path, ( $flags & ApiResult::ADD_ON_TOP ) ? 'prepend' : 'append' );
+               $arr = &$this->path( $path, ( $flags & self::ADD_ON_TOP ) ? 'prepend' : 'append' );
 
-               if ( $this->checkingSize && !( $flags & ApiResult::NO_SIZE_CHECK ) ) {
+               if ( $this->checkingSize && !( $flags & self::NO_SIZE_CHECK ) ) {
                        // self::size needs the validated value. Then flag
                        // to not re-validate later.
                        $value = self::validateValue( $value );
-                       $flags |= ApiResult::NO_VALIDATE;
+                       $flags |= self::NO_VALIDATE;
 
                        $newsize = $this->size + self::size( $value );
                        if ( $this->maxSize !== false && $newsize > $this->maxSize ) {
@@ -459,7 +459,7 @@ class ApiResult implements ApiSerializable {
                        $name = array_pop( $path );
                }
                $ret = self::unsetValue( $this->path( $path, 'dummy' ), $name );
-               if ( $this->checkingSize && !( $flags & ApiResult::NO_SIZE_CHECK ) ) {
+               if ( $this->checkingSize && !( $flags & self::NO_SIZE_CHECK ) ) {
                        $newsize = $this->size - self::size( $ret );
                        $this->size = max( $newsize, 0 );
                }
@@ -511,7 +511,7 @@ class ApiResult implements ApiSerializable {
        public function addParsedLimit( $moduleName, $limit ) {
                // Add value, allowing overwriting
                $this->addValue( 'limits', $moduleName, $limit,
-                       ApiResult::OVERRIDE | ApiResult::NO_SIZE_CHECK );
+                       self::OVERRIDE | self::NO_SIZE_CHECK );
        }
 
        /**@}*/
@@ -551,7 +551,7 @@ class ApiResult implements ApiSerializable {
         * @param int $flags Zero or more OR-ed flags like OVERRIDE | ADD_ON_TOP.
         */
        public function addContentField( $path, $name, $flags = 0 ) {
-               $arr = &$this->path( $path, ( $flags & ApiResult::ADD_ON_TOP ) ? 'prepend' : 'append' );
+               $arr = &$this->path( $path, ( $flags & self::ADD_ON_TOP ) ? 'prepend' : 'append' );
                self::setContentField( $arr, $name, $flags );
        }
 
@@ -1156,7 +1156,7 @@ class ApiResult implements ApiSerializable {
                $bools = [];
                foreach ( $vars as $k => $v ) {
                        if ( is_array( $v ) || is_object( $v ) ) {
-                               $vars[$k] = ApiResult::addMetadataToResultVars( (array)$v, is_object( $v ) );
+                               $vars[$k] = self::addMetadataToResultVars( (array)$v, is_object( $v ) );
                        } elseif ( is_bool( $v ) ) {
                                // Better here to use real bools even in BC formats
                                $bools[] = $k;
@@ -1176,22 +1176,22 @@ class ApiResult implements ApiSerializable {
                        // Get the list of keys we actually care about. Unfortunately, we can't support
                        // certain keys that conflict with ApiResult metadata.
                        $keys = array_diff( array_keys( $vars ), [
-                               ApiResult::META_TYPE, ApiResult::META_PRESERVE_KEYS, ApiResult::META_KVP_KEY_NAME,
-                               ApiResult::META_INDEXED_TAG_NAME, ApiResult::META_BC_BOOLS
+                               self::META_TYPE, self::META_PRESERVE_KEYS, self::META_KVP_KEY_NAME,
+                               self::META_INDEXED_TAG_NAME, self::META_BC_BOOLS
                        ] );
 
                        return [
-                               ApiResult::META_TYPE => 'kvp',
-                               ApiResult::META_KVP_KEY_NAME => 'key',
-                               ApiResult::META_PRESERVE_KEYS => $keys,
-                               ApiResult::META_BC_BOOLS => $bools,
-                               ApiResult::META_INDEXED_TAG_NAME => 'var',
+                               self::META_TYPE => 'kvp',
+                               self::META_KVP_KEY_NAME => 'key',
+                               self::META_PRESERVE_KEYS => $keys,
+                               self::META_BC_BOOLS => $bools,
+                               self::META_INDEXED_TAG_NAME => 'var',
                        ] + $vars;
                } else {
                        return [
-                               ApiResult::META_TYPE => 'array',
-                               ApiResult::META_BC_BOOLS => $bools,
-                               ApiResult::META_INDEXED_TAG_NAME => 'value',
+                               self::META_TYPE => 'array',
+                               self::META_BC_BOOLS => $bools,
+                               self::META_INDEXED_TAG_NAME => 'value',
                        ] + $vars;
                }
        }
index 9dc1f92..fb49e2d 100644 (file)
@@ -45,6 +45,10 @@ class UsageException extends MWException {
                $this->mCodestr = $codestr;
                $this->mExtraData = $extradata;
 
+               if ( !$this instanceof ApiUsageException ) {
+                       wfDeprecated( __METHOD__, '1.29' );
+               }
+
                // This should never happen, so throw an exception about it that will
                // hopefully get logged with a backtrace (T138585)
                if ( !is_string( $codestr ) || $codestr === '' ) {
@@ -58,6 +62,7 @@ class UsageException extends MWException {
         * @return string
         */
        public function getCodeString() {
+               wfDeprecated( __METHOD__, '1.29' );
                return $this->mCodestr;
        }
 
@@ -65,6 +70,7 @@ class UsageException extends MWException {
         * @return array
         */
        public function getMessageArray() {
+               wfDeprecated( __METHOD__, '1.29' );
                $result = [
                        'code' => $this->mCodestr,
                        'info' => $this->getMessage()
@@ -183,6 +189,7 @@ class ApiUsageException extends UsageException implements ILocalizedException {
         * @inheritdoc
         */
        public function getCodeString() {
+               wfDeprecated( __METHOD__, '1.29' );
                return $this->getApiMessage()->getApiCode();
        }
 
@@ -192,6 +199,7 @@ class ApiUsageException extends UsageException implements ILocalizedException {
         * @inheritdoc
         */
        public function getMessageArray() {
+               wfDeprecated( __METHOD__, '1.29' );
                $enMsg = clone $this->getApiMessage();
                $enMsg->inLanguage( 'en' )->useDatabase( false );
 
index 6fea718..8918620 100644 (file)
        "apihelp-query+langlinks-param-lang": "להחזיר רק קישורי שפה עם קוד השפה הזה.",
        "apihelp-query+langlinks-param-title": "קישור לחיפוש. חובה להשתמש עם <var>$1lang</var>.",
        "apihelp-query+langlinks-param-dir": "באיזה כיוון לרשום.",
-       "apihelp-query+langlinks-param-inlanguagecode": "ק×\95×\93 ×©×¤×\94 ×©שמות שפות מתורגמות.",
+       "apihelp-query+langlinks-param-inlanguagecode": "ק×\95×\93 ×©×¤×\94 ×\91ש×\91×\99×\9c שמות שפות מתורגמות.",
        "apihelp-query+langlinks-example-simple": "קבלת קישורים בין־לשוניים מהדף <kbd>Main Page</kbd>.",
        "apihelp-query+links-summary": "החזרת כל הקישורים מהדפים שצוינו.",
        "apihelp-query+links-param-namespace": "להציג קישורים רק במרחבי השם האלה.",
        "apihelp-query+siteinfo-param-filteriw": "החזרה רק של עיולים מקומיים או רק של עיולים לא מקומיים ממפת הבינוויקי.",
        "apihelp-query+siteinfo-param-showalldb": "רשימת כל שרתי מסד הנתונים, לא רק אלה שהכי מתעכבים.",
        "apihelp-query+siteinfo-param-numberingroup": "רשימת מספרי משתמשים בקבוצות משתמשים.",
-       "apihelp-query+siteinfo-param-inlanguagecode": "ק×\95×\93 ×©×¤×\94 ×©שמות שפות מתורגמות (מאמץ טוב ביותר) ושמות עיצובים.",
+       "apihelp-query+siteinfo-param-inlanguagecode": "ק×\95×\93 ×©×¤×\94 ×\91ש×\91×\99×\9c שמות שפות מתורגמות (מאמץ טוב ביותר) ושמות עיצובים.",
        "apihelp-query+siteinfo-example-simple": "איזור מידע על האתר.",
        "apihelp-query+siteinfo-example-interwiki": "אחזור תחיליות בינוויקי מקומיות.",
        "apihelp-query+siteinfo-example-replag": "בדיקת שיהוי השכפול הנוכחי.",
index 36b580e..391c06c 100644 (file)
        "apihelp-setpagelanguage-param-lang": "문서를 변경할 언어의 언어 코드입니다. 문서를 위키의 기본 콘텐츠 언어로 재설정하려면 <kbd>default</kbd>를 사용하십시오.",
        "apihelp-setpagelanguage-param-reason": "변경 이유.",
        "apihelp-setpagelanguage-example-language": "<kbd>Main Page</kbd>의 언어를 바스크어로 변경합니다.",
+       "apihelp-stashedit-summary": "공유된 캐시에서 편집을 준비합니다.",
        "apihelp-stashedit-param-sectiontitle": "새 문단을 위한 제목.",
        "apihelp-stashedit-param-text": "문서 내용.",
        "apihelp-stashedit-param-contentmodel": "새 콘텐츠의 콘텐츠 모델.",
index 1dfeb34..a311728 100644 (file)
@@ -11,7 +11,8 @@
                        "Winstonyin",
                        "Arthur2e5",
                        "烈羽",
-                       "Corainn"
+                       "Corainn",
+                       "A2093064"
                ]
        },
        "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|說明文件]]\n* [[mw:API:FAQ|常見問題]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api 郵寄清單]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API公告]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R 報告錯誤及請求功能]\n</div>\n<strong>狀態資訊:</strong>本頁所展示的所有功能都應正常工作,但是API仍在開發當中,將會隨時變化。請訂閱[https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ mediawiki-api-announce 郵件清單]以便得到更新通知。\n\n<strong>錯誤的請求:</strong>當API收到錯誤的請求時,會發出以「MediaWiki-API-Error」為鍵的HTTP頭欄位,隨後頭欄位的值與錯誤碼將會被設為相同的值。詳細資訊請參閱[[mw:API:Errors_and_warnings|API: 錯誤與警告]]。\n\n<strong>測試:</strong>要簡化API請求的測試過程,請見[[Special:ApiSandbox]]。",
@@ -57,7 +58,7 @@
        "apihelp-createaccount-summary": "建立新使用者帳號。",
        "apihelp-createaccount-param-name": "使用者名稱。",
        "apihelp-createaccount-param-password": "密碼 (若有設定 <var>$1mailpassword</var> 則可略過)。",
-       "apihelp-createaccount-param-domain": "å¤\96é\83¨èª\8d証使用的網域 (選填)。",
+       "apihelp-createaccount-param-domain": "å¤\96é\83¨èª\8dè­\89使用的網域 (選填)。",
        "apihelp-createaccount-param-token": "在第一次請求時已取得的帳號建立金鑰。",
        "apihelp-createaccount-param-email": "使用者的電子郵件地址 (選填) 。",
        "apihelp-createaccount-param-realname": "使用者的真實姓名 (選填)。",
index 6684fb9..956c985 100644 (file)
@@ -133,7 +133,7 @@ class AuthenticationResponse {
         */
        public static function newPass( $username = null ) {
                $ret = new AuthenticationResponse;
-               $ret->status = AuthenticationResponse::PASS;
+               $ret->status = self::PASS;
                $ret->username = $username;
                return $ret;
        }
@@ -145,7 +145,7 @@ class AuthenticationResponse {
         */
        public static function newFail( Message $msg ) {
                $ret = new AuthenticationResponse;
-               $ret->status = AuthenticationResponse::FAIL;
+               $ret->status = self::FAIL;
                $ret->message = $msg;
                $ret->messageType = 'error';
                return $ret;
@@ -158,7 +158,7 @@ class AuthenticationResponse {
         */
        public static function newRestart( Message $msg ) {
                $ret = new AuthenticationResponse;
-               $ret->status = AuthenticationResponse::RESTART;
+               $ret->status = self::RESTART;
                $ret->message = $msg;
                return $ret;
        }
@@ -169,7 +169,7 @@ class AuthenticationResponse {
         */
        public static function newAbstain() {
                $ret = new AuthenticationResponse;
-               $ret->status = AuthenticationResponse::ABSTAIN;
+               $ret->status = self::ABSTAIN;
                return $ret;
        }
 
@@ -189,7 +189,7 @@ class AuthenticationResponse {
                }
 
                $ret = new AuthenticationResponse;
-               $ret->status = AuthenticationResponse::UI;
+               $ret->status = self::UI;
                $ret->neededRequests = $reqs;
                $ret->message = $msg;
                $ret->messageType = $msgtype;
@@ -209,7 +209,7 @@ class AuthenticationResponse {
                }
 
                $ret = new AuthenticationResponse;
-               $ret->status = AuthenticationResponse::REDIRECT;
+               $ret->status = self::REDIRECT;
                $ret->neededRequests = $reqs;
                $ret->redirectTarget = $redirectTarget;
                $ret->redirectApiData = $redirectApiData;
index ad1fffb..f9f9a08 100644 (file)
@@ -816,7 +816,7 @@ class MessageCache {
                }
 
                // Normalise title-case input (with some inlining)
-               $lckey = MessageCache::normalizeKey( $key );
+               $lckey = self::normalizeKey( $key );
 
                Hooks::run( 'MessageCache::get', [ &$lckey ] );
 
index cffb59a..99dc899 100644 (file)
@@ -31,8 +31,6 @@ class ChangesFeed {
        public $format, $type, $titleMsg, $descMsg;
 
        /**
-        * Constructor
-        *
         * @param string $format Feed's format (either 'rss' or 'atom')
         * @param string $type Type of feed (for cache keys)
         */
index 30c6995..55cb9ed 100644 (file)
@@ -102,15 +102,17 @@ class EnhancedChangesList extends ChangesList {
                        $rc->mAttribs['rc_timestamp'],
                        $this->getUser()
                );
+               if ( $this->lastdate === '' ) {
+                       $this->lastdate = $date;
+               }
 
                $ret = '';
 
-               # If it's a new day, add the headline and flush the cache
-               if ( $date != $this->lastdate ) {
-                       # Process current cache
+               # If it's a new day, flush the cache and update $this->lastdate
+               if ( $date !== $this->lastdate ) {
+                       # Process current cache (uses $this->lastdate to generate a heading)
                        $ret = $this->recentChangesBlock();
                        $this->rc_cache = [];
-                       $ret .= Xml::element( 'h4', null, $date ) . "\n";
                        $this->lastdate = $date;
                }
 
@@ -763,7 +765,11 @@ class EnhancedChangesList extends ChangesList {
                        }
                }
 
-               return '<div>' . $blockOut . '</div>';
+               if ( $blockOut === '' ) {
+                       return '';
+               }
+               // $this->lastdate is kept up to date by recentChangesLine()
+               return Xml::element( 'h4', null, $this->lastdate ) . "\n<div>" . $blockOut . '</div>';
        }
 
        /**
index e8e35a3..5fad8fd 100644 (file)
@@ -130,7 +130,7 @@ class RecentChange {
                if ( is_array( $type ) ) {
                        $retval = [];
                        foreach ( $type as $t ) {
-                               $retval[] = RecentChange::parseToRCType( $t );
+                               $retval[] = self::parseToRCType( $t );
                        }
 
                        return $retval;
@@ -459,7 +459,7 @@ class RecentChange {
 
                $change = $change instanceof RecentChange
                        ? $change
-                       : RecentChange::newFromId( $change );
+                       : self::newFromId( $change );
 
                if ( !$change instanceof RecentChange ) {
                        return null;
index c9b5f96..2eb9b22 100644 (file)
@@ -643,24 +643,32 @@ class ChangeTags {
         * Handles selecting tags, and filtering.
         * Needs $tables to be set up properly, so we can figure out which join conditions to use.
         *
+        * WARNING: If $filter_tag contains more than one tag, this function will add DISTINCT,
+        * which may cause performance problems for your query unless you put the ID field of your
+        * table at the end of the ORDER BY, and set a GROUP BY equal to the ORDER BY. For example,
+        * if you had ORDER BY foo_timestamp DESC, you will now need GROUP BY foo_timestamp, foo_id
+        * ORDER BY foo_timestamp DESC, foo_id DESC.
+        *
         * @param string|array $tables Table names, see Database::select
         * @param string|array $fields Fields used in query, see Database::select
         * @param string|array $conds Conditions used in query, see Database::select
         * @param array $join_conds Join conditions, see Database::select
-        * @param array $options Options, see Database::select
-        * @param bool|string $filter_tag Tag to select on
+        * @param string|array $options Options, see Database::select
+        * @param string|array $filter_tag Tag(s) to select on
         *
         * @throws MWException When unable to determine appropriate JOIN condition for tagging
         */
        public static function modifyDisplayQuery( &$tables, &$fields, &$conds,
-                                                                               &$join_conds, &$options, $filter_tag = false ) {
-               global $wgRequest, $wgUseTagFilter;
+                                                                               &$join_conds, &$options, $filter_tag = '' ) {
+               global $wgUseTagFilter;
 
-               if ( $filter_tag === false ) {
-                       $filter_tag = $wgRequest->getVal( 'tagfilter' );
-               }
+               // Normalize to arrays
+               $tables = (array)$tables;
+               $fields = (array)$fields;
+               $conds = (array)$conds;
+               $options = (array)$options;
 
-               // Figure out which conditions can be done.
+               // Figure out which ID field to use
                if ( in_array( 'recentchanges', $tables ) ) {
                        $join_cond = 'ct_rc_id=rc_id';
                } elseif ( in_array( 'logging', $tables ) ) {
@@ -683,7 +691,13 @@ class ChangeTags {
 
                        $tables[] = 'change_tag';
                        $join_conds['change_tag'] = [ 'INNER JOIN', $join_cond ];
-                       $conds['ct_tag'] = explode( '|', $filter_tag );
+                       $conds['ct_tag'] = $filter_tag;
+                       if (
+                               is_array( $filter_tag ) && count( $filter_tag ) > 1 &&
+                               !in_array( 'DISTINCT', $options )
+                       ) {
+                               $options[] = 'DISTINCT';
+                       }
                }
        }
 
@@ -962,7 +976,7 @@ class ChangeTags {
 
                // tags cannot contain commas (used as a delimiter in tag_summary table),
                // pipe (used as a delimiter between multiple tags in
-               // modifyDisplayQuery), or slashes (would break tag description messages in
+               // SpecialRecentchanges and friends), or slashes (would break tag description messages in
                // MediaWiki namespace)
                if ( strpos( $tag, ',' ) !== false || strpos( $tag, '|' ) !== false
                        || strpos( $tag, '/' ) !== false ) {
index e2f82d4..d93362b 100644 (file)
@@ -535,7 +535,7 @@ class IcuCollation extends Collation {
         * @return string|bool
         */
        static function getUnicodeVersionForICU() {
-               $icuVersion = IcuCollation::getICUVersion();
+               $icuVersion = self::getICUVersion();
                if ( !$icuVersion ) {
                        return false;
                }
index 2d2ca47..8dd7a38 100644 (file)
@@ -40,8 +40,6 @@ class NumericUppercaseCollation extends UppercaseCollation {
        private $digitTransformLang;
 
        /**
-        * Constructor
-        *
         * @param $lang Language How to convert digits.
         *  For example, if given language "my" than ၇ is treated like 7.
         *
index c57eba7..6605c38 100644 (file)
@@ -243,7 +243,7 @@ class EtcdConfig implements Config, LoggerAwareInterface {
 
                $info = json_decode( $rbody, true );
                if ( $info === null || !isset( $info['node']['nodes'] ) ) {
-                       return [ null, $rcode, "Unexpected JSON response; missing 'nodes' list.", false ];
+                       return [ null, "Unexpected JSON response; missing 'nodes' list.", false ];
                }
 
                $config = [];
index f85b00d..8603360 100644 (file)
@@ -136,7 +136,7 @@ abstract class ContentHandler {
                        $modelId = $title->getContentModel();
                }
 
-               $handler = ContentHandler::getForModelID( $modelId );
+               $handler = self::getForModelID( $modelId );
 
                return $handler->unserializeContent( $text, $format );
        }
@@ -240,7 +240,7 @@ abstract class ContentHandler {
        public static function getForTitle( Title $title ) {
                $modelId = $title->getContentModel();
 
-               return ContentHandler::getForModelID( $modelId );
+               return self::getForModelID( $modelId );
        }
 
        /**
@@ -256,7 +256,7 @@ abstract class ContentHandler {
        public static function getForContent( Content $content ) {
                $modelId = $content->getModel();
 
-               return ContentHandler::getForModelID( $modelId );
+               return self::getForModelID( $modelId );
        }
 
        /**
@@ -293,8 +293,8 @@ abstract class ContentHandler {
        public static function getForModelID( $modelId ) {
                global $wgContentHandlers;
 
-               if ( isset( ContentHandler::$handlers[$modelId] ) ) {
-                       return ContentHandler::$handlers[$modelId];
+               if ( isset( self::$handlers[$modelId] ) ) {
+                       return self::$handlers[$modelId];
                }
 
                if ( empty( $wgContentHandlers[$modelId] ) ) {
@@ -327,9 +327,9 @@ abstract class ContentHandler {
                wfDebugLog( 'ContentHandler', 'Created handler for ' . $modelId
                        . ': ' . get_class( $handler ) );
 
-               ContentHandler::$handlers[$modelId] = $handler;
+               self::$handlers[$modelId] = $handler;
 
-               return ContentHandler::$handlers[$modelId];
+               return self::$handlers[$modelId];
        }
 
        /**
@@ -372,7 +372,7 @@ abstract class ContentHandler {
                $formats = [];
 
                foreach ( $wgContentHandlers as $model => $class ) {
-                       $handler = ContentHandler::getForModelID( $model );
+                       $handler = self::getForModelID( $model );
                        $formats = array_merge( $formats, $handler->getSupportedFormats() );
                }
 
index 0d0c149..6e3eda6 100644 (file)
@@ -75,7 +75,6 @@ class DerivativeContext extends ContextSource implements MutableContext {
        private $timing;
 
        /**
-        * Constructor
         * @param IContextSource $context Context to inherit from
         */
        public function __construct( IContextSource $context ) {
index 6d18444..3d22c03 100644 (file)
@@ -46,8 +46,6 @@ class CloneDatabase {
        private $db;
 
        /**
-        * Constructor
-        *
         * @param IMaintainableDatabase $db A database subclass
         * @param array $tablesToClone An array of tables to clone, unprefixed
         * @param string $newTablePrefix Prefix to assign to the tables
index e67a0b3..012837f 100644 (file)
@@ -425,7 +425,7 @@ class MWDebug {
                $html = '';
 
                if ( self::$enabled ) {
-                       MWDebug::log( 'MWDebug output complete' );
+                       self::log( 'MWDebug output complete' );
                        $debugInfo = self::getDebugInfo( $context );
 
                        // Cannot use OutputPage::addJsConfigVars because those are already outputted
@@ -495,7 +495,7 @@ class MWDebug {
                        }
                }
 
-               MWDebug::log( 'MWDebug output complete' );
+               self::log( 'MWDebug output complete' );
                $debugInfo = self::getDebugInfo( $context );
 
                ApiResult::setIndexedTagName( $debugInfo, 'debuginfo' );
index 072c1e0..c6facd9 100644 (file)
@@ -102,8 +102,6 @@ class LinksUpdate extends DataUpdate implements EnqueueableDataUpdate {
        private $db;
 
        /**
-        * Constructor
-        *
         * @param Title $title Title of the page we're updating
         * @param ParserOutput $parserOutput Output from a full parse of this page
         * @param bool $recursive Queue jobs for recursive updates?
index b9a259b..2766bcb 100644 (file)
@@ -44,8 +44,6 @@ class SearchUpdate implements DeferrableUpdate {
        private $page;
 
        /**
-        * Constructor
-        *
         * @param int $id Page id to update
         * @param Title|string $title Title of page to update
         * @param Content|string|bool $c Content of the page to update. Default: false.
index 95420d9..7f9af60 100644 (file)
@@ -104,7 +104,6 @@ class DifferenceEngine extends ContextSource {
        /**#@-*/
 
        /**
-        * Constructor
         * @param IContextSource $context Context to use, anything else will be ignored
         * @param int $old Old ID we want to show and diff with.
         * @param string|int $new Either revision ID or 'prev' or 'next'. Default: 0.
@@ -1175,17 +1174,17 @@ class DifferenceEngine extends ContextSource {
 
                if ( !$diff && !$otitle ) {
                        $header .= "
-                       <tr style='vertical-align: top;' lang='{$userLang}'>
-                       <td class='diff-ntitle'>{$ntitle}</td>
+                       <tr style=\"vertical-align: top;\" lang=\"{$userLang}\">
+                       <td class=\"diff-ntitle\">{$ntitle}</td>
                        </tr>";
                        $multiColspan = 1;
                } else {
                        if ( $diff ) { // Safari/Chrome show broken output if cols not used
                                $header .= "
-                               <col class='diff-marker' />
-                               <col class='diff-content' />
-                               <col class='diff-marker' />
-                               <col class='diff-content' />";
+                               <col class=\"diff-marker\" />
+                               <col class=\"diff-content\" />
+                               <col class=\"diff-marker\" />
+                               <col class=\"diff-content\" />";
                                $colspan = 2;
                                $multiColspan = 4;
                        } else {
@@ -1194,20 +1193,20 @@ class DifferenceEngine extends ContextSource {
                        }
                        if ( $otitle || $ntitle ) {
                                $header .= "
-                               <tr style='vertical-align: top;' lang='{$userLang}'>
-                               <td colspan='$colspan' class='diff-otitle'>{$otitle}</td>
-                               <td colspan='$colspan' class='diff-ntitle'>{$ntitle}</td>
+                               <tr style=\"vertical-align: top;\" lang=\"{$userLang}\">
+                               <td colspan=\"$colspan\" class=\"diff-otitle\">{$otitle}</td>
+                               <td colspan=\"$colspan\" class=\"diff-ntitle\">{$ntitle}</td>
                                </tr>";
                        }
                }
 
                if ( $multi != '' ) {
-                       $header .= "<tr><td colspan='{$multiColspan}' style='text-align: center;' " .
-                               "class='diff-multi' lang='{$userLang}'>{$multi}</td></tr>";
+                       $header .= "<tr><td colspan=\"{$multiColspan}\" style=\"text-align: center;\" " .
+                               "class=\"diff-multi\" lang=\"{$userLang}\">{$multi}</td></tr>";
                }
                if ( $notice != '' ) {
-                       $header .= "<tr><td colspan='{$multiColspan}' style='text-align: center;' " .
-                               "lang='{$userLang}'>{$notice}</td></tr>";
+                       $header .= "<tr><td colspan=\"{$multiColspan}\" style=\"text-align: center;\" " .
+                               "lang=\"{$userLang}\">{$notice}</td></tr>";
                }
 
                return $header . $diff . "</table>";
index 48bc3bd..f464d8a 100644 (file)
@@ -31,8 +31,6 @@ class HttpError extends MWException {
        private $httpCode, $header, $content;
 
        /**
-        * Constructor
-        *
         * @param int $httpCode HTTP status code to send to the client
         * @param string|Message $content Content of the message
         * @param string|Message|null $header Content of the header (\<title\> and \<h1\>)
index a307468..943aa04 100644 (file)
@@ -91,8 +91,8 @@ class WikiExporter {
         * @param int $buffer One of WikiExporter::BUFFER or WikiExporter::STREAM
         * @param int $text One of WikiExporter::TEXT or WikiExporter::STUB
         */
-       function __construct( $db, $history = WikiExporter::CURRENT,
-                       $buffer = WikiExporter::BUFFER, $text = WikiExporter::TEXT ) {
+       function __construct( $db, $history = self::CURRENT,
+                       $buffer = self::BUFFER, $text = self::TEXT ) {
                $this->db = $db;
                $this->history = $history;
                $this->buffer = $buffer;
@@ -272,7 +272,7 @@ class WikiExporter {
                        # Get logging table name for logging.* clause
                        $logging = $this->db->tableName( 'logging' );
 
-                       if ( $this->buffer == WikiExporter::STREAM ) {
+                       if ( $this->buffer == self::STREAM ) {
                                $prev = $this->db->bufferResults( false );
                        }
                        $result = null; // Assuring $result is not undefined, if exception occurs early
@@ -284,7 +284,7 @@ class WikiExporter {
                                        [ 'ORDER BY' => 'log_id', 'USE INDEX' => [ 'logging' => 'PRIMARY' ] ]
                                );
                                $this->outputLogStream( $result );
-                               if ( $this->buffer == WikiExporter::STREAM ) {
+                               if ( $this->buffer == self::STREAM ) {
                                        $this->db->bufferResults( $prev );
                                }
                        } catch ( Exception $e ) {
@@ -303,7 +303,7 @@ class WikiExporter {
 
                                // Putting database back in previous buffer mode
                                try {
-                                       if ( $this->buffer == WikiExporter::STREAM ) {
+                                       if ( $this->buffer == self::STREAM ) {
                                                $this->db->bufferResults( $prev );
                                        }
                                } catch ( Exception $e2 ) {
@@ -341,10 +341,10 @@ class WikiExporter {
                                if ( !empty( $this->history['limit'] ) ) {
                                        $opts['LIMIT'] = intval( $this->history['limit'] );
                                }
-                       } elseif ( $this->history & WikiExporter::FULL ) {
+                       } elseif ( $this->history & self::FULL ) {
                                # Full history dumps...
                                # query optimization for history stub dumps
-                               if ( $this->text == WikiExporter::STUB && $orderRevs ) {
+                               if ( $this->text == self::STUB && $orderRevs ) {
                                        $tables = [ 'revision', 'page' ];
                                        $opts[] = 'STRAIGHT_JOIN';
                                        $opts['ORDER BY'] = [ 'rev_page ASC', 'rev_id ASC' ];
@@ -353,13 +353,13 @@ class WikiExporter {
                                } else {
                                        $join['revision'] = [ 'INNER JOIN', 'page_id=rev_page' ];
                                }
-                       } elseif ( $this->history & WikiExporter::CURRENT ) {
+                       } elseif ( $this->history & self::CURRENT ) {
                                # Latest revision dumps...
                                if ( $this->list_authors && $cond != '' ) { // List authors, if so desired
                                        $this->do_list_authors( $cond );
                                }
                                $join['revision'] = [ 'INNER JOIN', 'page_id=rev_page AND page_latest=rev_id' ];
-                       } elseif ( $this->history & WikiExporter::STABLE ) {
+                       } elseif ( $this->history & self::STABLE ) {
                                # "Stable" revision dumps...
                                # Default JOIN, to be overridden...
                                $join['revision'] = [ 'INNER JOIN', 'page_id=rev_page AND page_latest=rev_id' ];
@@ -367,7 +367,7 @@ class WikiExporter {
                                if ( Hooks::run( 'WikiExporter::dumpStableQuery', [ &$tables, &$opts, &$join ] ) ) {
                                        throw new MWException( __METHOD__ . " given invalid history dump type." );
                                }
-                       } elseif ( $this->history & WikiExporter::RANGE ) {
+                       } elseif ( $this->history & self::RANGE ) {
                                # Dump of revisions within a specified range
                                $join['revision'] = [ 'INNER JOIN', 'page_id=rev_page' ];
                                $opts['ORDER BY'] = [ 'rev_page ASC', 'rev_id ASC' ];
@@ -381,12 +381,12 @@ class WikiExporter {
                                $opts['USE INDEX']['page'] = 'PRIMARY';
                        }
                        # Build text join options
-                       if ( $this->text != WikiExporter::STUB ) { // 1-pass
+                       if ( $this->text != self::STUB ) { // 1-pass
                                $tables[] = 'text';
                                $join['text'] = [ 'INNER JOIN', 'rev_text_id=old_id' ];
                        }
 
-                       if ( $this->buffer == WikiExporter::STREAM ) {
+                       if ( $this->buffer == self::STREAM ) {
                                $prev = $this->db->bufferResults( false );
                        }
                        $result = null; // Assuring $result is not undefined, if exception occurs early
@@ -399,7 +399,7 @@ class WikiExporter {
                                # Output dump results
                                $this->outputPageStream( $result );
 
-                               if ( $this->buffer == WikiExporter::STREAM ) {
+                               if ( $this->buffer == self::STREAM ) {
                                        $this->db->bufferResults( $prev );
                                }
                        } catch ( Exception $e ) {
@@ -418,7 +418,7 @@ class WikiExporter {
 
                                // Putting database back in previous buffer mode
                                try {
-                                       if ( $this->buffer == WikiExporter::STREAM ) {
+                                       if ( $this->buffer == self::STREAM ) {
                                                $this->db->bufferResults( $prev );
                                        }
                                } catch ( Exception $e2 ) {
index 43f1d21..56ccc64 100644 (file)
@@ -528,7 +528,7 @@ class ForeignAPIRepo extends FileRepo {
                }
 
                $req = MWHttpRequest::factory( $url, $options, __METHOD__ );
-               $req->setUserAgent( ForeignAPIRepo::getUserAgent() );
+               $req->setUserAgent( self::getUserAgent() );
                $status = $req->execute();
 
                if ( $status->isOK() ) {
index 399147b..d4351e0 100644 (file)
@@ -285,7 +285,7 @@ class HTMLForm extends ContextSource {
                                return ObjectFactory::constructClassInstance( OOUIHTMLForm::class, $arguments );
                        default:
                                /** @var HTMLForm $form */
-                               $form = ObjectFactory::constructClassInstance( HTMLForm::class, $arguments );
+                               $form = ObjectFactory::constructClassInstance( self::class, $arguments );
                                $form->setDisplayFormat( $displayFormat );
                                return $form;
                }
@@ -400,7 +400,13 @@ class HTMLForm extends ContextSource {
 
                if ( !in_array( $format, $this->availableDisplayFormats, true ) ) {
                        throw new MWException( 'Display format must be one of ' .
-                               print_r( $this->availableDisplayFormats, true ) );
+                               print_r(
+                                       array_merge(
+                                               $this->availableDisplayFormats,
+                                               $this->availableSubclassDisplayFormats
+                                       ),
+                                       true
+                               ) );
                }
 
                // Evil hack for mobile :(
index 4f21ce2..c10b312 100644 (file)
@@ -106,7 +106,7 @@ class Http {
                        $options['timeout'] = $args[1];
                        $caller = __METHOD__;
                }
-               return Http::request( 'GET', $url, $options, $caller );
+               return self::request( 'GET', $url, $options, $caller );
        }
 
        /**
@@ -119,7 +119,7 @@ class Http {
         * @return string|bool false on error
         */
        public static function post( $url, $options = [], $caller = __METHOD__ ) {
-               return Http::request( 'POST', $url, $options, $caller );
+               return self::request( 'POST', $url, $options, $caller );
        }
 
        /**
index 8034400..94a2b93 100644 (file)
@@ -93,7 +93,7 @@ class ImportStreamSource implements ImportSource {
                }
                $fname = $upload['tmp_name'];
                if ( is_uploaded_file( $fname ) ) {
-                       return ImportStreamSource::newFromFile( $fname );
+                       return self::newFromFile( $fname );
                } else {
                        return Status::newFatal( 'importnofile' );
                }
@@ -178,6 +178,6 @@ class ImportStreamSource implements ImportSource {
 
                $url = wfAppendQuery( $link, $params );
                # For interwikis, use POST to avoid redirects.
-               return ImportStreamSource::newFromURL( $url, "POST" );
+               return self::newFromURL( $url, "POST" );
        }
 }
index e5cbb7c..7b6ac5e 100644 (file)
@@ -83,6 +83,7 @@ abstract class DatabaseUpdater {
                FixDefaultJsonContentPages::class,
                CleanupEmptyCategories::class,
                AddRFCAndPMIDInterwiki::class,
+               PopulatePPSortKey::class
        ];
 
        /**
@@ -105,8 +106,6 @@ abstract class DatabaseUpdater {
        protected $holdContentHandlerUseDB = true;
 
        /**
-        * Constructor
-        *
         * @param Database $db To perform updates on
         * @param bool $shared Whether to perform updates on shared tables
         * @param Maintenance $maintenance Maintenance object which created us
index a311ce9..27300f3 100644 (file)
@@ -703,7 +703,7 @@ class WebInstaller extends Installer {
                        "<span class=\"config-help-field-hint\" title=\"" .
                        wfMessage( 'config-help-tooltip' )->escaped() . "\">" .
                        wfMessage( 'config-help' )->escaped() . "</span>\n" .
-                       "<span class=\"config-help-field-data\">" . $html . "</span>\n" .
+                       "<div class=\"config-help-field-data\">" . $html . "</div>\n" .
                        "</div>\n";
        }
 
index 2ba47f1..defaf1b 100644 (file)
@@ -1,4 +1,64 @@
 {
-       "@metadata": [],
-       "mainpagetext": "'''MediaWiki òsta zainstalowónô.'''"
+       "@metadata": {
+               "authors": [
+                       "Kaszeba"
+               ]
+       },
+       "config-desc": "Jinstalownik MediaWiki",
+       "config-title": "Jinstalowanié MediaWiki $1",
+       "config-information": "Wëdowiédzô",
+       "config-localsettings-key": "Klucz aktualizacëji:",
+       "config-localsettings-badkey": "Lëchi klucz aktualizacëji.",
+       "config-session-error": "Fela zrëszeniô sesëji – $1",
+       "config-your-language": "Twój jãzëk:",
+       "config-your-language-help": "Wybierzë jãzëk procesu jinstalacëji.",
+       "config-wiki-language": "Jãzëk wiki:",
+       "config-back": "← Nazôd",
+       "config-continue": "Dali →",
+       "config-page-language": "Jãzëk",
+       "config-page-welcome": "Witómë w MediaWiki!",
+       "config-page-dbconnect": "Sparłãczë z bazą pòdôwków",
+       "config-page-upgrade": "Zaktualnienié jinstalacëji",
+       "config-page-dbsettings": "Nastôw bazë pòdôwków",
+       "config-page-name": "Miono",
+       "config-page-options": "Òptacëje",
+       "config-page-install": "Wjinstalëjë",
+       "config-page-complete": "Fardich!",
+       "config-page-restart": "Zrëszë jinstalacëjã znowa",
+       "config-page-readme": "Spòdlowô wëdowiédzô",
+       "config-page-releasenotes": "Wëdowiédzô ò wersëji",
+       "config-page-copying": "Kòpérowanié",
+       "config-page-upgradedoc": "Zaktualnienié",
+       "config-page-existingwiki": "Egyzstëjącô wiki",
+       "config-restart": "Jo, zrëszë znowa",
+       "config-env-php": "PHP $1 je wjinastalowóné",
+       "config-env-hhvm": "HHVM $1 je wjinastalowóné",
+       "config-memory-raised": "Paraméter PHP <code>memory_limit</code> $1 òstôł zwikszony do $2.",
+       "config-xcache": "[Http://trac.lighttpd.net/xcache/ XCache] je wjinstalowóny",
+       "config-apc": "[Http://www.php.net/apc APC] je wjinstalowóny",
+       "config-apcu": "[http://www.php.net/apcu APCu] je wjinstalowóny",
+       "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] je wjinstalowóny",
+       "config-diff3-bad": "Felënk GNU diff3.",
+       "config-mysql-innodb": "InnoDB",
+       "config-mysql-myisam": "MyISAM",
+       "config-mysql-binary": "binarny",
+       "config-mysql-utf8": "UTF‐8",
+       "config-site-name": "Miono wiki:",
+       "config-site-name-blank": "Wpiszë miono starnów.",
+       "config-ns-other-default": "MòjôWiki",
+       "config-admin-box": "Kònto sprôwnika",
+       "config-admin-name": "Twòjé miono brëkòwnika:",
+       "config-admin-password": "Parola:",
+       "config-admin-password-confirm": "Pòwtórzë parolã:",
+       "config-admin-name-blank": "Wpiszë miono brëkòwnika, chtëren bãdze sprôwnikã.",
+       "config-admin-email": "E-mailowô adresa:",
+       "config-license-none": "Felënk stopczi z licencëją",
+       "config-email-settings": "Nastôwë e-mail",
+       "config-logo": "Adresa URL logo:",
+       "config-skins": "Wëzdrzatk",
+       "config-skins-use-as-default": "Ùżëjë tegò wëzdrzatkù za domëslny",
+       "config-install-step-done": "fardich",
+       "config-install-step-failed": "nie dzrzëło sã",
+       "config-help": "pòmòc",
+       "mainpagetext": "<strong>MediaWiki òsta wjinstalowónô.<strong>"
 }
index 71b50de..61acd3e 100644 (file)
@@ -6,7 +6,8 @@
                        "SNN95",
                        "MaxSem",
                        "Aviator",
-                       "Macofe"
+                       "Macofe",
+                       "Jeluang Terluang"
                ]
        },
        "config-desc": "Pemasang MediaWiki",
@@ -56,7 +57,7 @@
        "config-outdated-sqlite": "<strong>Amaran:</strong> anda mempunyai SQLite $1 yang lebih rendah daripada versi keperluan minimum $1. SQLite tidak akan disediakan.",
        "config-no-fts3": "<strong>Amaran:</strong> SQLite disusun tanpa [//sqlite.org/fts3.html modil FTS3], maka ciri-ciri pencarian tidak akan disediakan pada backend ini.",
        "config-pcre-old": "<strong>Amaran keras:</strong> PCRE $1 ke atas diperlukan.\nBinari PHP anda berpaut dengan PCRE $2.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/PCRE Keterangan lanjut].",
-       "config-memory-bad": "<strong>Amaran:</strong> <code>memory_limit</code> (Had memori) PHP adalah $1.\nIni mungkin terlalu rendah.\nPemasangan mungkin akan gagal!",
+       "config-memory-bad": "<strong>Amaran:</strong> <code>memory_limit</code> (Had memori) PHP ialah $1.\nIni mungkin terlalu rendah.\nPemasangan mungkin akan gagal!",
        "config-xcache": "[http://xcache.lighttpd.net/ XCache] dipasang",
        "config-apc": "[http://www.php.net/apc APC] dipasang",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] dipasang",
@@ -97,7 +98,7 @@
        "config-mysql-engine": "Enjin storan:",
        "config-mysql-innodb": "InnoDB",
        "config-mysql-myisam": "MyISAM",
-       "config-mysql-only-myisam-dep": "<strong>Amaran:</strong> MyISAM adalah satu-satunya enjin storan yang terdapat untuk MySQL di mesin ini, dan penggunaannya dengan MediaWiki tidak digalakkan kerana:\n* ia tidak menyokong keserempakan (''concurrency'') disebabkan penguncian jadual\n* ia lebih terdedah kepada korupsi daripada enjin-enjin lain\n* pangkalan kod MediaWiki tidak sentiasa mengendalikan MyISAM seperti yang diharapkan\n\nPemasangan MySQL anda tidak menyokong InnoDB. Mungkin tiba masanya untuk naik taraf.",
+       "config-mysql-only-myisam-dep": "<strong>Amaran:</strong> MyISAM ialah satu-satunya enjin storan yang terdapat untuk MySQL di mesin ini, dan penggunaannya dengan MediaWiki tidak digalakkan kerana:\n* ia tidak menyokong keserempakan (''concurrency'') disebabkan penguncian jadual\n* ia lebih terdedah kepada korupsi daripada enjin-enjin lain\n* pangkalan kod MediaWiki tidak sentiasa mengendalikan MyISAM seperti yang diharapkan\n\nPemasangan MySQL anda tidak menyokong InnoDB. Mungkin tiba masanya untuk naik taraf.",
        "config-mysql-charset": "Peranggu aksara pangkalan data:",
        "config-mysql-binary": "Perduaan",
        "config-mysql-utf8": "UTF-8",
index d20a233..1f4f179 100644 (file)
@@ -378,7 +378,7 @@ abstract class JobQueue {
                // Flag this job as an old duplicate based on its "root" job...
                try {
                        if ( $job && $this->isRootJobOldDuplicate( $job ) ) {
-                               JobQueue::incrStats( 'dupe_pops', $this->type );
+                               self::incrStats( 'dupe_pops', $this->type );
                                $job = DuplicateJob::newFromJob( $job ); // convert to a no-op
                        }
                } catch ( Exception $e ) {
index 7fdd617..44721d9 100644 (file)
@@ -28,7 +28,7 @@
  * For example, one can set $wgJobTypeConf['refreshLinks'] to point to a
  * JobQueueFederated instance, which itself would consist of three JobQueueRedis
  * instances, each using their own redis server. This would allow for the jobs
- * to be split (evenly or based on weights) accross multiple servers if a single
+ * to be split (evenly or based on weights) across multiple servers if a single
  * server becomes impractical or expensive. Different JobQueue classes can be mixed.
  *
  * The basic queue configuration (e.g. "order", "claimTTL") of a federated queue
diff --git a/includes/jobqueue/JobQueueSecondTestQueue.php b/includes/jobqueue/JobQueueSecondTestQueue.php
new file mode 100644 (file)
index 0000000..a1935df
--- /dev/null
@@ -0,0 +1,280 @@
+<?php
+
+/**
+ * A wrapper for the JobQueue that delegates all the method calls to a single,
+ * main queue, and also pushes all the jobs to a second job queue that's being
+ * debugged.
+ *
+ * This class was temporary added to test transitioning to the JobQueueEventBus
+ * and will removed after the transition is completed. This code is only needed
+ * while we are testing the new infrastructure to be able to compare the results
+ * between the queue implementations and make sure that they process the same jobs,
+ * deduplicate correctly, compare the delays, backlogs and make sure no jobs are lost.
+ * When the new infrastructure is well tested this will not be needed any more.
+ *
+ * @deprecated since 1.30
+ * @since 1.30
+ */
+class JobQueueSecondTestQueue extends JobQueue {
+
+       /**
+        * @var JobQueue
+        */
+       private $mainQueue;
+
+       /**
+        * @var JobQueue
+        */
+       private $debugQueue;
+
+       protected function __construct( array $params ) {
+               if ( !isset( $params['mainqueue'] ) ) {
+                       throw new MWException( "mainqueue parameter must be provided to the debug queue" );
+               }
+
+               if ( !isset( $params['debugqueue'] ) ) {
+                       throw new MWException( "debugqueue parameter must be provided to the debug queue" );
+               }
+
+               $conf = [ 'wiki' => $params['wiki'], 'type' => $params['type'] ];
+               $this->mainQueue = JobQueue::factory( $params['mainqueue'] + $conf );
+               $this->debugQueue = JobQueue::factory( $params['debugqueue'] + $conf );
+
+               // We need to construct parent after creating the main and debug queue
+               // because super constructor calls some methods we delegate to the main queue.
+               parent::__construct( $params );
+       }
+
+       /**
+        * Get the allowed queue orders for configuration validation
+        *
+        * @return array Subset of (random, timestamp, fifo, undefined)
+        */
+       protected function supportedOrders() {
+               return $this->mainQueue->supportedOrders();
+       }
+
+       /**
+        * Get the default queue order to use if configuration does not specify one
+        *
+        * @return string One of (random, timestamp, fifo, undefined)
+        */
+       protected function optimalOrder() {
+               return $this->mainQueue->optimalOrder();
+       }
+
+       /**
+        * Find out if delayed jobs are supported for configuration validation
+        *
+        * @return bool Whether delayed jobs are supported
+        */
+       protected function supportsDelayedJobs() {
+               return $this->mainQueue->supportsDelayedJobs();
+       }
+
+       /**
+        * @see JobQueue::isEmpty()
+        * @return bool
+        */
+       protected function doIsEmpty() {
+               return $this->mainQueue->doIsEmpty();
+       }
+
+       /**
+        * @see JobQueue::getSize()
+        * @return int
+        */
+       protected function doGetSize() {
+               return $this->mainQueue->doGetSize();
+       }
+
+       /**
+        * @see JobQueue::getAcquiredCount()
+        * @return int
+        */
+       protected function doGetAcquiredCount() {
+               return $this->mainQueue->doGetAcquiredCount();
+       }
+
+       /**
+        * @see JobQueue::getDelayedCount()
+        * @return int
+        */
+       protected function doGetDelayedCount() {
+               return $this->mainQueue->doGetDelayedCount();
+       }
+
+       /**
+        * @see JobQueue::getAbandonedCount()
+        * @return int
+        */
+       protected function doGetAbandonedCount() {
+               return $this->mainQueue->doGetAbandonedCount();
+       }
+
+       /**
+        * @see JobQueue::batchPush()
+        * @param IJobSpecification[] $jobs
+        * @param int $flags
+        */
+       protected function doBatchPush( array $jobs, $flags ) {
+               $this->mainQueue->doBatchPush( $jobs, $flags );
+
+               try {
+                       $this->debugQueue->doBatchPush( $jobs, $flags );
+               } catch ( Exception $exception ) {
+                       MWExceptionHandler::logException( $exception );
+               }
+       }
+
+       /**
+        * @see JobQueue::pop()
+        * @return Job|bool
+        */
+       protected function doPop() {
+               return $this->mainQueue->doPop();
+       }
+
+       /**
+        * @see JobQueue::ack()
+        * @param Job $job
+        */
+       protected function doAck( Job $job ) {
+               return $this->mainQueue->doAck( $job );
+       }
+
+       /**
+        * @see JobQueue::deduplicateRootJob()
+        * @param IJobSpecification $job
+        * @throws MWException
+        * @return bool
+        */
+       protected function doDeduplicateRootJob( IJobSpecification $job ) {
+               return $this->mainQueue->doDeduplicateRootJob( $job );
+       }
+
+       /**
+        * @see JobQueue::isRootJobOldDuplicate()
+        * @param Job $job
+        * @return bool
+        */
+       protected function doIsRootJobOldDuplicate( Job $job ) {
+               return $this->mainQueue->doIsRootJobOldDuplicate( $job );
+       }
+
+       /**
+        * @param string $signature Hash identifier of the root job
+        * @return string
+        */
+       protected function getRootJobCacheKey( $signature ) {
+               return $this->mainQueue->getRootJobCacheKey( $signature );
+       }
+
+       /**
+        * @see JobQueue::delete()
+        * @throws MWException
+        */
+       protected function doDelete() {
+               return $this->mainQueue->doDelete();
+       }
+
+       /**
+        * @see JobQueue::waitForBackups()
+        * @return void
+        */
+       protected function doWaitForBackups() {
+               $this->mainQueue->doWaitForBackups();
+       }
+
+       /**
+        * @see JobQueue::flushCaches()
+        * @return void
+        */
+       protected function doFlushCaches() {
+               $this->mainQueue->doFlushCaches();
+       }
+
+       /**
+        * Get an iterator to traverse over all available jobs in this queue.
+        * This does not include jobs that are currently acquired or delayed.
+        * Note: results may be stale if the queue is concurrently modified.
+        *
+        * @return Iterator
+        * @throws JobQueueError
+        */
+       public function getAllQueuedJobs() {
+               return $this->mainQueue->getAllQueuedJobs();
+       }
+
+       /**
+        * Get an iterator to traverse over all delayed jobs in this queue.
+        * Note: results may be stale if the queue is concurrently modified.
+        *
+        * @return Iterator
+        * @throws JobQueueError
+        * @since 1.22
+        */
+       public function getAllDelayedJobs() {
+               return $this->mainQueue->getAllDelayedJobs();
+       }
+
+       /**
+        * Get an iterator to traverse over all claimed jobs in this queue
+        *
+        * Callers should be quick to iterator over it or few results
+        * will be returned due to jobs being acknowledged and deleted
+        *
+        * @return Iterator
+        * @throws JobQueueError
+        * @since 1.26
+        */
+       public function getAllAcquiredJobs() {
+               return $this->mainQueue->getAllAcquiredJobs();
+       }
+
+       /**
+        * Get an iterator to traverse over all abandoned jobs in this queue
+        *
+        * @return Iterator
+        * @throws JobQueueError
+        * @since 1.25
+        */
+       public function getAllAbandonedJobs() {
+               return $this->mainQueue->getAllAbandonedJobs();
+       }
+
+       /**
+        * Do not use this function outside of JobQueue/JobQueueGroup
+        *
+        * @return string
+        * @since 1.22
+        */
+       public function getCoalesceLocationInternal() {
+               return $this->mainQueue->getCoalesceLocationInternal();
+       }
+
+       /**
+        * @see JobQueue::getSiblingQueuesWithJobs()
+        * @param array $types List of queues types
+        * @return array|null (list of queue types) or null if unsupported
+        */
+       protected function doGetSiblingQueuesWithJobs( array $types ) {
+               return $this->mainQueue->doGetSiblingQueuesWithJobs( $types );
+       }
+
+       /**
+        * @see JobQueue::getSiblingQueuesSize()
+        * @param array $types List of queues types
+        * @return array|null (list of queue types) or null if unsupported
+        */
+       protected function doGetSiblingQueueSizes( array $types ) {
+               return $this->mainQueue->doGetSiblingQueueSizes( $types );
+       }
+
+       /**
+        * @throws JobQueueReadOnlyError
+        */
+       protected function assertNotReadOnly() {
+               $this->mainQueue->assertNotReadOnly();
+       }
+}
index ea0f1b7..4c672f4 100644 (file)
@@ -78,7 +78,12 @@ class CSSMin {
                                $url = $match['file'][0];
 
                                // Skip fully-qualified and protocol-relative URLs and data URIs
-                               if ( substr( $url, 0, 2 ) === '//' || parse_url( $url, PHP_URL_SCHEME ) ) {
+                               // Also skips the rare `behavior` property specifying application's default behavior
+                               if (
+                                       substr( $url, 0, 2 ) === '//' ||
+                                       parse_url( $url, PHP_URL_SCHEME ) ||
+                                       substr( $url, 0, 9 ) === '#default#'
+                               ) {
                                        break;
                                }
 
@@ -183,17 +188,7 @@ class CSSMin {
                        return self::$mimeTypes[$ext];
                }
 
-               $realpath = realpath( $file );
-               if (
-                       $realpath
-                       && function_exists( 'finfo_file' )
-                       && function_exists( 'finfo_open' )
-                       && defined( 'FILEINFO_MIME_TYPE' )
-               ) {
-                       return finfo_file( finfo_open( FILEINFO_MIME_TYPE ), $realpath );
-               }
-
-               return false;
+               return mime_content_type( realpath( $file ) );
        }
 
        /**
@@ -252,7 +247,7 @@ class CSSMin {
                // quotation marks (e.g. "foo /* bar").
                $comments = [];
 
-               $pattern = '/(?!' . CSSMin::EMBED_REGEX . ')(' . CSSMin::COMMENT_REGEX . ')/s';
+               $pattern = '/(?!' . self::EMBED_REGEX . ')(' . self::COMMENT_REGEX . ')/s';
 
                $source = preg_replace_callback(
                        $pattern,
@@ -355,7 +350,7 @@ class CSSMin {
                        }, $source );
 
                // Re-insert comments
-               $pattern = '/' . CSSMin::PLACEHOLDER . '(\d+)x/';
+               $pattern = '/' . self::PLACEHOLDER . '(\d+)x/';
                $source = preg_replace_callback( $pattern, function ( $match ) use ( &$comments ) {
                        return $comments[ $match[1] ];
                }, $source );
@@ -474,7 +469,12 @@ class CSSMin {
 
                // Pass thru fully-qualified and protocol-relative URLs and data URIs, as well as local URLs if
                // we can't expand them.
-               if ( self::isRemoteUrl( $url ) || self::isLocalUrl( $url ) ) {
+               // Also skips the rare `behavior` property specifying application's default behavior
+               if (
+                       self::isRemoteUrl( $url ) ||
+                       self::isLocalUrl( $url ) ||
+                       substr( $url, 0, 9 ) === '#default#'
+               ) {
                        return $url;
                }
 
index e8b0e6a..b22f06d 100644 (file)
@@ -549,7 +549,7 @@ class IP {
         */
        private static function parseCIDR6( $range ) {
                # Explode into <expanded IP,range>
-               $parts = explode( '/', IP::sanitizeIP( $range ), 2 );
+               $parts = explode( '/', self::sanitizeIP( $range ), 2 );
                if ( count( $parts ) != 2 ) {
                        return [ false, false ];
                }
@@ -590,7 +590,7 @@ class IP {
         */
        private static function parseRange6( $range ) {
                # Expand any IPv6 IP
-               $range = IP::sanitizeIP( $range );
+               $range = self::sanitizeIP( $range );
                // CIDR notation...
                if ( strpos( $range, '/' ) !== false ) {
                        list( $network, $bits ) = self::parseCIDR6( $range );
@@ -732,8 +732,8 @@ class IP {
        public static function getSubnet( $ip ) {
                $matches = [];
                $subnet = false;
-               if ( IP::isIPv6( $ip ) ) {
-                       $parts = IP::parseRange( "$ip/64" );
+               if ( self::isIPv6( $ip ) ) {
+                       $parts = self::parseRange( "$ip/64" );
                        $subnet = $parts[0];
                } elseif ( preg_match( '/^(\d+\.\d+\.\d+)\.\d+$/', $ip, $matches ) ) {
                        // IPv4
index cffb5a3..9638706 100644 (file)
@@ -276,7 +276,7 @@ class StringUtils {
 
                // Replace instances of the separator inside HTML-like tags with the placeholder
                $replacer = new DoubleReplacer( $separator, $placeholder );
-               $cleaned = StringUtils::delimiterReplaceCallback( '<', '>', $replacer->cb(), $text );
+               $cleaned = self::delimiterReplaceCallback( '<', '>', $replacer->cb(), $text );
 
                // Explode, then put the replaced separators back in
                $items = explode( $separator, $cleaned );
@@ -303,7 +303,7 @@ class StringUtils {
 
                // Replace instances of the separator inside HTML-like tags with the placeholder
                $replacer = new DoubleReplacer( $search, $placeholder );
-               $cleaned = StringUtils::delimiterReplaceCallback( '<', '>', $replacer->cb(), $text );
+               $cleaned = self::delimiterReplaceCallback( '<', '>', $replacer->cb(), $text );
 
                // Explode, then put the replaced separators back in
                $cleaned = str_replace( $search, $replace, $cleaned );
index 9bfdbe8..77473d1 100644 (file)
@@ -1840,14 +1840,8 @@ abstract class FileBackendStore extends FileBackend {
                        return call_user_func_array( $this->mimeCallback, func_get_args() );
                }
 
-               $mime = null;
-               if ( $fsPath !== null && function_exists( 'finfo_file' ) ) {
-                       $finfo = finfo_open( FILEINFO_MIME_TYPE );
-                       $mime = finfo_file( $finfo, $fsPath );
-                       finfo_close( $finfo );
-               }
-
-               return is_string( $mime ) ? $mime : 'unknown/unknown';
+               $mime = ( $fsPath !== null ) ? mime_content_type( $fsPath ) : false;
+               return $mime ?: 'unknown/unknown';
        }
 }
 
index 631bb17..4d860bb 100644 (file)
@@ -988,18 +988,8 @@ EOT;
                $m = null;
                if ( $callback ) {
                        $m = $callback( $file );
-               } elseif ( function_exists( "finfo_open" ) && function_exists( "finfo_file" ) ) {
-                       $mime_magic_resource = finfo_open( FILEINFO_MIME );
-
-                       if ( $mime_magic_resource ) {
-                               $m = finfo_file( $mime_magic_resource, $file );
-                               finfo_close( $mime_magic_resource );
-                       } else {
-                               $this->logger->info( __METHOD__ .
-                                       ": finfo_open failed on " . FILEINFO_MIME . "!\n" );
-                       }
                } else {
-                       $this->logger->info( __METHOD__ . ": no magic mime detector found!\n" );
+                       $m = mime_content_type( $file );
                }
 
                if ( $m ) {
index 9bfcee7..e41c3a2 100644 (file)
@@ -42,8 +42,6 @@ class APCBagOStuff extends BagOStuff {
        const KEY_SUFFIX = ':2';
 
        /**
-        * Constructor
-        *
         * Available parameters are:
         *   - nativeSerialize:     If true, pass objects to apc_store(), and trust it
         *                          to serialize them correctly. If false, serialize
index 6e6a3ad..a26e560 100644 (file)
@@ -28,8 +28,6 @@
  */
 class APCUBagOStuff extends APCBagOStuff {
        /**
-        * Constructor
-        *
         * Available parameters are:
         *   - nativeSerialize:     If true, pass objects to apcu_store(), and trust it
         *                          to serialize them correctly. If false, serialize
index c568e7b..e3e66d5 100644 (file)
@@ -29,8 +29,6 @@
 class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
 
        /**
-        * Constructor
-        *
         * Available parameters are:
         *   - servers:             The list of IP:port combinations holding the memcached servers.
         *   - persistent:          Whether to use a persistent connection
index 687c67c..d94578d 100644 (file)
@@ -181,6 +181,12 @@ class MultiWriteBagOStuff extends BagOStuff {
                $ret = true;
                $args = array_slice( func_get_args(), 3 );
 
+               if ( $count > 1 && $asyncWrites ) {
+                       // Deep-clone $args to prevent misbehavior when something writes an
+                       // object to the BagOStuff then modifies it afterwards, e.g. T168040.
+                       $args = unserialize( serialize( $args ) );
+               }
+
                foreach ( $this->caches as $i => $cache ) {
                        if ( $i >= $count ) {
                                break; // ignore the lower tiers
index 723a4a6..b8b44e6 100644 (file)
@@ -2664,10 +2664,13 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        }
 
        final public function onTransactionPreCommitOrIdle( callable $callback, $fname = __METHOD__ ) {
-               if ( $this->mTrxLevel ) {
+               if ( $this->mTrxLevel || $this->getFlag( self::DBO_TRX ) ) {
+                       // As long as DBO_TRX is set, writes will accumulate until the load balancer issues
+                       // an implicit commit of all peer databases. This is true even if a transaction has
+                       // not yet been triggered by writes; make sure $callback runs *after* any such writes.
                        $this->mTrxPreCommitCallbacks[] = [ $callback, $fname ];
                } else {
-                       // If no transaction is active, then make one for this callback
+                       // No transaction is active nor will start implicitly, so make one for this callback
                        $this->startAtomic( __METHOD__ );
                        try {
                                call_user_func( $callback );
index a0bfb59..1ed18cd 100644 (file)
@@ -60,7 +60,7 @@ class BlockLogFormatter extends LogFormatter {
                        // is shown on the correct side of the tooltip text.
                        $durationTooltip = '&lrm;' . htmlspecialchars( $params[4] );
                        $params[4] = Message::rawParam(
-                               "<span class='blockExpiry' title='$durationTooltip'>" .
+                               "<span class=\"blockExpiry\" title=\"$durationTooltip\">" .
                                $this->context->getLanguage()->translateBlockExpiry(
                                        $params[4],
                                        $this->context->getUser(),
index f2b1670..ec97b50 100644 (file)
@@ -72,8 +72,6 @@ class LogPage {
        private $target;
 
        /**
-        * Constructor
-        *
         * @param string $type One of '', 'block', 'protect', 'rights', 'delete',
         *   'upload', 'move'
         * @param bool $rc Whether to update recent changes as well as the logging table
@@ -207,7 +205,7 @@ class LogPage {
         * @return bool
         */
        public static function isLogType( $type ) {
-               return in_array( $type, LogPage::validTypes() );
+               return in_array( $type, self::validTypes() );
        }
 
        /**
@@ -350,7 +348,7 @@ class LogPage {
                $this->action = $action;
                $this->target = $target;
                $this->comment = $comment;
-               $this->params = LogPage::makeParamBlob( $params );
+               $this->params = self::makeParamBlob( $params );
 
                if ( $doer === null ) {
                        global $wgUser;
index 11dce31..f79fcfa 100644 (file)
@@ -49,8 +49,6 @@ class LogPager extends ReverseChronologicalPager {
        public $mLogEventsList;
 
        /**
-        * Constructor
-        *
         * @param LogEventsList $list
         * @param string|array $types Log types to show
         * @param string $performer The user who made the log entries
index 3858f27..1f8489f 100644 (file)
@@ -175,18 +175,18 @@ class UserMailer {
                                // first send to non-split address list, then to split addresses one by one
                                $status = Status::newGood();
                                if ( $to ) {
-                                       $status->merge( UserMailer::sendInternal(
+                                       $status->merge( self::sendInternal(
                                                $to, $from, $subject, $body, $options ) );
                                }
                                foreach ( $splitTo as $newTo ) {
-                                       $status->merge( UserMailer::sendInternal(
+                                       $status->merge( self::sendInternal(
                                                [ $newTo ], $from, $subject, $body, $options ) );
                                }
                                return $status;
                        }
                }
 
-               return UserMailer::sendInternal( $to, $from, $subject, $body, $options );
+               return self::sendInternal( $to, $from, $subject, $body, $options );
        }
 
        /**
index 57b5b36..d25111c 100644 (file)
@@ -40,8 +40,6 @@ class DjVuImage {
        const DJVUTXT_MEMORY_LIMIT = 300000;
 
        /**
-        * Constructor
-        *
         * @param string $filename The DjVu file name.
         */
        function __construct( $filename ) {
index 9bfbc96..cd457f0 100644 (file)
@@ -96,8 +96,6 @@ class Exif {
        private $byteOrder;
 
        /**
-        * Constructor
-        *
         * @param string $file Filename.
         * @param string $byteOrder Type of byte ordering either 'BE' (Big Endian)
         *   or 'LE' (Little Endian). Default ''.
@@ -120,162 +118,162 @@ class Exif {
                        # TIFF Rev. 6.0 Attribute Information (p22)
                        'IFD0' => [
                                # Tags relating to image structure
-                               'ImageWidth' => Exif::SHORT_OR_LONG, # Image width
-                               'ImageLength' => Exif::SHORT_OR_LONG, # Image height
-                               'BitsPerSample' => [ Exif::SHORT, 3 ], # Number of bits per component
+                               'ImageWidth' => self::SHORT_OR_LONG, # Image width
+                               'ImageLength' => self::SHORT_OR_LONG, # Image height
+                               'BitsPerSample' => [ self::SHORT, 3 ], # Number of bits per component
                                # "When a primary image is JPEG compressed, this designation is not"
                                # "necessary and is omitted." (p23)
-                               'Compression' => Exif::SHORT, # Compression scheme #p23
-                               'PhotometricInterpretation' => Exif::SHORT, # Pixel composition #p23
-                               'Orientation' => Exif::SHORT, # Orientation of image #p24
-                               'SamplesPerPixel' => Exif::SHORT, # Number of components
-                               'PlanarConfiguration' => Exif::SHORT, # Image data arrangement #p24
-                               'YCbCrSubSampling' => [ Exif::SHORT, 2 ], # Subsampling ratio of Y to C #p24
-                               'YCbCrPositioning' => Exif::SHORT, # Y and C positioning #p24-25
-                               'XResolution' => Exif::RATIONAL, # Image resolution in width direction
-                               'YResolution' => Exif::RATIONAL, # Image resolution in height direction
-                               'ResolutionUnit' => Exif::SHORT, # Unit of X and Y resolution #(p26)
+                               'Compression' => self::SHORT, # Compression scheme #p23
+                               'PhotometricInterpretation' => self::SHORT, # Pixel composition #p23
+                               'Orientation' => self::SHORT, # Orientation of image #p24
+                               'SamplesPerPixel' => self::SHORT, # Number of components
+                               'PlanarConfiguration' => self::SHORT, # Image data arrangement #p24
+                               'YCbCrSubSampling' => [ self::SHORT, 2 ], # Subsampling ratio of Y to C #p24
+                               'YCbCrPositioning' => self::SHORT, # Y and C positioning #p24-25
+                               'XResolution' => self::RATIONAL, # Image resolution in width direction
+                               'YResolution' => self::RATIONAL, # Image resolution in height direction
+                               'ResolutionUnit' => self::SHORT, # Unit of X and Y resolution #(p26)
 
                                # Tags relating to recording offset
-                               'StripOffsets' => Exif::SHORT_OR_LONG, # Image data location
-                               'RowsPerStrip' => Exif::SHORT_OR_LONG, # Number of rows per strip
-                               'StripByteCounts' => Exif::SHORT_OR_LONG, # Bytes per compressed strip
-                               'JPEGInterchangeFormat' => Exif::SHORT_OR_LONG, # Offset to JPEG SOI
-                               'JPEGInterchangeFormatLength' => Exif::SHORT_OR_LONG, # Bytes of JPEG data
+                               'StripOffsets' => self::SHORT_OR_LONG, # Image data location
+                               'RowsPerStrip' => self::SHORT_OR_LONG, # Number of rows per strip
+                               'StripByteCounts' => self::SHORT_OR_LONG, # Bytes per compressed strip
+                               'JPEGInterchangeFormat' => self::SHORT_OR_LONG, # Offset to JPEG SOI
+                               'JPEGInterchangeFormatLength' => self::SHORT_OR_LONG, # Bytes of JPEG data
 
                                # Tags relating to image data characteristics
-                               'TransferFunction' => Exif::IGNORE, # Transfer function
-                               'WhitePoint' => [ Exif::RATIONAL, 2 ], # White point chromaticity
-                               'PrimaryChromaticities' => [ Exif::RATIONAL, 6 ], # Chromaticities of primarities
+                               'TransferFunction' => self::IGNORE, # Transfer function
+                               'WhitePoint' => [ self::RATIONAL, 2 ], # White point chromaticity
+                               'PrimaryChromaticities' => [ self::RATIONAL, 6 ], # Chromaticities of primarities
                                # Color space transformation matrix coefficients #p27
-                               'YCbCrCoefficients' => [ Exif::RATIONAL, 3 ],
-                               'ReferenceBlackWhite' => [ Exif::RATIONAL, 6 ], # Pair of black and white reference values
+                               'YCbCrCoefficients' => [ self::RATIONAL, 3 ],
+                               'ReferenceBlackWhite' => [ self::RATIONAL, 6 ], # Pair of black and white reference values
 
                                # Other tags
-                               'DateTime' => Exif::ASCII, # File change date and time
-                               'ImageDescription' => Exif::ASCII, # Image title
-                               'Make' => Exif::ASCII, # Image input equipment manufacturer
-                               'Model' => Exif::ASCII, # Image input equipment model
-                               'Software' => Exif::ASCII, # Software used
-                               'Artist' => Exif::ASCII, # Person who created the image
-                               'Copyright' => Exif::ASCII, # Copyright holder
+                               'DateTime' => self::ASCII, # File change date and time
+                               'ImageDescription' => self::ASCII, # Image title
+                               'Make' => self::ASCII, # Image input equipment manufacturer
+                               'Model' => self::ASCII, # Image input equipment model
+                               'Software' => self::ASCII, # Software used
+                               'Artist' => self::ASCII, # Person who created the image
+                               'Copyright' => self::ASCII, # Copyright holder
                        ],
 
                        # Exif IFD Attribute Information (p30-31)
                        'EXIF' => [
                                # @todo NOTE: Nonexistence of this field is taken to mean nonconformance
                                # to the Exif 2.1 AND 2.2 standards
-                               'ExifVersion' => Exif::UNDEFINED, # Exif version
-                               'FlashPixVersion' => Exif::UNDEFINED, # Supported Flashpix version #p32
+                               'ExifVersion' => self::UNDEFINED, # Exif version
+                               'FlashPixVersion' => self::UNDEFINED, # Supported Flashpix version #p32
 
                                # Tags relating to Image Data Characteristics
-                               'ColorSpace' => Exif::SHORT, # Color space information #p32
+                               'ColorSpace' => self::SHORT, # Color space information #p32
 
                                # Tags relating to image configuration
-                               'ComponentsConfiguration' => Exif::UNDEFINED, # Meaning of each component #p33
-                               'CompressedBitsPerPixel' => Exif::RATIONAL, # Image compression mode
-                               'PixelYDimension' => Exif::SHORT_OR_LONG, # Valid image height
-                               'PixelXDimension' => Exif::SHORT_OR_LONG, # Valid image width
+                               'ComponentsConfiguration' => self::UNDEFINED, # Meaning of each component #p33
+                               'CompressedBitsPerPixel' => self::RATIONAL, # Image compression mode
+                               'PixelYDimension' => self::SHORT_OR_LONG, # Valid image height
+                               'PixelXDimension' => self::SHORT_OR_LONG, # Valid image width
 
                                # Tags relating to related user information
-                               'MakerNote' => Exif::IGNORE, # Manufacturer notes
-                               'UserComment' => Exif::UNDEFINED, # User comments #p34
+                               'MakerNote' => self::IGNORE, # Manufacturer notes
+                               'UserComment' => self::UNDEFINED, # User comments #p34
 
                                # Tags relating to related file information
-                               'RelatedSoundFile' => Exif::ASCII, # Related audio file
+                               'RelatedSoundFile' => self::ASCII, # Related audio file
 
                                # Tags relating to date and time
-                               'DateTimeOriginal' => Exif::ASCII, # Date and time of original data generation #p36
-                               'DateTimeDigitized' => Exif::ASCII, # Date and time of original data generation
-                               'SubSecTime' => Exif::ASCII, # DateTime subseconds
-                               'SubSecTimeOriginal' => Exif::ASCII, # DateTimeOriginal subseconds
-                               'SubSecTimeDigitized' => Exif::ASCII, # DateTimeDigitized subseconds
+                               'DateTimeOriginal' => self::ASCII, # Date and time of original data generation #p36
+                               'DateTimeDigitized' => self::ASCII, # Date and time of original data generation
+                               'SubSecTime' => self::ASCII, # DateTime subseconds
+                               'SubSecTimeOriginal' => self::ASCII, # DateTimeOriginal subseconds
+                               'SubSecTimeDigitized' => self::ASCII, # DateTimeDigitized subseconds
 
                                # Tags relating to picture-taking conditions (p31)
-                               'ExposureTime' => Exif::RATIONAL, # Exposure time
-                               'FNumber' => Exif::RATIONAL, # F Number
-                               'ExposureProgram' => Exif::SHORT, # Exposure Program #p38
-                               'SpectralSensitivity' => Exif::ASCII, # Spectral sensitivity
-                               'ISOSpeedRatings' => Exif::SHORT, # ISO speed rating
-                               'OECF' => Exif::IGNORE,
+                               'ExposureTime' => self::RATIONAL, # Exposure time
+                               'FNumber' => self::RATIONAL, # F Number
+                               'ExposureProgram' => self::SHORT, # Exposure Program #p38
+                               'SpectralSensitivity' => self::ASCII, # Spectral sensitivity
+                               'ISOSpeedRatings' => self::SHORT, # ISO speed rating
+                               'OECF' => self::IGNORE,
                                # Optoelectronic conversion factor. Note: We don't have support for this atm.
-                               'ShutterSpeedValue' => Exif::SRATIONAL, # Shutter speed
-                               'ApertureValue' => Exif::RATIONAL, # Aperture
-                               'BrightnessValue' => Exif::SRATIONAL, # Brightness
-                               'ExposureBiasValue' => Exif::SRATIONAL, # Exposure bias
-                               'MaxApertureValue' => Exif::RATIONAL, # Maximum land aperture
-                               'SubjectDistance' => Exif::RATIONAL, # Subject distance
-                               'MeteringMode' => Exif::SHORT, # Metering mode #p40
-                               'LightSource' => Exif::SHORT, # Light source #p40-41
-                               'Flash' => Exif::SHORT, # Flash #p41-42
-                               'FocalLength' => Exif::RATIONAL, # Lens focal length
-                               'SubjectArea' => [ Exif::SHORT, 4 ], # Subject area
-                               'FlashEnergy' => Exif::RATIONAL, # Flash energy
-                               'SpatialFrequencyResponse' => Exif::IGNORE, # Spatial frequency response. Not supported atm.
-                               'FocalPlaneXResolution' => Exif::RATIONAL, # Focal plane X resolution
-                               'FocalPlaneYResolution' => Exif::RATIONAL, # Focal plane Y resolution
-                               'FocalPlaneResolutionUnit' => Exif::SHORT, # Focal plane resolution unit #p46
-                               'SubjectLocation' => [ Exif::SHORT, 2 ], # Subject location
-                               'ExposureIndex' => Exif::RATIONAL, # Exposure index
-                               'SensingMethod' => Exif::SHORT, # Sensing method #p46
-                               'FileSource' => Exif::UNDEFINED, # File source #p47
-                               'SceneType' => Exif::UNDEFINED, # Scene type #p47
-                               'CFAPattern' => Exif::IGNORE, # CFA pattern. not supported atm.
-                               'CustomRendered' => Exif::SHORT, # Custom image processing #p48
-                               'ExposureMode' => Exif::SHORT, # Exposure mode #p48
-                               'WhiteBalance' => Exif::SHORT, # White Balance #p49
-                               'DigitalZoomRatio' => Exif::RATIONAL, # Digital zoom ration
-                               'FocalLengthIn35mmFilm' => Exif::SHORT, # Focal length in 35 mm film
-                               'SceneCaptureType' => Exif::SHORT, # Scene capture type #p49
-                               'GainControl' => Exif::SHORT, # Scene control #p49-50
-                               'Contrast' => Exif::SHORT, # Contrast #p50
-                               'Saturation' => Exif::SHORT, # Saturation #p50
-                               'Sharpness' => Exif::SHORT, # Sharpness #p50
-                               'DeviceSettingDescription' => Exif::IGNORE,
+                               'ShutterSpeedValue' => self::SRATIONAL, # Shutter speed
+                               'ApertureValue' => self::RATIONAL, # Aperture
+                               'BrightnessValue' => self::SRATIONAL, # Brightness
+                               'ExposureBiasValue' => self::SRATIONAL, # Exposure bias
+                               'MaxApertureValue' => self::RATIONAL, # Maximum land aperture
+                               'SubjectDistance' => self::RATIONAL, # Subject distance
+                               'MeteringMode' => self::SHORT, # Metering mode #p40
+                               'LightSource' => self::SHORT, # Light source #p40-41
+                               'Flash' => self::SHORT, # Flash #p41-42
+                               'FocalLength' => self::RATIONAL, # Lens focal length
+                               'SubjectArea' => [ self::SHORT, 4 ], # Subject area
+                               'FlashEnergy' => self::RATIONAL, # Flash energy
+                               'SpatialFrequencyResponse' => self::IGNORE, # Spatial frequency response. Not supported atm.
+                               'FocalPlaneXResolution' => self::RATIONAL, # Focal plane X resolution
+                               'FocalPlaneYResolution' => self::RATIONAL, # Focal plane Y resolution
+                               'FocalPlaneResolutionUnit' => self::SHORT, # Focal plane resolution unit #p46
+                               'SubjectLocation' => [ self::SHORT, 2 ], # Subject location
+                               'ExposureIndex' => self::RATIONAL, # Exposure index
+                               'SensingMethod' => self::SHORT, # Sensing method #p46
+                               'FileSource' => self::UNDEFINED, # File source #p47
+                               'SceneType' => self::UNDEFINED, # Scene type #p47
+                               'CFAPattern' => self::IGNORE, # CFA pattern. not supported atm.
+                               'CustomRendered' => self::SHORT, # Custom image processing #p48
+                               'ExposureMode' => self::SHORT, # Exposure mode #p48
+                               'WhiteBalance' => self::SHORT, # White Balance #p49
+                               'DigitalZoomRatio' => self::RATIONAL, # Digital zoom ration
+                               'FocalLengthIn35mmFilm' => self::SHORT, # Focal length in 35 mm film
+                               'SceneCaptureType' => self::SHORT, # Scene capture type #p49
+                               'GainControl' => self::SHORT, # Scene control #p49-50
+                               'Contrast' => self::SHORT, # Contrast #p50
+                               'Saturation' => self::SHORT, # Saturation #p50
+                               'Sharpness' => self::SHORT, # Sharpness #p50
+                               'DeviceSettingDescription' => self::IGNORE,
                                # Device settings description. This could maybe be supported. Need to find an
                                # example file that uses this to see if it has stuff of interest in it.
-                               'SubjectDistanceRange' => Exif::SHORT, # Subject distance range #p51
+                               'SubjectDistanceRange' => self::SHORT, # Subject distance range #p51
 
-                               'ImageUniqueID' => Exif::ASCII, # Unique image ID
+                               'ImageUniqueID' => self::ASCII, # Unique image ID
                        ],
 
                        # GPS Attribute Information (p52)
                        'GPS' => [
-                               'GPSVersion' => Exif::UNDEFINED,
+                               'GPSVersion' => self::UNDEFINED,
                                # Should be an array of 4 Exif::BYTE's. However php treats it as an undefined
                                # Note exif standard calls this GPSVersionID, but php doesn't like the id suffix
-                               'GPSLatitudeRef' => Exif::ASCII, # North or South Latitude #p52-53
-                               'GPSLatitude' => [ Exif::RATIONAL, 3 ], # Latitude
-                               'GPSLongitudeRef' => Exif::ASCII, # East or West Longitude #p53
-                               'GPSLongitude' => [ Exif::RATIONAL, 3 ], # Longitude
-                               'GPSAltitudeRef' => Exif::UNDEFINED,
+                               'GPSLatitudeRef' => self::ASCII, # North or South Latitude #p52-53
+                               'GPSLatitude' => [ self::RATIONAL, 3 ], # Latitude
+                               'GPSLongitudeRef' => self::ASCII, # East or West Longitude #p53
+                               'GPSLongitude' => [ self::RATIONAL, 3 ], # Longitude
+                               'GPSAltitudeRef' => self::UNDEFINED,
                                # Altitude reference. Note, the exif standard says this should be an EXIF::Byte,
                                # but php seems to disagree.
-                               'GPSAltitude' => Exif::RATIONAL, # Altitude
-                               'GPSTimeStamp' => [ Exif::RATIONAL, 3 ], # GPS time (atomic clock)
-                               'GPSSatellites' => Exif::ASCII, # Satellites used for measurement
-                               'GPSStatus' => Exif::ASCII, # Receiver status #p54
-                               'GPSMeasureMode' => Exif::ASCII, # Measurement mode #p54-55
-                               'GPSDOP' => Exif::RATIONAL, # Measurement precision
-                               'GPSSpeedRef' => Exif::ASCII, # Speed unit #p55
-                               'GPSSpeed' => Exif::RATIONAL, # Speed of GPS receiver
-                               'GPSTrackRef' => Exif::ASCII, # Reference for direction of movement #p55
-                               'GPSTrack' => Exif::RATIONAL, # Direction of movement
-                               'GPSImgDirectionRef' => Exif::ASCII, # Reference for direction of image #p56
-                               'GPSImgDirection' => Exif::RATIONAL, # Direction of image
-                               'GPSMapDatum' => Exif::ASCII, # Geodetic survey data used
-                               'GPSDestLatitudeRef' => Exif::ASCII, # Reference for latitude of destination #p56
-                               'GPSDestLatitude' => [ Exif::RATIONAL, 3 ], # Latitude destination
-                               'GPSDestLongitudeRef' => Exif::ASCII, # Reference for longitude of destination #p57
-                               'GPSDestLongitude' => [ Exif::RATIONAL, 3 ], # Longitude of destination
-                               'GPSDestBearingRef' => Exif::ASCII, # Reference for bearing of destination #p57
-                               'GPSDestBearing' => Exif::RATIONAL, # Bearing of destination
-                               'GPSDestDistanceRef' => Exif::ASCII, # Reference for distance to destination #p57-58
-                               'GPSDestDistance' => Exif::RATIONAL, # Distance to destination
-                               'GPSProcessingMethod' => Exif::UNDEFINED, # Name of GPS processing method
-                               'GPSAreaInformation' => Exif::UNDEFINED, # Name of GPS area
-                               'GPSDateStamp' => Exif::ASCII, # GPS date
-                               'GPSDifferential' => Exif::SHORT, # GPS differential correction
+                               'GPSAltitude' => self::RATIONAL, # Altitude
+                               'GPSTimeStamp' => [ self::RATIONAL, 3 ], # GPS time (atomic clock)
+                               'GPSSatellites' => self::ASCII, # Satellites used for measurement
+                               'GPSStatus' => self::ASCII, # Receiver status #p54
+                               'GPSMeasureMode' => self::ASCII, # Measurement mode #p54-55
+                               'GPSDOP' => self::RATIONAL, # Measurement precision
+                               'GPSSpeedRef' => self::ASCII, # Speed unit #p55
+                               'GPSSpeed' => self::RATIONAL, # Speed of GPS receiver
+                               'GPSTrackRef' => self::ASCII, # Reference for direction of movement #p55
+                               'GPSTrack' => self::RATIONAL, # Direction of movement
+                               'GPSImgDirectionRef' => self::ASCII, # Reference for direction of image #p56
+                               'GPSImgDirection' => self::RATIONAL, # Direction of image
+                               'GPSMapDatum' => self::ASCII, # Geodetic survey data used
+                               'GPSDestLatitudeRef' => self::ASCII, # Reference for latitude of destination #p56
+                               'GPSDestLatitude' => [ self::RATIONAL, 3 ], # Latitude destination
+                               'GPSDestLongitudeRef' => self::ASCII, # Reference for longitude of destination #p57
+                               'GPSDestLongitude' => [ self::RATIONAL, 3 ], # Longitude of destination
+                               'GPSDestBearingRef' => self::ASCII, # Reference for bearing of destination #p57
+                               'GPSDestBearing' => self::RATIONAL, # Bearing of destination
+                               'GPSDestDistanceRef' => self::ASCII, # Reference for distance to destination #p57-58
+                               'GPSDestDistance' => self::RATIONAL, # Distance to destination
+                               'GPSProcessingMethod' => self::UNDEFINED, # Name of GPS processing method
+                               'GPSAreaInformation' => self::UNDEFINED, # Name of GPS area
+                               'GPSDateStamp' => self::ASCII, # GPS date
+                               'GPSDifferential' => self::SHORT, # GPS differential correction
                        ],
                ];
 
@@ -761,43 +759,43 @@ class Exif {
                }
                // Does not work if not typecast
                switch ( (string)$etype ) {
-                       case (string)Exif::BYTE:
+                       case (string)self::BYTE:
                                $this->debug( $val, __FUNCTION__, $debug );
 
                                return $this->isByte( $val );
-                       case (string)Exif::ASCII:
+                       case (string)self::ASCII:
                                $this->debug( $val, __FUNCTION__, $debug );
 
                                return $this->isASCII( $val );
-                       case (string)Exif::SHORT:
+                       case (string)self::SHORT:
                                $this->debug( $val, __FUNCTION__, $debug );
 
                                return $this->isShort( $val );
-                       case (string)Exif::LONG:
+                       case (string)self::LONG:
                                $this->debug( $val, __FUNCTION__, $debug );
 
                                return $this->isLong( $val );
-                       case (string)Exif::RATIONAL:
+                       case (string)self::RATIONAL:
                                $this->debug( $val, __FUNCTION__, $debug );
 
                                return $this->isRational( $val );
-                       case (string)Exif::SHORT_OR_LONG:
+                       case (string)self::SHORT_OR_LONG:
                                $this->debug( $val, __FUNCTION__, $debug );
 
                                return $this->isShort( $val ) || $this->isLong( $val );
-                       case (string)Exif::UNDEFINED:
+                       case (string)self::UNDEFINED:
                                $this->debug( $val, __FUNCTION__, $debug );
 
                                return $this->isUndefined( $val );
-                       case (string)Exif::SLONG:
+                       case (string)self::SLONG:
                                $this->debug( $val, __FUNCTION__, $debug );
 
                                return $this->isSlong( $val );
-                       case (string)Exif::SRATIONAL:
+                       case (string)self::SRATIONAL:
                                $this->debug( $val, __FUNCTION__, $debug );
 
                                return $this->isSrational( $val );
-                       case (string)Exif::IGNORE:
+                       case (string)self::IGNORE:
                                $this->debug( $val, __FUNCTION__, $debug );
 
                                return false;
index 2cf4d23..9b22cbe 100644 (file)
@@ -58,8 +58,6 @@ class SVGReader {
        private $languagePrefixes = [];
 
        /**
-        * Constructor
-        *
         * Creates an SVGReader drawing from the source provided
         * @param string $source URI from which to read
         * @throws MWException|Exception
index d13fb3c..e2cf2cf 100644 (file)
@@ -1594,7 +1594,7 @@ class Article implements Page {
                        [ 'delete', $this->getTitle()->getPrefixedText() ] )
                ) {
                        # Flag to hide all contents of the archived revisions
-                       $suppress = $request->getVal( 'wpSuppress' ) && $user->isAllowed( 'suppressrevision' );
+                       $suppress = $request->getCheck( 'wpSuppress' ) && $user->isAllowed( 'suppressrevision' );
 
                        $this->doDelete( $reason, $suppress );
 
@@ -1669,7 +1669,6 @@ class Article implements Page {
                $title = $this->getTitle();
                $ctx = $this->getContext();
                $outputPage = $ctx->getOutput();
-               $useMediaWikiUIEverywhere = $ctx->getConfig()->get( 'UseMediaWikiUIEverywhere' );
                $outputPage->setPageTitle( wfMessage( 'delete-confirm', $title->getPrefixedText() ) );
                $outputPage->addBacklinkSubtitle( $title );
                $outputPage->setRobotPolicy( 'noindex,nofollow' );
@@ -1692,14 +1691,6 @@ class Article implements Page {
                Hooks::run( 'ArticleConfirmDelete', [ $this, $outputPage, &$reason ] );
 
                $user = $this->getContext()->getUser();
-               if ( $user->isAllowed( 'suppressrevision' ) ) {
-                       $suppress = Html::openElement( 'div', [ 'id' => 'wpDeleteSuppressRow' ] ) .
-                               Xml::checkLabel( wfMessage( 'revdelete-suppress' )->text(),
-                                       'wpSuppress', 'wpSuppress', false, [ 'tabindex' => '4' ] ) .
-                               Html::closeElement( 'div' );
-               } else {
-                       $suppress = '';
-               }
                $checkWatch = $user->getBoolOption( 'watchdeletion' ) || $user->isWatched( $title );
 
                $outputPage->enableOOUI();
@@ -1770,6 +1761,21 @@ class Article implements Page {
                        );
                }
 
+               if ( $user->isAllowed( 'suppressrevision' ) ) {
+                       $fields[] = new OOUI\FieldLayout(
+                               new OOUI\CheckboxInputWidget( [
+                                       'name' => 'wpSuppress',
+                                       'inputId' => 'wpSuppress',
+                                       'tabIndex' => 4,
+                               ] ),
+                               [
+                                       'label' => $ctx->msg( 'revdelete-suppress' )->text(),
+                                       'align' => 'inline',
+                                       'infusable' => true,
+                               ]
+                       );
+               }
+
                $fields[] = new OOUI\FieldLayout(
                        new OOUI\ButtonInputWidget( [
                                'name' => 'wpConfirmB',
index 20fb9be..c05ba45 100644 (file)
@@ -50,7 +50,7 @@ class WikiPage implements Page, IDBAccessObject {
        public $mLatest = false;             // !< Integer (false means "not loaded")
        /**@}}*/
 
-       /** @var stdClass Map of cache fields (text, parser output, ect) for a proposed/new edit */
+       /** @var PreparedEdit Map of cache fields (text, parser output, ect) for a proposed/new edit */
        public $mPreparedEdit = false;
 
        /**
@@ -782,7 +782,7 @@ class WikiPage implements Page, IDBAccessObject {
         * Determine whether a page would be suitable for being counted as an
         * article in the site_stats table based on the title & its content
         *
-        * @param object|bool $editInfo (false): object returned by prepareTextForEdit(),
+        * @param PreparedEdit|bool $editInfo (false): object returned by prepareTextForEdit(),
         *   if false, the current database state will be used
         * @return bool
         */
@@ -1607,7 +1607,7 @@ class WikiPage implements Page, IDBAccessObject {
                $meta = [
                        'bot' => ( $flags & EDIT_FORCE_BOT ),
                        'minor' => ( $flags & EDIT_MINOR ) && $user->isAllowed( 'minoredit' ),
-                       'serialized' => $editInfo->pst,
+                       'serialized' => $pstContent->serialize( $serialFormat ),
                        'serialFormat' => $serialFormat,
                        'baseRevId' => $baseRevId,
                        'oldRevision' => $old_revision,
@@ -1961,7 +1961,9 @@ class WikiPage implements Page, IDBAccessObject {
 
        /**
         * Prepare content which is about to be saved.
-        * Returns a stdClass with source, pst and output members
+        *
+        * Prior to 1.30, this returned a stdClass object with the same class
+        * members.
         *
         * @param Content $content
         * @param Revision|int|null $revision Revision object. For backwards compatibility, a
@@ -2971,7 +2973,7 @@ class WikiPage implements Page, IDBAccessObject {
                }
 
                // Clear caches
-               WikiPage::onArticleDelete( $this->mTitle );
+               self::onArticleDelete( $this->mTitle );
                ResourceLoaderWikiModule::invalidateModuleCache(
                        $this->mTitle, $revision, null, wfWikiID()
                );
index 0b867ef..6620c47 100644 (file)
@@ -737,6 +737,6 @@ abstract class IndexPager extends ContextSource implements Pager {
         * @return bool
         */
        protected function getDefaultDirections() {
-               return IndexPager::DIR_ASCENDING;
+               return self::DIR_ASCENDING;
        }
 }
index 5da2546..b035b02 100644 (file)
@@ -227,7 +227,7 @@ class Parser {
         * @var string Deprecated accessor for the strip marker prefix.
         * @deprecated since 1.26; use Parser::MARKER_PREFIX instead.
         */
-       public $mUniqPrefix = Parser::MARKER_PREFIX;
+       public $mUniqPrefix = self::MARKER_PREFIX;
 
        /**
         * @var array Array with the language name of each language link (i.e. the
@@ -1295,7 +1295,7 @@ class Parser {
                        if ( !$frame->depth ) {
                                $flag = 0;
                        } else {
-                               $flag = Parser::PTD_FOR_INCLUSION;
+                               $flag = self::PTD_FOR_INCLUSION;
                        }
                        $dom = $this->preprocessToDom( $text, $flag );
                        $text = $frame->expand( $dom );
index 96a4368..73a9927 100644 (file)
@@ -927,7 +927,6 @@ class ParserOptions {
        }
 
        /**
-        * Constructor
         * @warning For interaction with the parser cache, use
         *  WikiPage::makeParserOptions(), ContentHandler::makeParserOptions(), or
         *  ParserOptions::newCanonical() instead.
index 10ac192..cfb0c3e 100644 (file)
@@ -253,7 +253,7 @@ class ParserOutput extends CacheTime {
                $text = $this->mText;
                if ( $this->mEditSectionTokens ) {
                        $text = preg_replace_callback(
-                               ParserOutput::EDITSECTION_REGEX,
+                               self::EDITSECTION_REGEX,
                                function ( $m ) {
                                        global $wgOut, $wgLang;
                                        $editsectionPage = Title::newFromText( htmlspecialchars_decode( $m[1] ) );
@@ -274,7 +274,7 @@ class ParserOutput extends CacheTime {
                                $text
                        );
                } else {
-                       $text = preg_replace( ParserOutput::EDITSECTION_REGEX, '', $text );
+                       $text = preg_replace( self::EDITSECTION_REGEX, '', $text );
                }
 
                // If you have an old cached version of this class - sorry, you can't disable the TOC
index ddf084e..20b0780 100644 (file)
@@ -31,7 +31,6 @@ abstract class ProfilerOutput {
        protected $params = [];
 
        /**
-        * Constructor
         * @param Profiler $collector The actual profiler
         * @param array $params Configuration array, passed down from $wgProfiler
         */
index 8553116..2f29200 100644 (file)
@@ -178,7 +178,7 @@ class ResourceLoader implements LoggerAwareInterface {
         * @return string Filtered data, or a comment containing an error message
         */
        public static function filter( $filter, $data, array $options = [] ) {
-               if ( strpos( $data, ResourceLoader::FILTER_NOMIN ) !== false ) {
+               if ( strpos( $data, self::FILTER_NOMIN ) !== false ) {
                        return $data;
                }
 
@@ -1079,7 +1079,7 @@ MESSAGE;
                                                                // mw.loader.implement will use globalEval if scripts is a string.
                                                                // Minify manually here, because general response minification is
                                                                // not effective due it being a string literal, not a function.
-                                                               if ( !ResourceLoader::inDebugMode() ) {
+                                                               if ( !self::inDebugMode() ) {
                                                                        $scripts = self::filter( 'minify-js', $scripts ); // T107377
                                                                }
                                                        } else {
@@ -1139,7 +1139,7 @@ MESSAGE;
                } else {
                        if ( count( $states ) ) {
                                $this->errors[] = 'Problematic modules: ' .
-                                       FormatJson::encode( $states, ResourceLoader::inDebugMode() );
+                                       FormatJson::encode( $states, self::inDebugMode() );
                        }
                }
 
@@ -1214,7 +1214,7 @@ MESSAGE;
                ];
                self::trimArray( $module );
 
-               return Xml::encodeJsCall( 'mw.loader.implement', $module, ResourceLoader::inDebugMode() );
+               return Xml::encodeJsCall( 'mw.loader.implement', $module, self::inDebugMode() );
        }
 
        /**
@@ -1228,7 +1228,7 @@ MESSAGE;
                return Xml::encodeJsCall(
                        'mw.messages.set',
                        [ (object)$messages ],
-                       ResourceLoader::inDebugMode()
+                       self::inDebugMode()
                );
        }
 
@@ -1285,13 +1285,13 @@ MESSAGE;
                        return Xml::encodeJsCall(
                                'mw.loader.state',
                                [ $name ],
-                               ResourceLoader::inDebugMode()
+                               self::inDebugMode()
                        );
                } else {
                        return Xml::encodeJsCall(
                                'mw.loader.state',
                                [ $name, $state ],
-                               ResourceLoader::inDebugMode()
+                               self::inDebugMode()
                        );
                }
        }
@@ -1317,7 +1317,7 @@ MESSAGE;
                return Xml::encodeJsCall(
                        "( function ( name, version, dependencies, group, source ) {\n\t$script\n} )",
                        [ $name, $version, $dependencies, $group, $source ],
-                       ResourceLoader::inDebugMode()
+                       self::inDebugMode()
                );
        }
 
@@ -1409,7 +1409,7 @@ MESSAGE;
                        return Xml::encodeJsCall(
                                'mw.loader.register',
                                [ $name ],
-                               ResourceLoader::inDebugMode()
+                               self::inDebugMode()
                        );
                } else {
                        $registration = [ $name, $version, $dependencies, $group, $source, $skip ];
@@ -1417,7 +1417,7 @@ MESSAGE;
                        return Xml::encodeJsCall(
                                'mw.loader.register',
                                $registration,
-                               ResourceLoader::inDebugMode()
+                               self::inDebugMode()
                        );
                }
        }
@@ -1441,13 +1441,13 @@ MESSAGE;
                        return Xml::encodeJsCall(
                                'mw.loader.addSource',
                                [ $id ],
-                               ResourceLoader::inDebugMode()
+                               self::inDebugMode()
                        );
                } else {
                        return Xml::encodeJsCall(
                                'mw.loader.addSource',
                                [ $id, $loadUrl ],
-                               ResourceLoader::inDebugMode()
+                               self::inDebugMode()
                        );
                }
        }
@@ -1494,7 +1494,7 @@ MESSAGE;
                return Xml::encodeJsCall(
                        'mw.config.set',
                        [ $configuration ],
-                       ResourceLoader::inDebugMode()
+                       self::inDebugMode()
                );
        }
 
index 197ac51..06f9841 100644 (file)
@@ -149,9 +149,7 @@ class ResourceLoaderClientHtml {
                                continue;
                        }
 
-                       $group = $module->getGroup();
-
-                       if ( $group === 'private' ) {
+                       if ( $module->shouldEmbedModule( $this->context ) ) {
                                // Embed via mw.loader.implement per T36907.
                                $data['embed']['general'][] = $name;
                                // Avoid duplicate request from mw.loader
@@ -186,7 +184,7 @@ class ResourceLoaderClientHtml {
                                // Avoid needless request for empty module
                                $data['states'][$name] = 'ready';
                        } else {
-                               if ( $group === 'private' ) {
+                               if ( $module->shouldEmbedModule( $this->context ) ) {
                                        // Embed via style element
                                        $data['embed']['styles'][] = $name;
                                        // Avoid duplicate request from mw.loader
@@ -392,62 +390,75 @@ class ResourceLoaderClientHtml {
                foreach ( $sortedModules as $source => $groups ) {
                        foreach ( $groups as $group => $grpModules ) {
                                $context = self::makeContext( $mainContext, $group, $only, $extraQuery );
-                               $context->setModules( array_keys( $grpModules ) );
-
-                               if ( $group === 'private' ) {
-                                       // Decide whether to use style or script element
-                                       if ( $only == ResourceLoaderModule::TYPE_STYLES ) {
-                                               $chunks[] = Html::inlineStyle(
-                                                       $rl->makeModuleResponse( $context, $grpModules )
-                                               );
-                                       } else {
-                                               $chunks[] = ResourceLoader::makeInlineScript(
-                                                       $rl->makeModuleResponse( $context, $grpModules )
-                                               );
-                                       }
-                                       continue;
-                               }
-
-                               // See if we have one or more raw modules
-                               $isRaw = false;
-                               foreach ( $grpModules as $key => $module ) {
-                                       $isRaw |= $module->isRaw();
-                               }
 
-                               // Special handling for the user group; because users might change their stuff
-                               // on-wiki like user pages, or user preferences; we need to find the highest
-                               // timestamp of these user-changeable modules so we can ensure cache misses on change
-                               // This should NOT be done for the site group (T29564) because anons get that too
-                               // and we shouldn't be putting timestamps in CDN-cached HTML
-                               if ( $group === 'user' ) {
-                                       // Must setModules() before makeVersionQuery()
-                                       $context->setVersion( $rl->makeVersionQuery( $context ) );
+                               // Separate sets of linked and embedded modules while preserving order
+                               $moduleSets = [];
+                               $idx = -1;
+                               foreach ( $grpModules as $name => $module ) {
+                                       $shouldEmbed = $module->shouldEmbedModule( $context );
+                                       if ( !$moduleSets || $moduleSets[$idx][0] !== $shouldEmbed ) {
+                                               $moduleSets[++$idx] = [ $shouldEmbed, [] ];
+                                       }
+                                       $moduleSets[$idx][1][$name] = $module;
                                }
 
-                               $url = $rl->createLoaderURL( $source, $context, $extraQuery );
-
-                               // Decide whether to use 'style' or 'script' element
-                               if ( $only === ResourceLoaderModule::TYPE_STYLES ) {
-                                       $chunk = Html::linkedStyle( $url );
-                               } else {
-                                       if ( $context->getRaw() || $isRaw ) {
-                                               $chunk = Html::element( 'script', [
-                                                       // In SpecialJavaScriptTest, QUnit must load synchronous
-                                                       'async' => !isset( $extraQuery['sync'] ),
-                                                       'src' => $url
-                                               ] );
+                               // Link/embed each set
+                               foreach ( $moduleSets as list( $embed, $moduleSet ) ) {
+                                       $context->setModules( array_keys( $moduleSet ) );
+                                       if ( $embed ) {
+                                               // Decide whether to use style or script element
+                                               if ( $only == ResourceLoaderModule::TYPE_STYLES ) {
+                                                       $chunks[] = Html::inlineStyle(
+                                                               $rl->makeModuleResponse( $context, $moduleSet )
+                                                       );
+                                               } else {
+                                                       $chunks[] = ResourceLoader::makeInlineScript(
+                                                               $rl->makeModuleResponse( $context, $moduleSet )
+                                                       );
+                                               }
                                        } else {
-                                               $chunk = ResourceLoader::makeInlineScript(
-                                                       Xml::encodeJsCall( 'mw.loader.load', [ $url ] )
-                                               );
+                                               // See if we have one or more raw modules
+                                               $isRaw = false;
+                                               foreach ( $moduleSet as $key => $module ) {
+                                                       $isRaw |= $module->isRaw();
+                                               }
+
+                                               // Special handling for the user group; because users might change their stuff
+                                               // on-wiki like user pages, or user preferences; we need to find the highest
+                                               // timestamp of these user-changeable modules so we can ensure cache misses on change
+                                               // This should NOT be done for the site group (T29564) because anons get that too
+                                               // and we shouldn't be putting timestamps in CDN-cached HTML
+                                               if ( $group === 'user' ) {
+                                                       // Must setModules() before makeVersionQuery()
+                                                       $context->setVersion( $rl->makeVersionQuery( $context ) );
+                                               }
+
+                                               $url = $rl->createLoaderURL( $source, $context, $extraQuery );
+
+                                               // Decide whether to use 'style' or 'script' element
+                                               if ( $only === ResourceLoaderModule::TYPE_STYLES ) {
+                                                       $chunk = Html::linkedStyle( $url );
+                                               } else {
+                                                       if ( $context->getRaw() || $isRaw ) {
+                                                               $chunk = Html::element( 'script', [
+                                                                       // In SpecialJavaScriptTest, QUnit must load synchronous
+                                                                       'async' => !isset( $extraQuery['sync'] ),
+                                                                       'src' => $url
+                                                               ] );
+                                                       } else {
+                                                               $chunk = ResourceLoader::makeInlineScript(
+                                                                       Xml::encodeJsCall( 'mw.loader.load', [ $url ] )
+                                                               );
+                                                       }
+                                               }
+
+                                               if ( $group == 'noscript' ) {
+                                                       $chunks[] = Html::rawElement( 'noscript', [], $chunk );
+                                               } else {
+                                                       $chunks[] = $chunk;
+                                               }
                                        }
                                }
-
-                               if ( $group == 'noscript' ) {
-                                       $chunks[] = Html::rawElement( 'noscript', [], $chunk );
-                               } else {
-                                       $chunks[] = $chunk;
-                               }
                        }
                }
 
index 79b8e79..4675191 100644 (file)
@@ -580,6 +580,12 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
                        'fileHashes' => $this->getFileHashes( $context ),
                        'messageBlob' => $this->getMessageBlob( $context ),
                ];
+
+               $lessVars = $this->getLessVars( $context );
+               if ( $lessVars ) {
+                       $summary[] = [ 'lessVars' => $lessVars ];
+               }
+
                return $summary;
        }
 
index 1c767fa..1608901 100644 (file)
@@ -918,6 +918,20 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
                return false;
        }
 
+       /**
+        * Check whether this module should be embeded rather than linked
+        *
+        * Modules returning true here will be embedded rather than loaded by
+        * ResourceLoaderClientHtml.
+        *
+        * @since 1.30
+        * @param ResourceLoaderContext $context
+        * @return bool
+        */
+       public function shouldEmbedModule( ResourceLoaderContext $context ) {
+               return $this->getGroup() === 'private';
+       }
+
        /** @var JSParser Lazy-initialized; use self::javaScriptParser() */
        private static $jsParser;
        private static $parseCacheVersion = 1;
index 1d7a4a3..643c2c1 100644 (file)
@@ -35,7 +35,6 @@ class SearchDatabase extends SearchEngine {
        protected $db;
 
        /**
-        * Constructor
         * @param IDatabase $db The database to search from
         */
        public function __construct( IDatabase $db = null ) {
index a8f9d0c..40905a5 100644 (file)
@@ -95,7 +95,7 @@ abstract class Skin extends ContextSource {
        static function normalizeKey( $key ) {
                global $wgDefaultSkin, $wgFallbackSkin;
 
-               $skinNames = Skin::getSkinNames();
+               $skinNames = self::getSkinNames();
 
                // Make keys lowercase for case-insensitive matching.
                $skinNames = array_change_key_case( $skinNames, CASE_LOWER );
index 5c048a2..0600642 100644 (file)
@@ -761,7 +761,7 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
                if ( $this->showCreateAccountLink() ) {
                        # Pass any language selection on to the mode switch link
                        if ( $this->mLanguage ) {
-                               $linkq .= '&uselang=' . $this->mLanguage;
+                               $linkq .= '&uselang=' . urlencode( $this->mLanguage );
                        }
                        // Supply URL, login template creates the button.
                        $template->set( 'createOrLoginHref', $titleObj->getLocalURL( $linkq ) );
@@ -1149,7 +1149,7 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
                                $linkq = $this->getReturnToQueryStringFragment();
                                // Pass any language selection on to the mode switch link
                                if ( $this->mLanguage ) {
-                                       $linkq .= '&uselang=' . $this->mLanguage;
+                                       $linkq .= '&uselang=' . urlencode( $this->mLanguage );
                                }
                                $loggedIn = $this->getUser()->isLoggedIn();
 
index 67c14d8..4c3ca54 100644 (file)
@@ -383,7 +383,7 @@ class SpecialPage implements MessageLocalizer {
                        return true;
                } elseif ( $securityStatus === AuthManager::SEC_REAUTH ) {
                        $request = $this->getRequest();
-                       $title = SpecialPage::getTitleFor( 'Userlogin' );
+                       $title = self::getTitleFor( 'Userlogin' );
                        $query = [
                                'returnto' => $this->getFullTitle()->getPrefixedDBkey(),
                                'returntoquery' => wfArrayToCgi( array_diff_key( $request->getQueryValues(),
index e7c9423..9028787 100644 (file)
@@ -28,9 +28,6 @@
  */
 class SpecialActiveUsers extends SpecialPage {
 
-       /**
-        * Constructor
-        */
        public function __construct() {
                parent::__construct( 'Activeusers' );
        }
index 4056709..9e66447 100644 (file)
@@ -33,9 +33,6 @@ class SpecialAllMessages extends SpecialPage {
         */
        protected $table;
 
-       /**
-        * Constructor
-        */
        public function __construct() {
                parent::__construct( 'Allmessages' );
        }
index 17f6cca..4d84e31 100644 (file)
@@ -44,8 +44,6 @@ class SpecialAllPages extends IncludableSpecialPage {
        protected $nsfromMsg = 'allpagesfrom';
 
        /**
-        * Constructor
-        *
         * @param string $name Name of the special page, as seen in links and URLs (default: 'Allpages')
         */
        function __construct( $name = 'Allpages' ) {
index a2930fc..a827e89 100644 (file)
@@ -46,9 +46,6 @@ class SpecialImport extends SpecialPage {
        private $pageLinkDepth;
        private $importSources;
 
-       /**
-        * Constructor
-        */
        public function __construct() {
                parent::__construct( 'Import', 'import' );
        }
index 1a8dccf..dee2968 100644 (file)
@@ -29,9 +29,7 @@
  * @ingroup SpecialPage
  */
 class SpecialListUsers extends IncludableSpecialPage {
-       /**
-        * Constructor
-        */
+
        public function __construct() {
                parent::__construct( 'Listusers' );
        }
index 15bbffd..f0c2bc4 100644 (file)
@@ -315,7 +315,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                $opts = parent::getDefaultOptions();
                $user = $this->getUser();
 
-               $opts->add( 'days', $user->getIntOption( 'rcdays' ) );
+               $opts->add( 'days', $user->getIntOption( 'rcdays' ), FormOptions::FLOAT );
                $opts->add( 'limit', $user->getIntOption( 'rclimit' ) );
                $opts->add( 'from', '' );
 
@@ -359,7 +359,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                        if ( preg_match( '/^limit=(\d+)$/', $bit, $m ) ) {
                                $opts['limit'] = $m[1];
                        }
-                       if ( preg_match( '/^days=(\d+)$/', $bit, $m ) ) {
+                       if ( preg_match( '/^days=(\d+(?:\.\d+)?)$/', $bit, $m ) ) {
                                $opts['days'] = $m[1];
                        }
                        if ( preg_match( '/^namespace=(.*)$/', $bit, $m ) ) {
@@ -373,6 +373,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
 
        public function validateOptions( FormOptions $opts ) {
                $opts->validateIntBounds( 'limit', 0, 5000 );
+               $opts->validateBounds( 'days', 0, $this->getConfig()->get( 'RCMaxAge' ) / ( 3600 * 24 ) );
                parent::validateOptions( $opts );
        }
 
@@ -387,8 +388,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                        $query_options, $join_conds, $opts );
 
                // Calculate cutoff
-               $cutoff_unixtime = time() - ( $opts['days'] * 86400 );
-               $cutoff_unixtime = $cutoff_unixtime - ( $cutoff_unixtime % 86400 );
+               $cutoff_unixtime = time() - $opts['days'] * 3600 * 24;
                $cutoff = $dbr->timestamp( $cutoff_unixtime );
 
                $fromValid = preg_match( '/^[0-9]{14}$/', $opts['from'] );
@@ -430,13 +430,14 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                $fields[] = 'page_latest';
                $join_conds['page'] = [ 'LEFT JOIN', 'rc_cur_id=page_id' ];
 
+               $tagFilter = $opts['tagfilter'] ? explode( '|', $opts['tagfilter'] ) : [];
                ChangeTags::modifyDisplayQuery(
                        $tables,
                        $fields,
                        $conds,
                        $join_conds,
                        $query_options,
-                       $opts['tagfilter']
+                       $tagFilter
                );
 
                if ( !$this->runMainQueryHook( $tables, $fields, $conds, $query_options, $join_conds,
@@ -449,13 +450,24 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                        return false;
                }
 
+               $orderByAndLimit = [
+                       'ORDER BY' => 'rc_timestamp DESC',
+                       'LIMIT' => $opts['limit']
+               ];
+               if ( in_array( 'DISTINCT', $query_options ) ) {
+                       // ChangeTags::modifyDisplayQuery() adds DISTINCT when filtering on multiple tags.
+                       // In order to prevent DISTINCT from causing query performance problems,
+                       // we have to GROUP BY the primary key. This in turn requires us to add
+                       // the primary key to the end of the ORDER BY, and the old ORDER BY to the
+                       // start of the GROUP BY
+                       $orderByAndLimit['ORDER BY'] = 'rc_timestamp DESC, rc_id DESC';
+                       $orderByAndLimit['GROUP BY'] = 'rc_timestamp, rc_id';
+               }
                // array_merge() is used intentionally here so that hooks can, should
                // they so desire, override the ORDER BY / LIMIT condition(s); prior to
                // MediaWiki 1.26 this used to use the plus operator instead, which meant
                // that extensions weren't able to change these conditions
-               $query_options = array_merge( [
-                       'ORDER BY' => 'rc_timestamp DESC',
-                       'LIMIT' => $opts['limit'] ], $query_options );
+               $query_options = array_merge( $orderByAndLimit, $query_options );
                $rows = $dbr->select(
                        $tables,
                        $fields,
@@ -669,6 +681,15 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                                [ 'class' => 'rcfilters-container' ]
                        );
 
+                       $loadingContainer = Html::rawElement(
+                               'div',
+                               [ 'class' => 'rcfilters-spinner' ],
+                               Html::element(
+                                       'div',
+                                       [ 'class' => 'rcfilters-spinner-bounce' ]
+                               )
+                       );
+
                        // Wrap both with rcfilters-head
                        $this->getOutput()->addHTML(
                                Html::rawElement(
@@ -677,6 +698,9 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                                        $rcfilterContainer . $rcoptions
                                )
                        );
+
+                       // Add spinner
+                       $this->getOutput()->addHTML( $loadingContainer );
                } else {
                        $this->getOutput()->addHTML( $rcoptions );
                }
index b3b9210..fee336e 100644 (file)
@@ -103,15 +103,33 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges {
                        $join_conds['page'] = [ 'LEFT JOIN', 'rc_cur_id=page_id' ];
                        $select[] = 'page_latest';
                }
+
+               $tagFilter = $opts['tagfilter'] ? explode( '|', $opts['tagfilter'] ) : [];
                ChangeTags::modifyDisplayQuery(
                        $tables,
                        $select,
                        $conds,
                        $join_conds,
                        $query_options,
-                       $opts['tagfilter']
+                       $tagFilter
                );
 
+               if ( $dbr->unionSupportsOrderAndLimit() ) {
+                       if ( count( $tagFilter ) > 1 ) {
+                               // ChangeTags::modifyDisplayQuery() will have added DISTINCT.
+                               // To prevent this from causing query performance problems, we need to add
+                               // a GROUP BY, and add rc_id to the ORDER BY.
+                               $order = [
+                                       'GROUP BY' => 'rc_timestamp, rc_id',
+                                       'ORDER BY' => 'rc_timestamp DESC, rc_id DESC'
+                               ];
+                       } else {
+                               $order = [ 'ORDER BY' => 'rc_timestamp DESC' ];
+                       }
+               } else {
+                       $order = [];
+               }
+
                if ( !$this->runMainQueryHook( $tables, $select, $conds, $query_options, $join_conds,
                        $opts )
                ) {
@@ -181,12 +199,6 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges {
                                }
                        }
 
-                       if ( $dbr->unionSupportsOrderAndLimit() ) {
-                               $order = [ 'ORDER BY' => 'rc_timestamp DESC' ];
-                       } else {
-                               $order = [];
-                       }
-
                        $query = $dbr->selectSQLText(
                                array_merge( $tables, [ $link_table ] ),
                                $select,
index 451669c..4f29082 100644 (file)
@@ -130,9 +130,29 @@ class SpecialSpecialpages extends UnlistedSpecialPage {
                        );
                }
 
-               if ( $includesRestrictedPages || $includesCachedPages ) {
-                       $out->wrapWikiMsg( "<h2 class=\"mw-specialpages-note-top\">$1</h2>", 'specialpages-note-top' );
-                       $out->wrapWikiMsg( "<div class=\"mw-specialpages-notes\">\n$1\n</div>", 'specialpages-note' );
+               // add legend
+               $notes = [];
+               if ( $includesRestrictedPages ) {
+                       $restricedMsg = $this->msg( 'specialpages-note-restricted' );
+                       if ( !$restricedMsg->isDisabled() ) {
+                               $notes[] = $restricedMsg->plain();
+                       }
+               }
+               if ( $includesCachedPages ) {
+                       $cachedMsg = $this->msg( 'specialpages-note-cached' );
+                       if ( !$cachedMsg->isDisabled() ) {
+                               $notes[] = $cachedMsg->plain();
+                       }
+               }
+               if ( $notes !== [] ) {
+                       $out->wrapWikiMsg(
+                               "<h2 class=\"mw-specialpages-note-top\">$1</h2>", 'specialpages-note-top'
+                       );
+                       $out->addWikiText(
+                               "<div class=\"mw-specialpages-notes\">\n" .
+                               implode( "\n", $notes ) .
+                               "\n</div>"
+                       );
                }
        }
 }
index 810f8fb..e7478ee 100644 (file)
@@ -669,13 +669,7 @@ class SpecialUndelete extends SpecialPage {
 
                $archive = new PageArchive( $this->mTargetObj, $this->getConfig() );
                Hooks::run( 'UndeleteForm::showHistory', [ &$archive, $this->mTargetObj ] );
-               /*
-               $text = $archive->getLastRevisionText();
-               if( is_null( $text ) ) {
-                       $out->addWikiMsg( 'nohistory' );
-                       return;
-               }
-               */
+
                $out->addHTML( '<div class="mw-undelete-history">' );
                if ( $this->mAllowed ) {
                        $out->addWikiMsg( 'undeletehistory' );
@@ -858,11 +852,12 @@ class SpecialUndelete extends SpecialPage {
                        $misc = Html::hidden( 'target', $this->mTarget );
                        $misc .= Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() );
                        $history .= $misc;
-               }
-
-               $form->appendContent( new OOUI\HtmlSnippet( $history ) );
 
-               $out->addHTML( $form );
+                       $form->appendContent( new OOUI\HtmlSnippet( $history ) );
+                       $out->addHTML( $form );
+               } else {
+                       $out->addHTML( $history );
+               }
 
                return true;
        }
index 073e58d..4cdc78f 100644 (file)
@@ -33,7 +33,6 @@ use MediaWiki\MediaWikiServices;
  */
 class SpecialUpload extends SpecialPage {
        /**
-        * Constructor : initialise object
         * Get data POSTed through the form and assign them to the object
         * @param WebRequest $request Data posted.
         */
index 65131ec..549362f 100644 (file)
@@ -34,6 +34,8 @@ use Wikimedia\Rdbms\IDatabase;
 class SpecialWatchlist extends ChangesListSpecialPage {
        public function __construct( $page = 'Watchlist', $restriction = 'viewmywatchlist' ) {
                parent::__construct( $page, $restriction );
+
+               $this->maxDays = $this->getConfig()->get( 'RCMaxAge' ) / ( 3600 * 24 );
        }
 
        public function doesWrites() {
@@ -173,6 +175,11 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                return $opts;
        }
 
+       public function validateOptions( FormOptions $opts ) {
+               $opts->validateBounds( 'days', 0, $this->maxDays );
+               parent::validateOptions( $opts );
+       }
+
        /**
         * Get all custom filters
         *
@@ -255,7 +262,7 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                // Calculate cutoff
                if ( $opts['days'] > 0 ) {
                        $conds[] = 'rc_timestamp > ' .
-                               $dbr->addQuotes( $dbr->timestamp( time() - intval( $opts['days'] * 86400 ) ) );
+                               $dbr->addQuotes( $dbr->timestamp( time() - $opts['days'] * 3600 * 24 ) );
                }
        }
 
@@ -499,7 +506,7 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                if ( $opts['days'] > 0 ) {
                        $days = $opts['days'];
                } else {
-                       $days = $this->getConfig()->get( 'RCMaxAge' ) / ( 3600 * 24 );
+                       $days = $this->maxDays;
                }
                $timestamp = wfTimestampNow();
                $wlInfo = $this->msg( 'wlnote' )->numParams( $numRows, round( $days * 24 ) )->params(
@@ -599,7 +606,7 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                        $days[] = $userWatchlistOption;
                }
 
-               $maxDays = (string)( $this->getConfig()->get( 'RCMaxAge' ) / ( 3600 * 24 ) );
+               $maxDays = (string)$this->maxDays;
                // add the maximum possible value, if it isn't available already
                if ( !in_array( $maxDays, $days ) ) {
                        $days[] = $maxDays;
index 7fa03ba..142b128 100644 (file)
@@ -275,17 +275,6 @@ class UsersPager extends AlphabeticPager {
                        $groupOptions[ $groupText ] = $group;
                }
 
-               $optionsDefault = [];
-               if ( $this->editsOnly ) {
-                       $optionsDefault[] = 'editsOnly';
-               }
-               if ( $this->creationSort ) {
-                       $optionsDefault[] = 'creationSort';
-               }
-               if ( $this->mDefaultDirection ) {
-                       $optionsDefault[] = 'desc';
-               }
-
                $formDescriptor = [
                        'user' => [
                                'class' => 'HTMLUserTextField',
@@ -294,20 +283,32 @@ class UsersPager extends AlphabeticPager {
                                'value' => $this->requestedUser,
                        ],
                        'dropdown' => [
-                               'label' => $this->msg( 'group' ),
+                               'label' => $this->msg( 'group' )->text(),
                                'name' => 'group',
                                'value' => $this->requestedGroup,
                                'class' => 'HTMLSelectField',
                                'options' => $groupOptions,
                        ],
-                       'options' => [
-                               'class' => 'HTMLMultiSelectField',
-                               'options' => [
-                                       $this->msg( 'listusers-editsonly' )->text() => 'editsOnly',
-                                       $this->msg( 'listusers-creationsort' )->text() => 'creationSort',
-                                       $this->msg( 'listusers-desc' )->text() => 'desc'
-                               ],
-                               'default' => $optionsDefault
+                       'editsOnly' => [
+                               'type' => 'check',
+                               'label' => $this->msg( 'listusers-editsonly' )->text(),
+                               'name' => 'editsOnly',
+                               'id' => 'editsOnly',
+                               'value' => $this->editsOnly
+                       ],
+                       'creationSort' => [
+                               'type' => 'check',
+                               'label' => $this->msg( 'listusers-creationsort' )->text(),
+                               'name' => 'creationSort',
+                               'id' => 'creationSort',
+                               'value' => $this->creationSort
+                       ],
+                       'desc' => [
+                               'type' => 'check',
+                               'label' => $this->msg( 'listusers-desc' )->text(),
+                               'name' => 'desc',
+                               'id' => 'desc',
+                               'value' => $this->mDefaultDirection
                        ],
                        'limithiddenfield' => [
                                'class' => 'HTMLHiddenField',
index b0c12e4..4852ce5 100644 (file)
@@ -2156,7 +2156,7 @@ class Balancer {
                if (
                        $this->allowComments &&
                        !( $this->inRCDATA || $this->inRAWTEXT ) &&
-                       preg_match( Balancer::VALID_COMMENT_REGEX, $x, $regs, PREG_OFFSET_CAPTURE ) &&
+                       preg_match( self::VALID_COMMENT_REGEX, $x, $regs, PREG_OFFSET_CAPTURE ) &&
                        // verify EOF condition where necessary
                        ( $regs[4][1] < 0 || !$this->bitsIterator->valid() )
                ) {
index dbcf568..a797398 100644 (file)
@@ -81,8 +81,6 @@ class RemexCompatMunger implements TreeHandler {
        ];
 
        /**
-        * Constructor
-        *
         * @param Serializer $serializer
         */
        public function __construct( Serializer $serializer ) {
index 9a955fb..25625e7 100644 (file)
@@ -411,7 +411,7 @@ class BotPassword implements IDBAccessObject {
         * @return array|false
         */
        public static function canonicalizeLoginData( $username, $password ) {
-               $sep = BotPassword::getSeparator();
+               $sep = self::getSeparator();
                // the strlen check helps minimize the password information obtainable from timing
                if ( strlen( $password ) >= 32 && strpos( $username, $sep ) !== false ) {
                        // the separator is not valid in new usernames but might appear in legacy ones
index 7bf6be5..fa84c94 100644 (file)
@@ -603,7 +603,7 @@ class User implements IDBAccessObject {
                        ]
                );
 
-               return $id ? User::newFromId( $id ) : null;
+               return $id ? self::newFromId( $id ) : null;
        }
 
        /**
@@ -842,7 +842,7 @@ class User implements IDBAccessObject {
                global $wgContLang, $wgMaxNameChars;
 
                if ( $name == ''
-                       || User::isIP( $name )
+                       || self::isIP( $name )
                        || strpos( $name, '/' ) !== false
                        || strlen( $name ) > $wgMaxNameChars
                        || $name != $wgContLang->ucfirst( $name )
@@ -1109,17 +1109,17 @@ class User implements IDBAccessObject {
                        case false:
                                break;
                        case 'valid':
-                               if ( !User::isValidUserName( $name ) ) {
+                               if ( !self::isValidUserName( $name ) ) {
                                        $name = false;
                                }
                                break;
                        case 'usable':
-                               if ( !User::isUsableName( $name ) ) {
+                               if ( !self::isUsableName( $name ) ) {
                                        $name = false;
                                }
                                break;
                        case 'creatable':
-                               if ( !User::isCreatableName( $name ) ) {
+                               if ( !self::isCreatableName( $name ) ) {
                                        $name = false;
                                }
                                break;
@@ -1591,7 +1591,7 @@ class User implements IDBAccessObject {
                // since extensions may change the set of searchable namespaces depending
                // on user groups/permissions.
                foreach ( $wgNamespacesToBeSearchedDefault as $nsnum => $val ) {
-                       $defOpt['searchNs' . $nsnum] = (boolean)$val;
+                       $defOpt['searchNs' . $nsnum] = (bool)$val;
                }
                $defOpt['skin'] = Skin::normalizeKey( $wgDefaultSkin );
 
@@ -2212,7 +2212,7 @@ class User implements IDBAccessObject {
         * @return int The user's ID; 0 if the user is anonymous or nonexistent
         */
        public function getId() {
-               if ( $this->mId === null && $this->mName !== null && User::isIP( $this->mName ) ) {
+               if ( $this->mId === null && $this->mName !== null && self::isIP( $this->mName ) ) {
                        // Special case, we know the user is anonymous
                        return 0;
                } elseif ( !$this->isItemLoaded( 'id' ) ) {
@@ -4131,7 +4131,7 @@ class User implements IDBAccessObject {
                }
                $dbw->insert( 'user', $fields, __METHOD__, [ 'IGNORE' ] );
                if ( $dbw->affectedRows() ) {
-                       $newUser = User::newFromId( $dbw->insertId() );
+                       $newUser = self::newFromId( $dbw->insertId() );
                } else {
                        $newUser = null;
                }
@@ -5036,7 +5036,7 @@ class User implements IDBAccessObject {
                        // Do nothing
                } elseif ( $wgGroupsAddToSelf[$group] === true ) {
                        // No idea WHY this would be used, but it's there
-                       $groups['add-self'] = User::getAllGroups();
+                       $groups['add-self'] = self::getAllGroups();
                } elseif ( is_array( $wgGroupsAddToSelf[$group] ) ) {
                        $groups['add-self'] = $wgGroupsAddToSelf[$group];
                }
@@ -5044,7 +5044,7 @@ class User implements IDBAccessObject {
                if ( empty( $wgGroupsRemoveFromSelf[$group] ) ) {
                        // Do nothing
                } elseif ( $wgGroupsRemoveFromSelf[$group] === true ) {
-                       $groups['remove-self'] = User::getAllGroups();
+                       $groups['remove-self'] = self::getAllGroups();
                } elseif ( is_array( $wgGroupsRemoveFromSelf[$group] ) ) {
                        $groups['remove-self'] = $wgGroupsRemoveFromSelf[$group];
                }
@@ -5065,7 +5065,7 @@ class User implements IDBAccessObject {
                        // compatibility with old "userrights lets you change
                        // everything")
                        // Using array_merge to make the groups reindexed
-                       $all = array_merge( User::getAllGroups() );
+                       $all = array_merge( self::getAllGroups() );
                        return [
                                'add' => $all,
                                'remove' => $all,
@@ -5491,7 +5491,7 @@ class User implements IDBAccessObject {
                global $wgLang;
 
                $groups = [];
-               foreach ( User::getGroupsWithPermission( $permission ) as $group ) {
+               foreach ( self::getGroupsWithPermission( $permission ) as $group ) {
                        $groups[] = UserGroupMembership::getLink( $group, RequestContext::getMain(), 'wiki' );
                }
 
index e51a8ed..8dfe00f 100644 (file)
@@ -38,8 +38,6 @@ class ConverterRule {
        public $mUnidtable = [];// array of the translation in each variant
 
        /**
-        * Constructor
-        *
         * @param string $text The text between -{ and }-
         * @param LanguageConverter $converter
         */
index 83dff65..12f26c3 100644 (file)
@@ -208,11 +208,11 @@ class Language {
         * @return Language
         */
        protected static function newFromCode( $code, $fallback = false ) {
-               if ( !Language::isValidCode( $code ) ) {
+               if ( !self::isValidCode( $code ) ) {
                        throw new MWException( "Invalid language code \"$code\"" );
                }
 
-               if ( !Language::isValidBuiltInCode( $code ) ) {
+               if ( !self::isValidBuiltInCode( $code ) ) {
                        // It's not possible to customise this code with class files, so
                        // just return a Language object. This is to support uselang= hacks.
                        $lang = new Language;
@@ -228,9 +228,9 @@ class Language {
                }
 
                // Keep trying the fallback list until we find an existing class
-               $fallbacks = Language::getFallbacksFor( $code );
+               $fallbacks = self::getFallbacksFor( $code );
                foreach ( $fallbacks as $fallbackCode ) {
-                       if ( !Language::isValidBuiltInCode( $fallbackCode ) ) {
+                       if ( !self::isValidBuiltInCode( $fallbackCode ) ) {
                                throw new MWException( "Invalid fallback '$fallbackCode' in fallback sequence for '$code'" );
                        }
 
@@ -829,7 +829,7 @@ class Language {
                global $wgExtraLanguageNames, $wgUsePigLatinVariant;
 
                // If passed an invalid language code to use, fallback to en
-               if ( $inLanguage !== null && !Language::isValidCode( $inLanguage ) ) {
+               if ( $inLanguage !== null && !self::isValidCode( $inLanguage ) ) {
                        $inLanguage = 'en';
                }
 
@@ -1192,7 +1192,7 @@ class Language {
                                case 'D':
                                        $usedDay = true;
                                        $s .= $this->getWeekdayAbbreviation(
-                                               Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'w' ) + 1
+                                               self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'w' ) + 1
                                        );
                                        break;
                                case 'j':
@@ -1223,7 +1223,7 @@ class Language {
                                case 'l':
                                        $usedDay = true;
                                        $s .= $this->getWeekdayName(
-                                               Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'w' ) + 1
+                                               self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'w' ) + 1
                                        );
                                        break;
                                case 'F':
@@ -1404,36 +1404,36 @@ class Language {
                                case 'O':
                                case 'P':
                                case 'T':
-                                       $s .= Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
+                                       $s .= self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
                                        break;
                                case 'w':
                                case 'N':
                                case 'z':
                                        $usedDay = true;
-                                       $num = Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
+                                       $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
                                        break;
                                case 'W':
                                        $usedWeek = true;
-                                       $num = Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
+                                       $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
                                        break;
                                case 't':
                                        $usedMonth = true;
-                                       $num = Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
+                                       $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
                                        break;
                                case 'L':
                                        $usedIsLeapYear = true;
-                                       $num = Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
+                                       $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
                                        break;
                                case 'o':
                                        $usedISOYear = true;
-                                       $num = Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
+                                       $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
                                        break;
                                case 'U':
                                        $usedSecond = true;
                                        // fall through
                                case 'I':
                                case 'Z':
-                                       $num = Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
+                                       $num = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
                                        break;
                                case '\\':
                                        # Backslash escaping
@@ -1467,7 +1467,7 @@ class Language {
                                        $s .= $num;
                                        $raw = false;
                                } elseif ( $roman ) {
-                                       $s .= Language::romanNumeral( $num );
+                                       $s .= self::romanNumeral( $num );
                                        $roman = false;
                                } elseif ( $hebrewNum ) {
                                        $s .= self::hebrewNumeral( $num );
@@ -1509,7 +1509,7 @@ class Language {
                                substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
                        if ( $usedWeek ) {
                                $possibleTtls[] =
-                                       ( 7 - Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'N' ) ) * 86400 +
+                                       ( 7 - self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'N' ) ) * 86400 +
                                        $timeRemainingInDay;
                        } elseif ( $usedISOYear ) {
                                // December 28th falls on the last ISO week of the year, every year.
@@ -1519,29 +1519,29 @@ class Language {
                                        substr( $ts, 0, 4 ) . '1228',
                                        $zone ?: new DateTimeZone( 'UTC' )
                                )->format( 'W' );
-                               $currentISOWeek = Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'W' );
+                               $currentISOWeek = self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'W' );
                                $weeksRemaining = $lastWeekOfISOYear - $currentISOWeek;
                                $timeRemainingInWeek =
-                                       ( 7 - Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'N' ) ) * 86400
+                                       ( 7 - self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'N' ) ) * 86400
                                        + $timeRemainingInDay;
                                $possibleTtls[] = $weeksRemaining * 604800 + $timeRemainingInWeek;
                        }
 
                        if ( $usedMonth ) {
                                $possibleTtls[] =
-                                       ( Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 't' ) -
+                                       ( self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 't' ) -
                                                substr( $ts, 6, 2 ) ) * 86400
                                        + $timeRemainingInDay;
                        } elseif ( $usedYear ) {
                                $possibleTtls[] =
-                                       ( Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'L' ) + 364 -
-                                               Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'z' ) ) * 86400
+                                       ( self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'L' ) + 364 -
+                                               self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'z' ) ) * 86400
                                        + $timeRemainingInDay;
                        } elseif ( $usedIsLeapYear ) {
                                $year = substr( $ts, 0, 4 );
                                $timeRemainingInYear =
-                                       ( Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'L' ) + 364 -
-                                               Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'z' ) ) * 86400
+                                       ( self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'L' ) + 364 -
+                                               self::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'z' ) ) * 86400
                                        + $timeRemainingInDay;
                                $mod = $year % 4;
                                if ( $mod || ( !( $year % 100 ) && $year % 400 ) ) {
@@ -3956,7 +3956,7 @@ class Language {
         * @return string Text, wrapped in LRE...PDF or RLE...PDF or nothing
         */
        public function embedBidi( $text = '' ) {
-               $dir = Language::strongDirFromContent( $text );
+               $dir = self::strongDirFromContent( $text );
                if ( $dir === 'ltr' ) {
                        // Wrap in LEFT-TO-RIGHT EMBEDDING ... POP DIRECTIONAL FORMATTING
                        return self::$lre . $text . self::$pdf;
@@ -4264,7 +4264,7 @@ class Language {
                        $this->mParentLanguage = null;
                        return null;
                }
-               $lang = Language::factory( $code );
+               $lang = self::factory( $code );
                if ( !$lang->hasVariant( $this->getCode() ) ) {
                        $this->mParentLanguage = null;
                        return null;
@@ -4420,7 +4420,7 @@ class Language {
         * @return array Non-empty array, ending in "en"
         */
        public static function getFallbacksFor( $code ) {
-               if ( $code === 'en' || !Language::isValidBuiltInCode( $code ) ) {
+               if ( $code === 'en' || !self::isValidBuiltInCode( $code ) ) {
                        return [];
                }
                // For unknown languages, fallbackSequence returns an empty array,
@@ -4821,7 +4821,7 @@ class Language {
         */
        public function getCompiledPluralRules() {
                $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ), 'compiledPluralRules' );
-               $fallbacks = Language::getFallbacksFor( $this->mCode );
+               $fallbacks = self::getFallbacksFor( $this->mCode );
                if ( !$pluralRules ) {
                        foreach ( $fallbacks as $fallbackCode ) {
                                $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ), 'compiledPluralRules' );
@@ -4840,7 +4840,7 @@ class Language {
         */
        public function getPluralRules() {
                $pluralRules = self::$dataCache->getItem( strtolower( $this->mCode ), 'pluralRules' );
-               $fallbacks = Language::getFallbacksFor( $this->mCode );
+               $fallbacks = self::getFallbacksFor( $this->mCode );
                if ( !$pluralRules ) {
                        foreach ( $fallbacks as $fallbackCode ) {
                                $pluralRules = self::$dataCache->getItem( strtolower( $fallbackCode ), 'pluralRules' );
@@ -4859,7 +4859,7 @@ class Language {
         */
        public function getPluralRuleTypes() {
                $pluralRuleTypes = self::$dataCache->getItem( strtolower( $this->mCode ), 'pluralRuleTypes' );
-               $fallbacks = Language::getFallbacksFor( $this->mCode );
+               $fallbacks = self::getFallbacksFor( $this->mCode );
                if ( !$pluralRuleTypes ) {
                        foreach ( $fallbacks as $fallbackCode ) {
                                $pluralRuleTypes = self::$dataCache->getItem( strtolower( $fallbackCode ), 'pluralRuleTypes' );
index 00d91ce..19ff2a4 100644 (file)
@@ -383,6 +383,8 @@ class Names {
                'si' => 'සිංහල', # Sinhalese
                'simple' => 'Simple English', # Simple English
                'sk' => 'slovenčina', # Slovak
+               'skr' => 'سرائیکی', # Saraiki (multiple scripts - defaults to Arabic)
+               'skr-arab' => 'سرائیکی', # Saraiki (Arabic script)
                'sl' => 'slovenščina', # Slovenian
                'sli' => 'Schläsch', # Lower Selisian
                'sm' => 'Gagana Samoa', # Samoan
index 870d7b7..c098ef2 100644 (file)
        "rcfilters-legend-heading": "<strong>Llista d'abreviatures:</strong>",
        "rcfilters-activefilters": "Filtros activos",
        "rcfilters-advancedfilters": "Filtros avanzaos",
+       "rcfilters-limit-title": "Cambios a amosar",
+       "rcfilters-limit-shownum": "Amosar los últimos $1 cambios",
+       "rcfilters-days-title": "Últimos díes",
+       "rcfilters-hours-title": "Últimes hores",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|día|díes}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|hora|hores}}",
        "rcfilters-quickfilters": "Filtros guardaos",
        "rcfilters-quickfilters-placeholder-title": "Entá nun se guardaron enllaces",
        "rcfilters-quickfilters-placeholder-description": "Pa guardar les preferencies del filtru y volver a usales sero, pulsia nel iconu del marcador del área de Filtru Activu más abaxo.",
        "rcfilters-invalid-filter": "Filtru inválidu",
        "rcfilters-empty-filter": "Nun hai filtros activos. Amuésense toles contribuciones.",
        "rcfilters-filterlist-title": "Filtros",
-       "rcfilters-filterlist-whatsthis": "¿Qué ye esto?",
+       "rcfilters-filterlist-whatsthis": "¿Como funciona esto?",
        "rcfilters-filterlist-feedbacklink": "Comentar sobro los nuevos filtros (beta)",
        "rcfilters-highlightbutton-title": "Resaltar resultaos",
        "rcfilters-highlightmenu-title": "Seleiciona un color",
        "rcfilters-filter-editsbyself-description": "Contribuciones de to.",
        "rcfilters-filter-editsbyother-label": "Cambios d'otros",
        "rcfilters-filter-editsbyother-description": "Tolos cambios menos los de to.",
-       "rcfilters-filtergroup-userExpLevel": "Nivel d'esperiencia (solo pa usuarios rexistraos)",
+       "rcfilters-filtergroup-userExpLevel": "Rexistru d'usuarios y esperiencia",
        "rcfilters-filter-user-experience-level-registered-label": "Rexistraos",
        "rcfilters-filter-user-experience-level-registered-description": "Editores coneutaos.",
        "rcfilters-filter-user-experience-level-unregistered-label": "Non rexistraos",
-       "rcfilters-filter-user-experience-level-unregistered-description": "Editores ensin coneutar.",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Editores que nun tán coneutaos.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Recién llegaos",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Menos de 10 ediciones y 4 díes d'actividá.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Editores rexistraos con menos de 10 ediciones y 4 díes d'actividá.",
        "rcfilters-filter-user-experience-level-learner-label": "Aprendices",
-       "rcfilters-filter-user-experience-level-learner-description": "Más esperiencia que los «Recién llegaos», pero menos que los «Usuarios espertos».",
+       "rcfilters-filter-user-experience-level-learner-description": "Editores rexistraos con esperiencia ente «Recién llegaos» y «Usuarios espertos».",
        "rcfilters-filter-user-experience-level-experienced-label": "Usuarios espertos",
-       "rcfilters-filter-user-experience-level-experienced-description": "Más de 30 díes d'actividá y 500 ediciones.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Editores rexistraos con más de 500 ediciones y 30 díes d'actividá.",
        "rcfilters-filtergroup-automated": "Contribuciones automátiques",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Ediciones feches con ferramientes automátiques.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "El filtru «Ediciones menores» fai conflictu con un filtru «Tipu de cambiu» o más, porque dellos tipos de cambiu nun pueden designase como «menores». Los filtros que faen conflictu tan marcaos nel área de Filtros Activos, más arriba.",
        "rcfilters-hideminor-conflicts-typeofchange": "Dellos tipos de cambiu nun pueden designase como «menores», de manera qu'esti filtru fai conflictu colos siguientes filtros «Tipu de cambiu»: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Esti filtru de «Tipu de cambiu» fai conflictu col filtru «Ediciones menores». Dellos tipos de cambiu nun pueden designase como «menores».",
-       "rcfilters-filtergroup-lastRevision": "Última revisión",
+       "rcfilters-filtergroup-lastRevision": "Últimes revisiones",
        "rcfilters-filter-lastrevision-label": "Última revisión",
-       "rcfilters-filter-lastrevision-description": "El cambio más recien d'una páxina.",
-       "rcfilters-filter-previousrevision-label": "Revisiones anteriores",
-       "rcfilters-filter-previousrevision-description": "Tolos cambios que nun son los más recien d'una páxina.",
+       "rcfilters-filter-lastrevision-description": "Sólo el cambiu más recien d'una páxina.",
+       "rcfilters-filter-previousrevision-label": "Non la cabera revisión",
+       "rcfilters-filter-previousrevision-description": "Tolos cambios que nun son la «cabera revisión».",
        "rcfilters-filter-excluded": "Escluíu",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:non</strong> $1",
+       "rcfilters-exclude-button-off": "Torgar los seleicionaos",
+       "rcfilters-exclude-button-on": "Torgando los seleicionaos",
        "rcfilters-view-tags": "Ediciones etiquetaes",
        "rcfilters-view-namespaces-tooltip": "Filtriar los resultaos por espaciu de nomes",
        "rcfilters-view-tags-tooltip": "Filtriar los resultaos usando les etiquetes d'edición",
        "delete-warning-toobig": "Esta páxina tien un historial d'ediciones grande, más de $1 {{PLURAL:$1|revisión|revisiones}}.\nEsborralu pue perturbar les operaciones de la base de datos de {{SITENAME}};\nobra con precaución.",
        "deleteprotected": "Nun pues desaniciar esta páxina porque ta protexida.",
        "deleting-backlinks-warning": "<strong>Avisu:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|Otres páxines]] enllacen a, o trescluyen de, la páxina que tas a piques de desaniciar.",
+       "deleting-subpages-warning": "<strong>Avisu:</strong> La páxina que vas desaniciar tien [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|una subpáxina|$1 subpáxines|51=más de 50 subpáxines}}]].",
        "rollback": "Revertir ediciones",
        "rollbacklink": "revertir",
        "rollbacklinkcount": "revertir $1 {{PLURAL:$1|edición|ediciones}}",
        "fileduplicatesearch-noresults": "Nun s'alcontró dengún ficheru nomáu «$1».",
        "specialpages": "Páxines especiales",
        "specialpages-note-top": "Lleenda",
-       "specialpages-note": "* Páxines especiales normales.\n* <span class=\"mw-specialpagerestricted\">Páxines especiales restrinxíes.</span>",
        "specialpages-group-maintenance": "Informes de mantenimientu",
        "specialpages-group-other": "Otres páxines especiales",
        "specialpages-group-login": "Entrar / crear cuenta",
index 44e67df..246ddaa 100644 (file)
        "thu": "Кс",
        "fri": "Йм",
        "sat": "Шб",
-       "january": "Ò\93инÑ\83аÑ\80",
-       "february": "февраль",
-       "march": "маÑ\80Ñ\82",
-       "april": "апÑ\80елÑ\8c",
-       "may_long": "май (Ò»абанай)",
-       "june": "иÑ\8eнÑ\8c",
-       "july": "иÑ\8eлÑ\8c",
-       "august": "авгÑ\83Ñ\81Ñ\82",
-       "september": "сентябрь",
-       "october": "окÑ\82Ñ\8fбÑ\80Ñ\8c",
-       "november": "ноÑ\8fбÑ\80Ñ\8c",
-       "december": "декабÑ\80Ñ\8c",
-       "january-gen": "Ò\93инÑ\83аÑ\80",
-       "february-gen": "февраль",
-       "march-gen": "маÑ\80Ñ\82",
-       "april-gen": "апÑ\80елÑ\8c",
-       "may-gen": "май",
-       "june-gen": "иÑ\8eнÑ\8c",
-       "july-gen": "иÑ\8eлÑ\8c",
-       "august-gen": "авгÑ\83Ñ\81Ñ\82",
-       "september-gen": "сентябрь",
-       "october-gen": "окÑ\82Ñ\8fбÑ\80Ñ\8c",
-       "november-gen": "ноÑ\8fбÑ\80Ñ\8c",
-       "december-gen": "декабÑ\80Ñ\8c",
-       "jan": "Ò\93ин",
-       "feb": "фев",
-       "mar": "мар",
-       "apr": "апр",
-       "may": "май",
-       "jun": "июн",
-       "jul": "июл",
-       "aug": "авг",
-       "sep": "сен",
-       "oct": "окт",
-       "nov": "ноя",
-       "dec": "дек",
+       "january": "Ò\92инÑ\83аÑ\80 (ÒºÑ\8bÑ\83Ñ\8bÒ\93ай)",
+       "february": "Февраль (Шаҡай)",
+       "march": "Ð\9cаÑ\80Ñ\82 (Ð\91Ñ\83Ñ\80анай)",
+       "april": "Ð\90пÑ\80елÑ\8c (Ð\90лаÒ\93аÑ\80ай)",
+       "may_long": "Ð\9cай (Òºабанай)",
+       "june": "Ð\98Ñ\8eнÑ\8c (ÒºÓ©Ñ\82ай)",
+       "july": "Ð\98Ñ\8eлÑ\8c (Ð\9cайай)",
+       "august": "Ð\90вгÑ\83Ñ\81Ñ\82 (УÑ\80аÒ\93ай)",
+       "september": "Сентябрь (Һарысай)",
+       "october": "Ð\9eкÑ\82Ñ\8fбÑ\80Ñ\8c (ҠаÑ\80аÑ\81ай)",
+       "november": "Ð\9dоÑ\8fбÑ\80Ñ\8c (Ò Ñ\8bÑ\80паÒ\93ай)",
+       "december": "Ð\94екабÑ\80Ñ\8c (Ð\90Ò¡Ñ\8aÑ\8eлай)",
+       "january-gen": "Ò\92инÑ\83аÑ\80 (ÒºÑ\8bÑ\83Ñ\8bÒ\93ай)",
+       "february-gen": "Февраль (Шаҡай)",
+       "march-gen": "Ð\9cаÑ\80Ñ\82 (Ð\91Ñ\83Ñ\80анай)",
+       "april-gen": "Ð\90пÑ\80елÑ\8c (Ð\90лаÒ\93аÑ\80ай)",
+       "may-gen": "Ð\9cай (Һабанай)",
+       "june-gen": "Ð\98Ñ\8eнÑ\8c (ÒºÓ©Ñ\82ай)",
+       "july-gen": "Ð\98Ñ\8eлÑ\8c (Ð\9cайай)",
+       "august-gen": "Ð\90вгÑ\83Ñ\81Ñ\82 (УÑ\80аÒ\93ай)",
+       "september-gen": "Сентябрь (Һарысай)",
+       "october-gen": "Ð\9eкÑ\82Ñ\8fбÑ\80Ñ\8c (ҠаÑ\80аÑ\81ай)",
+       "november-gen": "Ð\9dоÑ\8fбÑ\80Ñ\8c (Ò Ñ\8bÑ\80паÒ\93ай)",
+       "december-gen": "Ð\94екабÑ\80Ñ\8c (Ð\90Ò¡Ñ\8aÑ\8eлай)",
+       "jan": "Ò\92ин",
+       "feb": "Фев",
+       "mar": "Ð\9cар",
+       "apr": "Ð\90пр",
+       "may": "Ð\9cай",
+       "jun": "Ð\98юн",
+       "jul": "Ð\98юл",
+       "aug": "Ð\90вг",
+       "sep": "Сен",
+       "oct": "Ð\9eкт",
+       "nov": "Ð\9dоя",
+       "dec": "Ð\94ек",
        "january-date": "Ғинуар $1",
        "february-date": "Февраль $1",
        "march-date": "Март $1",
        "september-date": "Сентябрь $1",
        "october-date": "Октябрь $1",
        "november-date": "Ноябрь $1",
-       "december-date": "СенÑ\82Ñ\8fбрь $1",
+       "december-date": "Ð\94екабрь $1",
        "period-am": "ТК",
        "period-pm": "ТС",
        "pagecategories": "{{PLURAL:$1|1=Категория|Категориялар}}",
        "category-empty": "\"Был категория әлегә буш.\"",
        "hidden-categories": "{{PLURAL:$1|Йәшерен категория|Йәшерен категориялар}}",
        "hidden-category-category": "Йәшерен категориялар",
-       "category-subcat-count": "{{PLURAL:$2|Был категорияла тик киләһе эске категория ғына бар.|Барлығы $2 категориянан, был категорияла киләһе  {{PLURAL:$1|эске категория|$1 эске категория}} күрһәтелә.}}",
+       "category-subcat-count": "{{PLURAL:$2|Был категорияла тик киләһе эске категория ғына бар.|Барлығы $2 категориянан, был категорияла киләһе {{PLURAL:$1|эске категория|$1 эске категория}} күрһәтелә.}}",
        "category-subcat-count-limited": "Был категорияға киләһе {{PLURAL:$1|эске категория|$1 эске категория}} ингән.",
        "category-article-count": "{{PLURAL:$2|1=Был категорияла бер генә бит бар.|Категориялағы $2 биттең $1 бите күрһәтелгән.}}",
        "category-article-count-limited": "Был категорияла {{PLURAL:$1|$1 бит}} бар.",
        "category-file-count": "{{PLURAL:$2|Был категорияла бер генә файл бар.|Категориялағы $2 файлдың {{PLURAL:$1|$1 файлы күрһәтелгән}}.}}",
        "category-file-count-limited": "Был категорияла {{PLURAL:$1|$1 файл}} бар.",
-       "listingcontinuesabbrev": "(дауамы)",
+       "listingcontinuesabbrev": "дауамы",
        "index-category": "Индексланған биттәр",
        "noindex-category": "Индексланмаған биттәр",
        "broken-file-category": "Файлға һылтанмалары эшләмәгән биттәр",
        "newwindow": "(яңы биттә)",
        "cancel": "Кире алырға",
        "moredotdotdot": "Дауамы...",
-       "morenotlisted": "Был исемлек тулы түгел",
+       "morenotlisted": "Был исемлек тулы түгел.",
        "mypage": "Бит",
        "mytalk": "Әңгәмә",
        "anontalk": "Әңгәмә",
        "navigation": "Төп йүнәлештәр",
        "and": "&#32;һәм",
-       "faq": "ЙБҺ",
+       "faq": "ЙБҺ (ЧаВо)",
        "actions": "Ғәмәлдәр",
        "namespaces": "Исем арауыҡтары",
        "variants": "Варианттар",
        "tagline": "{{SITENAME}} проектынан",
        "help": "Белешмә",
        "search": "Эҙләү",
-       "search-ignored-headings": " #<!-- был юлды нисек бар шулай ҡалдырығыҙ --> <pre>\n# Эҙләүҙәр инҡар иткән атамалар.\n# Атамаһы булған бит индексланғас та, үҙгәртмәләр үҙ көсөнә инәсәк.\n# Буш төҙәтеү менән һеҙ битте яңынан индекслата алаһығыҙ\n# Синтаксис шулай күренә:\n#   * Ошо символға «#» башланған юлдың аҙағына тиклем комментарий була\n#   * Һәр буш булмаған юл - инҡар ителгәндең атамаһы, быға регистр ҙа инә\nИҫкәрмәләр\nҺылтанмалар\nҠарағыҙ шулай уҡ\n#</pre> <!-- был юлды шул көйө ҡалдырығыҙ -->",
+       "search-ignored-headings": " #<!-- был юлды нисек бар, шулай ҡалдырығыҙ --> <pre>\n# Эҙләүҙәр инҡар иткән атамалар.\n# Атамаһы булған бит индексланғас та, үҙгәртмәләр үҙ көсөнә инәсәк.\n# Буш төҙәтеү менән һеҙ битте яңынан индекслата алаһығыҙ\n# Синтаксис шулай күренә:\n#   * Ошо символға «#» башланған юлдың аҙағына тиклем комментарий була\n#   * Һәр буш булмаған юл - инҡар ителгәндең атамаһы, быға регистр ҙа инә\nИҫкәрмәләр\nҺылтанмалар\nҠарағыҙ шулай уҡ\n#</pre> <!-- был юлды шул көйө ҡалдырығыҙ -->",
        "searchbutton": "Эҙләү",
        "go": "Күсеү",
        "searcharticle": "Күсеү",
        "updatedmarker": "һуңғы инеүемдән һуң яңыртылған",
        "printableversion": "Баҫтырыу өлгөһө",
        "permalink": "Даими һылтанма",
-       "print": "Баҫыу",
+       "print": "Ð\91аҫÑ\82Ñ\8bÑ\80Ñ\8bÑ\83",
        "view": "Ҡарау",
        "view-foreign": "$1 сайтында ҡарау",
        "edit": "Үҙгәртеү",
        "edit-local": "Локаль тасуирламаны үҙгәртергә",
-       "create": "Төҙөргә",
+       "create": "Төҙөү",
        "create-local": "Локаль тасуирлама өҫтәргә",
-       "delete": "Юҡ  итергә",
+       "delete": "Юйырға",
        "undelete_short": "$1 {{PLURAL:$1|үҙгәртеүҙе}} тергеҙергә",
        "viewdeleted_short": "{{PLURAL:$1|1=1 юйылған үҙгәртеүҙе|$1 юйылған үҙгәртеүҙе}} ҡарау",
        "protect": "Һаҡларға",
        "talkpagelinktext": "әңг.",
        "specialpage": "Ярҙамсы бит",
        "personaltools": "Шәхси ҡоралдар",
-       "talk": "Әңгәмә",
+       "talk": "Фекер алышыу",
        "views": "Ҡарауҙар",
        "toolbox": "Ҡоралдар",
        "tool-link-userrights": "{{GENDER:$1|Ҡатнашыусы}} төркөмдәрен үҙгәртергә",
-       "tool-link-userrights-readonly": "{{GENDER:$1|Ҡатнашыусы|Ҡатнашыулар}} төркөмдәрен ҡарарға",
+       "tool-link-userrights-readonly": "{{GENDER:$1|Ҡатнашыусы}} төркөмдәрен ҡарарға",
        "tool-link-emailuser": "{{GENDER:$1|Ҡатнашыусыға}} хат яҙырға",
        "imagepage": "Файл битен ҡарарға",
        "mediawikipage": "Хәбәрҙәр битен ҡарарға",
        "jumpto": "Унда күсергә:",
        "jumptonavigation": "төп йүнәлештәр",
        "jumptosearch": "эҙләү",
-       "view-pool-error": "Ò\92Ó\99Ñ\84Ò¯ Ð¸Ñ\82егеÒ\99, Ñ\85Ó\99Ò\99еÑ\80ге Ð²Ð°Ò¡Ñ\8bÑ\82Ñ\82а Ñ\81еÑ\80веÑ\80Ò\99аÑ\80 Ð°Ñ\80Ñ\82Ñ\8bÒ¡ Ñ\82ейÓ\99лгÓ\99н.\nБыл битте ҡарарға теләүселәр бик күп.\nБыл биткә һуңғарак кереп ҡарағыҙ.\n\n$1",
-       "generic-pool-error": "Ò\92Ó\99Ñ\84Ò¯ Ð¸Ñ\82егеÒ\99, Ñ\85Ó\99Ò\99еÑ\80ге Ð²Ð°Ò¡Ñ\8bÑ\82Ñ\82а Ñ\81еÑ\80веÑ\80Ò\99аÑ\80 ÐºÓ©Ñ\81Ó©Ñ\80гÓ\99неÑ\88ле Ñ\8dÑ\88лÓ\99й.\nÐ\91Ñ\8bл Ð±Ð¾Ð»Ð´Ñ\8b Ò¡Ð°Ñ\80аÑ\80Ò\93а Ñ\82елÓ\99Ò¯Ñ\81елÓ\99Ñ\80 Ð±Ð¸Ðº ÐºÒ¯Ð¿.\nÐ\97инһаÑ\80, Ð±ер ни тиклем көтөгөҙ һәм һуңыраҡ тағы мөрәжәғәт итеп ҡарағыҙ.",
+       "view-pool-error": "Ò\92Ó\99Ñ\84Ò¯ Ð¸Ñ\82егеÒ\99, Ñ\85Ó\99Ò\99еÑ\80ге Ð²Ð°Ò¡Ñ\8bÑ\82Ñ\82а Ñ\81еÑ\80веÑ\80Ò\99аÑ\80 ÐºÓ©Ñ\81Ó©Ñ\80гÓ\99неÑ\88ле Ñ\8dÑ\88лÓ\99й.\nБыл битте ҡарарға теләүселәр бик күп.\nБыл биткә һуңғарак кереп ҡарағыҙ.\n\n$1",
+       "generic-pool-error": "Ò\92Ó\99Ñ\84Ò¯ Ð¸Ñ\82егеÒ\99, Ñ\85Ó\99Ò\99еÑ\80ге Ð²Ð°Ò¡Ñ\8bÑ\82Ñ\82а Ñ\81еÑ\80веÑ\80Ò\99аÑ\80 ÐºÓ©Ñ\81Ó©Ñ\80гÓ\99неÑ\88ле Ñ\8dÑ\88лÓ\99й.\nÐ\91Ñ\8bл Ð±Ð¾Ð»Ð´Ñ\8b Ò¡Ð°Ñ\80аÑ\80Ò\93а Ñ\82елÓ\99Ò¯Ñ\81елÓ\99Ñ\80 Ð±Ð¸Ðº ÐºÒ¯Ð¿.\nÐ\91ер ни тиклем көтөгөҙ һәм һуңыраҡ тағы мөрәжәғәт итеп ҡарағыҙ.",
        "pool-timeout": "Блоклауҙы көтөү ваҡыты үтте",
        "pool-queuefull": "Һорауҙар сираты тулы",
        "pool-errorunknown": "Билдәһеҙ хата",
        "disclaimerpage": "Project:Яуаплылыҡтан баш тартыу",
        "edithelp": "Төҙәтеү белешмәһе",
        "helppage-top-gethelp": "Ярҙам",
-       "mainpage": "Ð\91аÑ\88 Ð±ит",
+       "mainpage": "Ð\91аÑ\88 Ð\91ит",
        "mainpage-description": "Баш бит",
        "policy-url": "Project:Ҡағиҙәләр",
        "portal": "Берләшмә",
        "badaccess": "Кереү хатаһы",
        "badaccess-group0": "Һоратылған ғәмәлде үтәй алмайһығыҙ.",
        "badaccess-groups": "Һоратылған ғәмәлде киләһе {{PLURAL:$2|1=төркөм|төркөмдәр}} ҡулланыусылары ғына башҡара ала: $1.",
-       "versionrequired": "MediaWiki-ның $1 версияһы кәрәкле",
+       "versionrequired": "MediaWiki-ның $1 версияһы кәрәк",
        "versionrequiredtext": "Был бит менән эшләү өсөн MediaWiki-ның $1 версияһы кәрәк. [[Special:Version|Ҡулланылған версия тураһында мәғлүмәт битен]] ҡара.",
        "ok": "Тамам",
        "pagetitle": "{{SITENAME}} проектынан",
        "retrievedfrom": "Сығанағы — «$1»",
-       "youhavenewmessages": "Яңы $1 бар ($2).",
+       "youhavenewmessages": "{{PLURAL:$3|Һеҙгә}} $1 ($2) бар.",
        "youhavenewmessagesfromusers": "{{PLURAL:$4|Һеҙгә}} {{PLURAL:$3|$3 ҡатнашыусыһынан}} $1 килде ($2).",
        "youhavenewmessagesmanyusers": "Һеҙгә күп ҡатнашыусынан $1 бар ($2).",
        "newmessageslinkplural": "{{PLURAL:$1|1=яңы хәбәр|яңы хәбәр}}",
        "botpasswords-updated-body": "$1 роботы өсөн $2 ҡулланыусыһы серһүҙе яңыртылды.",
        "botpasswords-deleted-title": "Робот серһүҙе юйылды.",
        "botpasswords-deleted-body": "$1 роботы өсөн $2 ҡулланыусыһы серһүҙе юйылды.",
-       "botpasswords-newpassword": "Ð\98неү Ó©Ñ\81өн Ñ\8fÒ£Ñ\8b Ñ\81еÑ\80Ò»Ò¯Ò\99 <strong>$1</strong> â\80\94 <strong>$2</strong>. <em>Ð\90Ñ\80Ñ\82абан Ò¡Ñ\83лланÑ\8bÑ\83 Ó©Ñ\81өн Ñ\8fÒ»Ñ\8bп Ð°Ð»Ñ\8bÒ\93Ñ\8bÒ\99.</em><strong>$3</strong> Ò¡Ð°Ñ\82наÑ\88Ñ\8bÑ\83Ñ\81Ñ\8b Ð¸Ñ\81еме <strong>$4</strong> Ð¿Ð°Ñ\80олÑ\8c Ñ\81иÑ\84аÑ\82Ñ\8b)",
+       "botpasswords-newpassword": "Ð\98неү Ó©Ñ\81өн Ñ\8fÒ£Ñ\8b Ñ\81еÑ\80Ò»Ò¯Ò\99 <strong>$1</strong> â\80\94 <strong>$2</strong>. <em>Ð\90Ñ\80Ñ\82абан Ò¡Ñ\83лланÑ\8bÑ\83 Ó©Ñ\81өн Ñ\8fÒ\99Ñ\8bп Ð°Ð»Ñ\8bÒ\93Ñ\8bÒ\99.</em> <br /> (Ð\98Ò«Ó\99п Ñ\8fÒ\99маһÑ\8b Ð¼ÐµÐ½Ó\99н Ò¡Ð°Ñ\82наÑ\88Ñ\8bÑ\83Ñ\81Ñ\8bнÑ\8bÒ£ Ð¸Ñ\81еме Ð±ÐµÑ\80 Ð±Ñ\83лÑ\8bÑ\83Ñ\8bн Ñ\82алап Ð¸Ñ\82кÓ\99н Ð¸Ò«ÐºÐµ Ð±Ð¾Ñ\82Ñ\82аÑ\80 Ó©Ñ\81өн, <strong>$3</strong> Ò¡Ð°Ñ\82наÑ\88Ñ\8bÑ\83Ñ\81Ñ\8b Ð¸Ñ\81еме Ð¸Ñ\82еп Ò»Ó\99м <strong>$4</strong> Ñ\81еÑ\80Ò»Ò¯Ò\99 Ð¸Ñ\82еп Ò¡Ñ\83ллана Ð°Ð»Ð°Ò»Ñ\8bÒ\93Ñ\8bÒ\99.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider ғәмәлдә түгел.",
        "botpasswords-restriction-failed": "Робот серһүҙе менән бәйле сәбәптәр булғанға инеү башҡарылманы.",
        "botpasswords-invalid-name": "Күрһәтелгән ҡулланыусы исемендә робот $1 серһүҙен бүлеүсе тамға юҡ.",
        "passwordreset-emailelement": "Ҡулланыусы исеме: \n$1\n\nВаҡытлыса серһүҙ: \n$2",
        "passwordreset-emailsentemail": "Серһүҙҙе ташлау тураһындағы мәғлүмәт менән электрон почта аша хат ебәрелде.",
        "passwordreset-emailsentusername": "Әгәр был ҡатнашыусының исеменә бәйле  электрон почтаһының адресы булһа, ул саҡта  серһүҙҙе тергеҙеү өсөн  хат ебәреләсәк.",
+       "passwordreset-nocaller": "Мөрәжәғәт сығанағы күрһәтелергә тейеш",
+       "passwordreset-nosuchcaller": "Мөрәжәғәт сығанағы юҡ: $1",
+       "passwordreset-ignored": "Серһүҙҙе ташлау эшләнмәне. Бәлки, бер провайдер ҙа көйләнмәгәндер?",
        "passwordreset-invalidemail": "Электрон почта адресы ҡабул ителмәй",
+       "passwordreset-nodata": "Ҡатнашыусы исеме лә, электрон почта адресы ла күрһәтелмәгән",
        "changeemail": "Электрон почта адресын үҙгәртергә",
        "changeemail-header": "Электрон почта адресын үҙгәртеү",
        "changeemail-no-info": "Был биткә туранан ирешеү өсөн һеҙгә системала танылыу кәрәк.",
        "preview": "Ҡарап сығыу",
        "showpreview": "Ҡарап сығырға",
        "showdiff": "Индерелгән үҙгәрештәр",
-       "blankarticle": "<strong>Иҫкәртеү:</strong> Һеҙ булдырасаҡ бит буш.\nӘгәр тағы ла «$1» кнопкаға баҫһағыҙ, шул уҡ йөкмәткеле бит  яңынан барлыҡҡа киләсәк.",
+       "blankarticle": "<strong>Иҫкәртеү:</strong> Һеҙ булдырасаҡ бит буш.\nӘгәр тағы ла «$1» төймәһенә баҫһағыҙ, йөкмәткеһеҙ бит барлыҡҡа киләсәк.",
        "anoneditwarning": "<strong>Иғтибар!</strong> Һеҙ сайтта теркәлмәнегеҙ. Әгәр ҙә һеҙ ниндәй ҙә булһа төҙәтмәләр  йәки үҙгәртүҙәр индерһәгеҙ, һеҙҙең IP-адрес башҡаларға ла күрһәтеләсәк. Сайтҡа <strong>[$1 керһәгеҙ]</strong> йәки <strong>[$2 ҡуллануысы яҙмаһын төҙөһәгеҙ]</strong>, һеҙ индергән үҙгәртеүҙәр һеҙҙең ҡулланыусы яҙмағыҙға бәйләнгән була, шулай уҡ башҡа мөмкинлектәр ҙә тыуасаҡ.",
        "anonpreviewwarning": "''Һеҙ танылмағанһығыҙ. Яҙҙырыу ваҡытында IP-адресығыҙ был биттең үҙгәртеүҙәр тарихына яҙыласаҡ.''",
        "missingsummary": "'''Иҫкәртеү.''' Һеҙ үҙгәртеүҙергә ҡыҫҡа тасуирлама яҙманығыҙ. Ҡабаттан «Битте һаҡларға» төймәһенә баҫһағыҙ, үҙгәртеүҙәрегеҙ тасуирламаһыҙ һаҡланасаҡ.",
        "selfredirect": "<strong>Иғтибар:</strong> Һеҙ шул уҡ мәҡәләгә йүнәлтеү эшләйһегеҙ.\n «$1» төәмәһенә баҫһағыҙ тағы шул биткә йүнәлтеләсәк.",
        "missingcommenttext": "Зинһар, аҫҡа үҙ тасуирламағыҙҙы керетегеҙ.",
        "missingcommentheader": "'''Иҫкәртеү:''' Һеҙ был комментарий өсөн тема/исем яҙманығыҙ.\n«$1» төймәһенә ҡабат баҫыу менән үҙгәртеүҙерегеҙ исемһеҙ яҙыласаҡ.",
-       "summary-preview": "Буласаҡ тасуирлама:",
-       "subject-preview": "Тема/башлыҡты алдан ҡарау:",
+       "summary-preview": "Үҙгәртеүҙәр аңлатмаһын ҡарап сығыу:",
+       "subject-preview": "Теманы/баш исемде алдан ҡарау:",
        "previewerrortext": "Алдан ҡарау ваҡытында хата китте.",
        "blockedtitle": "Ҡулланыусы блокланған",
        "blockedtext": "'''Иҫәп яҙыуығыҙ йәки IP-адресығыҙ блокланған.'''\n\nБлоклаусы хаким: $1.\nБелдерелгән сәбәп: ''$2''.\n\n* Блоклау башланған ваҡыт: $8\n* Блоклау  аҙағы: $6\n* Блоклауҙар һаны: $7\n\nҺеҙ $1 йәки башҡа [[{{MediaWiki:Grouppage-sysop}}|хакимгә]] блоклау буйынса һорауҙарығыҙҙы ебәрә алаһығыҙ.\nИҫегеҙҙе тотоғоҙ: әгәр һеҙ теркәлмәгән һәм электрон почта адресығыҙҙы раҫламаған булһағыҙ ([[Special:Preferences|көйләүҙәрем битендә]]), хакимгә хат ебәрә алмайһығыҙ. Шулай ук блоклау ваҡытында һеҙҙең хат ебәреү мөмкинлегегеҙ сикләгән булырға ла мөмкин.\nҺеҙҙең IP-адрес — $3, блоклау идентификаторы — #$5.\nХаттарҙа был мәғлүмәттәрҙе күрһәтергә онотмағыҙ.",
        "readonlywarning": "<strong>КИҪӘТЕҮ: Техник хеҙмәтләндереү сәбәпле мәғлүмәттәр базаһы блокланған, шунлыҡтан үҙгәртеүҙәрегеҙҙе хәҙер һаҡлай алмайһығыҙ.<strong>\nТексты аҙаҡтан ҡулланыу өсөн башҡа файлда һаҡлап тора алаһығыҙ.\n\nХаким белдергән сәбәп: $1",
        "protectedpagewarning": "'''КИҪӘТЕҮ: Һеҙ был битте үҙгәртә алмайһығыҙ, был хоҡуҡҡа хакимдәр генә эйә.'''\nБелешмә өсөн түбәндә һуңғы үҙгәртеү тураһында мәғлүмәт бирелә:",
        "semiprotectedpagewarning": "'''Киҫәтеү:''' был бит һаҡланған. Уны теркәлгән ҡулланыусылар ғына үҙгәртә ала.\nБелешмә өсөн түбәндә һуңғы үҙгәртеү тураһында мәғлүмәт бирелә:",
-       "cascadeprotectedwarning": "<strong>Киҫәтеү:</strong> Был битте тик хакимдәр генә үҙгәртә ала.  Сөнки бит {{PLURAL:$1|каскадлы яҡлау исемлегенә индерелгән}}:",
+       "cascadeprotectedwarning": "<strong>Киҫәтеү:</strong> Был битте тик [[Special:ListGroupRights|махсус хоҡуҡлы]] ҡатнашыусылар ғына үҙгәртә ала.  Сөнки бит {{PLURAL:$1|1=түбәндәге каскадлы яҡлау битенә индерелгән}}:",
        "titleprotectedwarning": "'''Киҫәтеү: Бындый исемле бит һаҡланған, уны үҙгәртеү өсөн [[Special:ListGroupRights|тейешле хоҡуҡҡа]] эйә булыу кәрәк.'''\nБелешмә өсөн түбәндә һуңғы үҙгәртеү тураһында мәғлүмәт бирелә:",
        "templatesused": "Был биттә ҡулланылған {{PLURAL:$1|1=ҡалып|ҡалыптар}}:",
        "templatesusedpreview": "Алдан ҡаралған биттә ҡулланылған {{PLURAL:$1|1=ҡалып|ҡалыптар}}:",
        "invalid-content-data": "Тыйылған мәғлүмәт",
        "content-not-allowed-here": "\"$1\" эстәлеге [[$2]] бит өсөн ярамай",
        "editwarning-warning": "Икенсе биткә күсеү һеҙ индергән үҙгәрештәрҙең юғалыуына килтереүе мөмкин.\nӘгәр системала танылыу үтһәгеҙ, көйләүҙәрегеҙ битенең \"Мөхәррирләү\" бүлегендә был киҫәтеүҙе һүндерә алаһығыҙ.",
+       "editpage-invalidcontentmodel-title": "Йөкмәтке форматы ҡабул ителмәй",
        "editpage-notsupportedcontentformat-title": "Йөкмәтке форматы асылмай",
        "editpage-notsupportedcontentformat-text": "$1 эстәлеге форматы $2 моделе форматы менән тап килмәй.",
        "content-model-wikitext": "викитекст",
        "recentchangeslinked-feed": "Бәйле үҙгәртеүҙәр",
        "recentchangeslinked-toolbox": "Бәйле үҙгәртеүҙәр",
        "recentchangeslinked-title": "\"$1\" битенә бәйле үҙгәртеүҙәр",
-       "recentchangeslinked-summary": "Был күрһәтелгән бит һылтанма яһаған (йәки күрһәтелгән категорияға кергән) һуңғы үҙгәртеүҙәр исемлеге.\n[[Special:Watchlist|Күҙәтеү исемлегегеҙгә]] керә торған биттәр '''ҡалын''' итеп күрһәтелгән.",
+       "recentchangeslinked-summary": "Был күрһәтелгән бит һылтанма яһаған (йәки күрһәтелгән категорияға кергән) һуңғы үҙгәртеүҙәр исемлеге.\n[[Special:Watchlist|Күҙәтеү исемлегегеҙгә]] керә торған биттәр '''ҡалын''' итеп күрһәтелгән.",
        "recentchangeslinked-page": "Бит исеме:",
        "recentchangeslinked-to": "Киреһенсә, был биткә һылтанма яһаған биттәрҙәге үҙгәртеүҙәрҙе күрһәтергә",
        "recentchanges-page-added-to-category": "[[:$1]] категорияға өҫтәлгән",
        "tooltip-invert": "Һайланған исемдәр арауығындағы (һәм бәйле исемдәр арауығындағы, әгәр күрһәтелһә) биттәрҙәге үҙгәртеүҙәрҙе йәшерер өсөн был билдәне ҡуйығыҙ.",
        "tooltip-whatlinkshere-invert": "Был тамғаны һайланған исемдәр арауығындағы һылтанмаларҙы йәшереү өсөн ҡуйығыҙ.",
        "namespace_association": "Бәйле арауыҡ",
-       "tooltip-namespace_association": "Һайланған исемдәр арауығы менән бәйле әңгәмә(йәки тема) исем арауыҡтарын ҡушыр өсөн был билдәне ҡуйығыҙ.",
+       "tooltip-namespace_association": "Һайланған исемдәр арауығы менән бәйле әңгәмә (йәки тема) исем арауыҡтарын ҡушыр өсөн был билдәне ҡуйығыҙ.",
        "blanknamespace": "(Төп)",
        "contributions": "{{GENDER:$1|Ҡатнашыусы}} башҡарған эш",
        "contributions-title": "$1 исемле ҡатнашыусы башҡарған эш",
        "tooltip-pt-watchlist": "Һеҙ күҙәткән биттәр исемлеге",
        "tooltip-pt-mycontris": "{{GENDER:|Һеҙҙең}} төҙәтеүҙәр исемлеге",
        "tooltip-pt-anoncontribs": "Был IP-адрестан яһалған төҙәтеүҙәр",
-       "tooltip-pt-login": "Бында теркәлеү үтергә була, әммә был эш мәжбүри түгел.",
+       "tooltip-pt-login": "Бында теркәлеү үтергә була, әммә был эш мәжбүри түгел",
        "tooltip-pt-logout": "Сығырға",
-       "tooltip-pt-createaccount": "Ð\9cоÑ\82лаҡ Ð±Ñ\83лмаһа Ð»Ð°, ÒºÐµÒ\99гÓ\99 Ð¸Ò«Ó\99п Ñ\8fÒ\99маһÑ\8b Ñ\82Ó©Ò\99Ó©Ñ\80гө Ò»Ó\99м Ñ\81иÑ\81Ñ\82емала Ñ\82анÑ\8bлÑ\8bÑ\80Ò\93а Ñ\82Ó\99ҡдим Ð¸Ñ\82Ó\99беÒ\99.",
+       "tooltip-pt-createaccount": "Ð\9cоÑ\82лаҡ Ð±Ñ\83лмаһа Ð»Ð°, ÒºÐµÒ\99гÓ\99 Ð¸Ò«Ó\99п Ñ\8fÒ\99маһÑ\8b Ñ\82Ó©Ò\99Ó©Ñ\80гÓ\99 Ò»Ó\99м Ñ\81иÑ\81Ñ\82емала Ñ\82анÑ\8bлÑ\8bÑ\80Ò\93а Ñ\82Ó\99ҡдим Ð¸Ñ\82Ó\99беÒ\99",
        "tooltip-ca-talk": "Биттең эстәлеге тураһында фекерләшеү",
        "tooltip-ca-edit": "Был битте үҙгәртергә",
        "tooltip-ca-addsection": "Яңы бүлек эшләргә",
        "tooltip-watchlistedit-raw-submit": "Күҙәтеү исемлеген яңыртырға",
        "tooltip-recreate": "Битте юйылған булыуына ҡарамаҫтан тергеҙергә",
        "tooltip-upload": "Күсерә башларға",
-       "tooltip-rollback": "Бер баҫыу менән аҙаҡҡы мөхәррирләүсенең үҙгәртеүҙәрен кире ала.",
+       "tooltip-rollback": "Бер баҫыу менән аҙаҡҡы мөхәррирләүсенең үҙгәртеүҙәрен кире ала",
        "tooltip-undo": "\"Кире ал\" төҙәтеүҙе кире ала һәм төҙәтеү формаһын \"алдан байҡау\"ҙа күрһәтә. Һәм кире алыуҙың сәбәбен белдерергә була.",
        "tooltip-preferences-save": "Көйләүҙәрҙе һаҡларға",
        "tooltip-summary": "Ҡыҫҡаса тасуирлама керетегеҙ",
        "fileduplicatesearch-noresults": "\"$1\" исемле файл табылманы",
        "specialpages": "Махсус биттәр",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Ябай махсус биттәр.\n* <span class=\"mw-specialpagerestricted\">Сикле махсус биттәр.</span>\n* <span class=\"mw-specialpagecached\">Кешланған махсус биттәр (иҫкергән булыуы мөмкин).</span>",
        "specialpages-group-maintenance": "Техник хеҙмәтләндереү хисапламалары",
        "specialpages-group-other": "Башҡа махсус биттәр",
        "specialpages-group-login": "Танылыу йәки теркәлеү",
        "logentry-patrol-patrol": "$1 $3 битенең $4 версияһын {{GENDER:$2|тикшерҙе}}.",
        "logentry-patrol-patrol-auto": "$1 $3 битенең $4 версияһын автоматик рәүештә {{GENDER:$2|тикшерҙе}}.",
        "logentry-newusers-newusers": " {{GENDER:$2|ҡатнашыусы}} $1 иҫәп яҙмаһы булдырҙы",
-       "logentry-newusers-create": "{{GENDER:$2|ҡатнашыусы}} $1 иҫәп яҙмаһы булдырҙы.",
+       "logentry-newusers-create": "{{GENDER:$2|ҡатнашыусы}} $1 иҫәп яҙмаһы булдырҙы",
        "logentry-newusers-create2": "$1 {{GENDER:$2|ҡатнашыусы}} $3 иҫәп яҙмаһын булдырҙы",
        "logentry-newusers-byemail": "$1 {{GENDER:$2|}} $3 иҫәп яҙмаһын булдырҙы һәм серһүҙ электрон почта аша ебәрелде",
        "logentry-newusers-autocreate": "Автоматик рәүештә {{GENDER:$2| ҡатнашыусының}} $1 иҫәп яҙмаһы яһалды",
index 5f1fc6d..7ecb3bf 100644 (file)
        "rcfilters-advancedfilters": "Пашыраныя фільтры",
        "rcfilters-limit-title": "Паказаць зьменаў",
        "rcfilters-limit-shownum": "Паказаць апошнія $1 зьменаў",
+       "rcfilters-days-title": "Апошнія дні",
+       "rcfilters-hours-title": "Апошнія гадзіны",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|дзень|дні|дзён}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|гадзіна|гадзіны|гадзінаў}}",
        "rcfilters-quickfilters": "Захаваныя фільтры",
        "rcfilters-quickfilters-placeholder-title": "Спасылкі яшчэ не захаваныя",
        "rcfilters-quickfilters-placeholder-description": "Каб захаваць налады вашага фільтру і выкарыстаць іх пазьней, націсьніце на выяву закладкі ў зоне актыўнага фільтру ніжэй.",
        "rcfilters-filter-user-experience-level-unregistered-label": "Незарэгістраваныя",
        "rcfilters-filter-user-experience-level-unregistered-description": "Рэдактары, якія не ўвайшлі ў сыстэму",
        "rcfilters-filter-user-experience-level-newcomer-label": "Навічкі",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Ð\9cенÑ\88 Ð·Ð° 10 Ð¿Ñ\80авак Ñ\96 4 Ð´Ð½і актыўнасьці.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Ð\97аÑ\80Ñ\8dгÑ\96Ñ\81Ñ\82Ñ\80аванÑ\8bÑ\8f Ñ\80Ñ\8dдакÑ\82аÑ\80Ñ\8b Ð·Ñ\8c Ð¼ÐµÐ½Ñ\88 Ñ\87Ñ\8bм 10 Ð¿Ñ\80аÑ\9eкамÑ\96 Ñ\96 4 Ð´Ð½Ñ\8fмі актыўнасьці.",
        "rcfilters-filter-user-experience-level-learner-label": "Вучні",
-       "rcfilters-filter-user-experience-level-learner-description": "Ð\91олÑ\8cÑ\88 Ð´Ð¾Ñ\81Ñ\8cведÑ\83, чым у «навічкоў», але меней чым у «дасьведчаных удзельнікаў».",
+       "rcfilters-filter-user-experience-level-learner-description": "Ð\97аÑ\80Ñ\8dгÑ\96Ñ\81Ñ\82Ñ\80аванÑ\8bÑ\8f Ñ\80Ñ\8dдакÑ\82аÑ\80Ñ\8b, Ñ\87Ñ\8bй Ð´Ð¾Ñ\81Ñ\8cвед Ð±Ð¾Ð»Ñ\8cÑ\88 чым у «навічкоў», але меней чым у «дасьведчаных удзельнікаў».",
        "rcfilters-filter-user-experience-level-experienced-label": "Дасьведчаныя ўдзельнікі",
-       "rcfilters-filter-user-experience-level-experienced-description": "Ð\91олÑ\8cÑ\88 Ð·Ð° 30 Ð´Ð·Ñ\91н Ð°ÐºÑ\82Ñ\8bÑ\9eнаÑ\81Ñ\8cÑ\86Ñ\96 Ñ\96 500 Ð¿Ñ\80авак.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Ð\97аÑ\80Ñ\8dгÑ\96Ñ\81Ñ\82Ñ\80аванÑ\8bÑ\8f Ñ\9eдзелÑ\8cнÑ\96кÑ\96 Ð· Ð±Ð¾Ð»Ñ\8cÑ\88 Ñ\87Ñ\8bм 500 Ð¿Ñ\80аÑ\9eкамÑ\96 Ñ\96 30 Ð´Ð½Ñ\8fмÑ\96 Ð°ÐºÑ\82Ñ\8bÑ\9eнаÑ\81Ñ\8cÑ\86Ñ\96.",
        "rcfilters-filtergroup-automated": "Аўтаматычны ўнёсак",
        "rcfilters-filter-bots-label": "Робат",
        "rcfilters-filter-bots-description": "Праўкі, зробленыя з дапамогай аўтаматызаваных інструмэнтаў.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "Фільтар «Дробныя праўкі» канфліктуе з адным ці некалькімі фільтрамі «Тыпаў зьменаў», бо некаторыя тыпы зьменаў ня могуць быць вызначаныя як «дробныя». Канфліктныя фільтры пазначаныя ў разьдзеле актыўных фільтраў вышэй.",
        "rcfilters-hideminor-conflicts-typeofchange": "Некаторыя тыпы зьменаў ня могуць быць вызначаныя як «дробныя», таму гэты фільтар канфліктуе з наступнымі фільтрамі «Тыпаў зьменаў»: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Гэты фільтар тыпаў зьменаў канфліктуе зь фільтрам «Дробныя праўкі». Некаторыя тыпы зьменаў ня могуць быць вызначаныя як «дробныя».",
-       "rcfilters-filtergroup-lastRevision": "ЦÑ\8fпеÑ\80аÑ\88нÑ\8fÑ\8f Ð²Ñ\8dÑ\80Ñ\81Ñ\96Ñ\8f",
+       "rcfilters-filtergroup-lastRevision": "ЦÑ\8fпеÑ\80аÑ\88нÑ\96Ñ\8f Ð²Ñ\8dÑ\80Ñ\81Ñ\96Ñ\96",
        "rcfilters-filter-lastrevision-label": "Апошняя вэрсія",
        "rcfilters-filter-lastrevision-description": "Апошняя зьмена на старонцы.",
        "rcfilters-filter-previousrevision-label": "Ранейшыя вэрсіі",
        "fileduplicatesearch-noresults": "Файл з назвай «$1» ня знойдзены.",
        "specialpages": "Спэцыяльныя старонкі",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Звычайныя спэцыяльныя старонкі.\n* <strong class=\"mw-specialpagerestricted\">Спэцыяльныя старонкі з абмежаваным доступам.</strong>",
        "specialpages-group-maintenance": "Тэхнічныя справаздачы",
        "specialpages-group-other": "Іншыя спэцыяльныя старонкі",
        "specialpages-group-login": "Уваход / стварэньне рахунку",
index 031eabb..9a6dd50 100644 (file)
@@ -31,7 +31,8 @@
                        "Liashko",
                        "Mechanizatar",
                        "Artsiom91",
-                       "Andrus"
+                       "Andrus",
+                       "Da voli"
                ]
        },
        "tog-underline": "Падкрэсліваць спасылкі:",
        "rcfilters-filter-unpatrolled-label": "Недагледжаны",
        "rcfilters-filtergroup-lastRevision": "Цяперашняя версія",
        "rcfilters-filter-lastrevision-label": "Актуальная версія",
+       "rcfilters-exclude-button-on": "За выключэннем выбранага",
        "rcnotefrom": "Ніжэй {{PLURAL:$5|паказана змяненне|паказаны змены}} з <strong>$3, $4</strong> (не больш за <strong>$1</strong>).",
        "rclistfrom": "Паказаць змены з $3 $2",
        "rcshowhideminor": "$1 дробныя праўкі",
        "fileduplicatesearch-noresults": "Не знойдзены файл з іменем «$1».",
        "specialpages": "Адмысловыя старонкі",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Звычайныя адмысловыя старонкі.\n* <span class=\"mw-specialpagerestricted\">Адмысловыя старонкі з абмежаваным доступам.</span>\n* <span class=\"mw-specialpagecached\">Закэшаваныя адмысловыя старонкі (могуць быць састарэлымі).</span>",
        "specialpages-group-maintenance": "Звесткі аб працы",
        "specialpages-group-other": "Іншыя адмысловыя старонкі",
        "specialpages-group-login": "Прадстаўленне / рэгістрацыя",
index e1efe59..a919e62 100644 (file)
        "recentchanges-label-plusminus": "Размерът на страницата е променен с този брой байтове",
        "recentchanges-legend-heading": "<strong>Легенда:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (вижте също [[Special:NewPages|списъка с нови страници]])",
-       "recentchanges-submit": "Покажи",
+       "recentchanges-submit": "Показване",
+       "rcfilters-legend-heading": "<strong>Списък на съкращенията:</strong>",
        "rcfilters-activefilters": "Активни филтри",
        "rcfilters-quickfilters": "Запазени филтри",
        "rcfilters-quickfilters-placeholder-title": "Няма запазени препратки",
        "recentchanges-page-added-to-category-bundled": "[[:$1]] е добавена към категория, [[Special:WhatLinksHere/$1|към страницата сочат други страници]]",
        "recentchanges-page-removed-from-category": "[[:$1]] е премахната от категория",
        "upload": "Качи файл",
-       "uploadbtn": "Качване",
-       "reuploaddesc": "Връщане към формуляра за качване.",
+       "uploadbtn": "Качване на файл",
+       "reuploaddesc": "Връщане към формуляра за качване",
        "upload-tryagain": "Съхраняване на промененото описание на файла",
        "uploadnologin": "Не сте влезли",
        "uploadnologintext": "За да могат да бъдат качвани файлове е необходимо $1 в системата.",
        "upload_directory_missing": "Директорията за качване ($1) липсва и не може да бъде създадена на сървъра.",
-       "upload_directory_read_only": "СÑ\8aÑ\80вÑ\8aÑ\80Ñ\8aÑ\82 Ð½Ñ\8fма Ð´Ð¾Ñ\81Ñ\82Ñ\8aп за писане в директорията за качване „$1“.",
+       "upload_directory_read_only": "СÑ\8aÑ\80вÑ\8aÑ\80Ñ\8aÑ\82 Ð½Ñ\8fма Ð¿Ñ\80ава за писане в директорията за качване „$1“.",
        "uploaderror": "Грешка при качване",
        "upload-recreate-warning": "<strong>Внимание: Файл с това име вече е бил изтрит или преместен.</strong>\n\nЗа повече информация можете да прегледате записите от дневниците на изтриванията и преместванията:",
        "uploadtext": "Формулярът по-долу служи за качване на файлове, които ще могат да се използват в страниците.\nЗа преглеждане и търсене на вече качените файлове, може да се използва [[Special:FileList|списъка с качени файлове]]. Качванията се записват в [[Special:Log/upload|дневника на качванията]], а изтриванията &mdash; в [[Special:Log/delete|дневник на изтриванията]].\n\nЗа включване на файл в страница, може да се използва една от следния синтаксис: \n* <strong><code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.jpg]]</nowiki></code></strong> за използване пълната версия на файла\n* <strong><code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.png|200px|thumb|left|alt text]]</nowiki></code></strong> за определяне на широчина от 200 пиксела, ляво позициониране и „alt text“ за описание\n* <strong><code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:File.ogg]]</nowiki></code></strong> за директна препратка, без файлът да бъде показван",
        "upload-preferred": "{{PLURAL:$2|Предпочитан файлов формат|Предпочитани файлови формати}}: $1.",
        "upload-prohibited": "{{PLURAL:$2|Непозволен файлов формат|Непозволени файлови формати}}: $1.",
        "uploadlogpage": "Дневник на качванията",
-       "uploadlogpagetext": "Списък на последните качвания.",
+       "uploadlogpagetext": "Списък на последните качвания.\nВижте [[Special:NewFiles|галерията на новите файлове]] за визуален преглед.",
        "filename": "Име на файл",
        "filedesc": "Описание",
        "fileuploadsummary": "Описание:",
        "filereuploadsummary": "Промени по файла:",
        "filestatus": "Авторско право:",
        "filesource": "Изходен код:",
-       "ignorewarning": "Съхраняване на файла въпреки предупреждението.",
+       "ignorewarning": "Съхраняване на файла въпреки предупреждението",
        "ignorewarnings": "Пренебрегване на всякакви предупреждения",
        "minlength1": "Имената на файловете трябва да съдържат поне един знак.",
        "illegalfilename": "Името на файла „$1“ съдържа знаци, които не са позволени в заглавия на страници. Преименувайте файла и се опитайте да го качите отново.",
        "fileduplicatesearch-noresults": "Не беше открит файл с име „$1“.",
        "specialpages": "Специални страници",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Обикновени специални страници.\n* <strong class=\"mw-specialpagerestricted\">Специални страници с ограничения.</strong>",
        "specialpages-group-maintenance": "Доклади по поддръжката",
        "specialpages-group-other": "Други специални страници",
        "specialpages-group-login": "Влизане / създаване на сметка",
index 15d7df2..074c99f 100644 (file)
        "rollbacklinkcount": "रोलबैक $1 {{PLURAL:$1|संपादन|संपादन सब}}",
        "protectlogpage": "सुरक्षा लॉग",
        "protectlogtext": "नीचे पन्ना सुरक्षा में भइल बदलावकुल के सूची बा।\nहाल में सुरक्षित पन्नन के सूची खातिर [[Special:ProtectedPages|सुरक्षित पन्नन के सूची]] देखीं।",
+       "protectedarticle": "\"[[$1]]\" सुरक्षित कइल गइल",
        "restriction-move": "स्थानांतरण",
        "restriction-create": "बनावे पर",
        "restriction-upload": "अपलोड",
        "contributions-title": " $1 खातिर प्रयोगकर्ता योगदान",
        "mycontris": "योगदान",
        "anoncontribs": "योगदान",
+       "contribsub2": "{{GENDER:$3|$1}} ($2) खातिर",
        "nocontribs": "ई मानदंड से मिलत जुलत कौनो बदलाव ना मिलल।",
        "uctop": "(वर्तमान)",
        "month": "महीना से (आ ओ से पहिले):",
        "sp-contributions-newbies-title": "नया खाता खातिर प्रयोगकर्ता के योगदान।",
        "sp-contributions-blocklog": "ब्लॉक लॉग",
        "sp-contributions-deleted": "नष्ट प्रयोगकर्ता के योगदान।",
+       "sp-contributions-uploads": "अपलोड",
        "sp-contributions-logs": "लॉग",
        "sp-contributions-talk": "बातचीत",
        "sp-contributions-userrights": "प्रयोगकर्ता अधिकार प्रबन्धन",
        "sp-contributions-blocked-notice": "ई प्रयोगकर्ता के ई समय निष्क्रीय करल गईल बा।\nनविनतम नष्ट लौग प्रविष्टी उद्धरण खातिर निचे दिहल बा:",
+       "sp-contributions-search": "योगदान खातिर खोज करीं",
+       "sp-contributions-username": "आइपी पता भा प्रयोगकर्तानाँव:",
+       "sp-contributions-newonly": "खाली उहे संपादन देखीं जेकरा से नया पन्ना बनल होखे",
+       "sp-contributions-submit": "खोजीं",
        "whatlinkshere": "इहाँ का जुड़ल बा",
        "whatlinkshere-title": "पन्ना जेवन \"$1\" से जुड़ल बा",
        "whatlinkshere-page": "पन्ना:",
        "tooltip-ca-delete": "ई पन्ना मिटाईं",
        "tooltip-ca-move": "एह पन्ना के स्थानांतरण करीं",
        "tooltip-ca-watch": "ए पन्ना के अपनी धियानसूची में जोड़ीं",
+       "tooltip-ca-unwatch": "ई पन्ना अपना धियानसूची से हटाईं",
        "tooltip-search": "{{SITENAME}} में खोजीं",
        "tooltip-search-go": "अगर ठीक एही नाँव के पन्ना मौजूद होखे तब ओहपर जाईं",
        "tooltip-search-fulltext": "अइसन पन्ना खोजीं जिनहन में ई पाठ (शब्द भा वाक्य) बाटे",
        "tooltip-undo": "\"वापस लीं\" ए संपादन के पलट देला आ संपादन फार्म के झलक देखावे वाला मोड में खोलेला। ई छोट सारांश में कारण जोड़े के मोका देला।",
        "tooltip-summary": "संछेप में एगो सारांश लिखीं",
        "simpleantispam-label": "स्पैम-बिरोधी रोक (Anti-spam check)\nएके <strong>मत</strong> भरीं!",
+       "pageinfo-length": "पन्ना लंबाई (बाइट में)",
        "pageinfo-toolboxlink": "पन्ना से जुड़ल जानकारी",
        "previousdiff": "← पुरान संपादन",
        "nextdiff": "नया संपादन →",
index 2af08f2..6015fa9 100644 (file)
        "rcfilters-filter-editsbyself-description": "আপনার নিজস্ব অবদান।",
        "rcfilters-filter-editsbyother-label": "অন্যদের দ্বারা পরিবর্তিত",
        "rcfilters-filter-editsbyother-description": "আপনার নিজস্বগুলি ছাড়া সকল পরিবর্তন।",
-       "rcfilters-filtergroup-userExpLevel": "নিবন্ধন ও অভিজ্ঞতা",
+       "rcfilters-filtergroup-userExpLevel": "বà§\8dযবহারà¦\95ারà§\80 à¦¨à¦¿à¦¬à¦¨à§\8dধন à¦\93 à¦\85ভিà¦\9cà§\8dà¦\9eতা",
        "rcfilters-filter-user-experience-level-registered-label": "নিবন্ধিত",
        "rcfilters-filter-user-experience-level-registered-description": "প্রবেশকৃত সম্পাদকবৃন্দ।",
        "rcfilters-filter-user-experience-level-unregistered-label": "অনিবন্ধিত",
        "rcfilters-typeofchange-conflicts-hideminor": "এই \"পরিবর্তনের ধরন\"-সংক্রান্ত ছাঁকনিটির সাথে \"অনুল্লেখ্য সম্পাদনা\" ছাঁকনিটির সংঘর্ষ আছে। কিছু নির্দিষ্ট ধরনের সম্পাদনা \"অনুল্লেখ্য\" হিসেবে চিহ্নিত করা সম্ভব নয়।",
        "rcfilters-filtergroup-lastRevision": "সর্বশেষ সংস্করণ",
        "rcfilters-filter-lastrevision-label": "সর্বশেষ সংশোধন",
-       "rcfilters-filter-lastrevision-description": "একটি পাতার সর্বশেষ সাম্প্রতিক পরিবর্তন।",
-       "rcfilters-filter-previousrevision-label": "পà§\82রà§\8dববরà§\8dতà§\80 à¦¸à¦\82শà§\8bধন",
-       "rcfilters-filter-previousrevision-description": "সব পরিবর্তন যা একটি পাতার সর্বশেষ সাম্প্রতিক পরিবর্তন নয়।",
+       "rcfilters-filter-lastrevision-description": "শà§\81ধà§\81মাতà§\8dর à¦\8fà¦\95à¦\9fি à¦ªà¦¾à¦¤à¦¾à¦° à¦¸à¦°à§\8dবশà§\87ষ à¦¸à¦¾à¦®à§\8dপà§\8dরতিà¦\95 à¦ªà¦°à¦¿à¦¬à¦°à§\8dতন।",
+       "rcfilters-filter-previousrevision-label": "সরà§\8dবশà§\87ষ à¦¸à¦\82শà§\8bধন à¦¨à¦¯à¦¼",
+       "rcfilters-filter-previousrevision-description": "সব পরিবর্তন যা \"সর্বশেষ সংশোধন\" নয়।",
        "rcfilters-filter-excluded": "বর্জিত",
+       "rcfilters-tag-prefix-namespace-inverted": "$1 <strong>:নয়</strong>",
        "rcfilters-view-tags": "ট্যাগকৃত সম্পাদনা",
+       "rcfilters-view-tags-tooltip": "সম্পাদনা ট্যাগ ব্যবহার করে ফলাফল ছাঁকুন",
        "rcfilters-view-return-to-default-tooltip": "মূল ছাঁকনির মেনুতে ফিরুন",
        "rcfilters-liveupdates-button": "সরাসরি হালনাগাদ",
        "rcnotefrom": "<strong>$2</strong>টা থেকে সংঘটিত পরিবর্তনগুলি (সর্বোচ্চ <strong>$1টি</strong> দেখানো হয়েছে)।",
        "delete-warning-toobig": "এই পাতাটির একটি বৃহৎ সম্পাদনা ইতিহাস রয়েছে, যা $1 {{PLURAL:$1|সংস্করণেরও|সংস্করণেরও}} বেশি।\nএই পাতাটি মুছে ফেললে তা {{SITENAME}} সাইটের ডেটাবেজ সমস্যার কারণ হতে পারে;\nসাবধানতার সাথে এগিয়ে যান।",
        "deleteprotected": "আপনি এই পাতাটি মুছে ফেলতে পারবেন না কারণ এটি সুরক্ষিত করা হয়েছে।",
        "deleting-backlinks-warning": "<strong>সতর্কীকরণ:</strong> আপনি যেটি মুছে ফেলতে যাচ্ছেন তা [[Special:WhatLinksHere/{{FULLPAGENAME}}|অন্যান্য পাতাসমূহে]] সংযুক্ত অথবা অন্তর্ভুক্ত রয়েছে।",
+       "deleting-subpages-warning": "<strong>সতর্কীকরণ:</strong> আপনি যে পাতাটি মুছে ফেলতে যাচ্ছেন তাঁর [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|একটি উপপাতা|$1টি উপপাতা|51=৫০টির বেশী}}]] রয়েছে।",
        "rollback": "সম্পাদনা ফিরিয়ে নিন",
        "rollbacklink": "পুনর্বহাল",
        "rollbacklinkcount": "$1টি {{PLURAL:$1|সম্পাদনা}} রোলব্যাক করুন",
        "undeletepage": "মুছে ফেলা পাতাগুলি দেখুন ও ফিরিয়ে আনুন",
        "undeletepagetitle": "'''[[:$1|$1]] এর অপসারিত সংস্করণগুলোর সমন্বয়ে দেখানো হচ্ছে'''।",
        "viewdeletedpage": "মুছে ফেলা হয়েছে, এমন পাতাগুলো দেখুন",
-       "undeletepagetext": "নিচের {{PLURAL:$1|পাতাটি মুছে ফেলা হয়েছে কিন্তু এটি|$1 পাতাগুলি মুছে ফেলা হয়েছে কিন্তু এগুলি}} এখনও আর্কাইভে আছে ও পুনরুদ্ধার করা সম্ভব। আর্কাইভ পর্যায়ক্রমিকভাবে পরিষ্কার করা হতে পারে।",
+       "undeletepagetext": "নিচের {{PLURAL:$1|পাতাটি মুছে ফেলা হয়েছে কিন্তু এটি|$1টি পাতা মুছে ফেলা হয়েছে কিন্তু এগুলি}} এখনও আর্কাইভে আছে ও পুনরুদ্ধার করা সম্ভব। আর্কাইভ পর্যায়ক্রমিকভাবে পরিষ্কার করা হতে পারে।",
        "undelete-fieldset-title": "সংশোধন পুনরুদ্ধার",
        "undeleteextrahelp": "সম্পূর্ণ পাতাটি পুনরুদ্ধার করার জন্য সবগুলি টিকবাক্স অনির্বাচিত করুন এবং '''''{{int:undeletebtn}}''''' বোতামে ক্লিক করুন।\nনির্বাচিত পুনরুদ্ধারের জন্য যেসব সংশোধন পুনরুদ্ধার করতে চান, তার পাশের বাক্সে টিক দিন এবং '''''{{int:undeletebtn}}''''' বোতামে ক্লিক করুন।",
        "undeleterevisions": "$1{{PLURAL:$1|টি সংশোধন}} অপসারিত",
        "fileduplicatesearch-noresults": "\"$1\" নামের কোনো ফাইল খুঁজে পাওয়া যায়নি।",
        "specialpages": "বিশেষ পাতাসমূহ",
        "specialpages-note-top": "ব্যাখ্যা",
-       "specialpages-note": "* সাধারণ বিশেষ পাতাসমূহ।\n* <span class=\"mw-specialpagerestricted\">সীমাবদ্ধ বিশেষ পাতা।</span>",
+       "specialpages-note-restricted": "* সাধারণ বিশেষ পাতাসমূহ।\n* <span class=\"mw-specialpagerestricted\">সীমাবদ্ধ বিশেষ পাতাসমূহ।</span>",
        "specialpages-group-maintenance": "রক্ষণাবেক্ষণের কার্যবিবরণীসমূহ",
        "specialpages-group-other": "অন্যান্য বিশেষ পাতাসমূহ",
        "specialpages-group-login": "প্রবেশ/নতুন অ্যাকাউন্ট",
index 03a2b69..7c89fbd 100644 (file)
        "gender-unknown": "Kad Vas spominje, softver će pokušati izbjegavati rod kad god je to moguće",
        "gender-male": "On uređuje wiki stranice",
        "gender-female": "Ona uređuje wiki stranice",
-       "prefs-help-gender": "Postavljanje ovih podešavanja nije obavezno.\nSoftver koristi ove vrijednosti za vaše naslovljanje i ispravke gramatičkog roda u porukama softvera. Ova će informacija biti javna.",
+       "prefs-help-gender": "Ova postavka nije obavezna.\nSoftver koristi datu vrijednost da bi Vam se obratio i spomenuo Vas drugima koristeći odgovarajući gramatički rod.\nPodatak će biti javan.",
        "email": "E-pošta",
        "prefs-help-realname": "Pravo ime nije obavezno.\nAko izaberete da date ime, biće korišteno za pripisivanje vašem radu.",
        "prefs-help-email": "Adresa e-pošte nije obavezna, ali je potrebna u slučaju ponovnog postavljanja šifre, ako je zaboravite.",
        "recentchanges-submit": "Prikaži",
        "rcfilters-activefilters": "Aktivni filteri",
        "rcfilters-advancedfilters": "Napredni filteri",
-       "rcfilters-quickfilters": "Sačuvane postavke filtera",
+       "rcfilters-quickfilters": "Sačuvani filteri",
        "rcfilters-quickfilters-placeholder-title": "Zasad nema sačuvanih linkova",
        "rcfilters-quickfilters-placeholder-description": "Da sačuvate postavke filtera da biste ih kasnije ponovo upotrijebili, kliknite na ikonu markera pod \"Aktivni filterima\" ispod.",
        "rcfilters-savedqueries-defaultlabel": "Sačuvani filteri",
        "move-page": "Premjesti $1",
        "move-page-legend": "Premjesti stranicu",
        "movepagetext": "Korištenjem ovog formulara možete preimenovati stranicu, premještajući cijelu historiju na novo ime.\nČlanak pod starim imenom postat će stranica koja preusmjerava na članak pod novim imenom. \nMožete automatski izmijeniti preusmjerenje do izvornog naslova.\nAko se ne odlučite na to, provjerite [[Special:DoubleRedirects|dvostruka]] ili [[Special:BrokenRedirects|neispravna preusmjeravanja]].\nDužni ste provjeriti da svi linkovi i dalje nastave voditi na prave stranice.\n\nImajte na umu da članak <strong>neće</strong> biti premješten ako već postoji članak pod imenom na koje ga namjeravate preusmjeriti osim u slučaju stranice za preusmjeravanje koja nema nikakvih starih izmjena.\nTo znači da možete vratiti stranicu na prethodno mjesto ako pogriješite, ali ne možete zamijeniti postojeću stranicu.\n\n<strong>Napomena:</strong>\nOvo može biti drastična i neočekivana promjena kad su u pitanju popularne stranice.\nMolimo da dobro razmislite prije no što premjestite stranicu.",
-       "movepagetext-noredirectfixer": "Koristeći donji obrazac, preimenovat ćete stranicu i premjestiti cijelu njenu historiju na novi naziv.\nStari naziv postat će preusmjerenje na novi naziv.\nMolimo da provjerite postoje li [[Special:DoubleRedirects|dvostruka]] ili [[Special:BrokenRedirects|nedovršena preusmjerenja]].\nVi ste za to odgovorni te morate provjeriti jesu li linkovi ispravni i vode li tamo kamo bi trebali voditi.\n\nImajte na umu da stranica '''neće''' biti premještena ako već postoji stranica s tim imenom, osim ako je prazna ili je preusmjerenje ili nema ranije historije.\nOvo znači da možete preimenovati stranicu nazad gdje je ranije bila preimenovana ako ste pogriješili, ali ne možete ponovo preimenovati postojeću stranicu.\n\n<strong>Napomena:</strong>\nImajte na umu da premještanje popularnog članka može biti\ndrastična i neočekivana promjena za korisnike; molimo da budete sigurni da ste shvatili posljedice prije no što nastavite.",
+       "movepagetext-noredirectfixer": "Koristeći donji obrazac, preimenovat ćete stranicu i premjestiti cijelu njenu historiju na novi naziv.\nStari naziv postat će preusmjerenje na novi naziv.\nMolimo da provjerite postoje li [[Special:DoubleRedirects|dvostruka]] ili [[Special:BrokenRedirects|nedovršena preusmjerenja]].\nVi ste za to odgovorni te morate provjeriti jesu li linkovi ispravni i vode li tamo kamo bi trebali voditi.\n\nImajte na umu da stranica '''neće''' biti premještena ako već postoji stranica s tim imenom, osim ako je prazna ili je preusmjerenje ili nema ranije historije.\nOvo znači da možete preimenovati stranicu nazad gdje je ranije bila preimenovana ako ste pogriješili, ali ne možete ponovo preimenovati postojeću stranicu.\n\n<strong>Napomena:</strong>\nOvo može biti drastična i neočekivana promjena za popularnu stranicu;\ndobro razmislite o posljedicama prije nego što nastavite.",
        "movepagetalktext": "Ako označite ovu kutijicu, odgovarajuća stranica za razgovor, ako postoji, automatski će biti premještena na novi naziv, osim ako već postoji sadržaj na odredišnoj stranici za razgovor.\n\nU tom slučaju, morat ćete ručno premjestiti ili prekopirati stranicu ako to želite.",
        "moveuserpage-warning": "<strong>Upozorenje:</strong> Premještate korisničku stranicu. Imajte u vidu da će samo stranica biti premještena, a sam korisnik <em>neće</em> biti preimenovan.",
        "movecategorypage-warning": "<strong>Upozorenje:</strong> Premještate stranicu kategorije. Imajte na umu da će samo stranica biti premještena i da sve stranice u staroj kategoriji <em>neće</em> biti ponovo kategorirane u novu kategoriju.",
        "fileduplicatesearch-noresults": "Nije pronađena datoteka s imenom \"$1\".",
        "specialpages": "Posebne stranice",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Normalne posebne stranice.\n* <strong class=\"mw-specialpagerestricted\">Zaštićene posebne stranice.</strong>",
        "specialpages-group-maintenance": "Izvještaji za održavanje",
        "specialpages-group-other": "Ostale posebne stranice",
        "specialpages-group-login": "Prijava / otvaranje računa",
        "htmlform-user-not-exists": "<strong>$1</strong> ne postoji.",
        "htmlform-user-not-valid": "<strong>$1</strong> nije ispravno korisničko ime.",
        "logentry-delete-delete": "$1 {{GENDER:$2|obrisao|obrisala}} je stranicu $3",
-       "logentry-delete-delete_redir": "$1 {{GENDER:$2|obrisao|obrisala}} je preusmjerenje $3 prepisivanjem",
+       "logentry-delete-delete_redir": "$1 {{GENDER:$2|obrisao|obrisala}} je preusmjerenje $3 presnimavanjem",
        "logentry-delete-restore": "$1 {{GENDER:$2|vratio|vratila}} je stranicu $3 ($4)",
        "logentry-delete-restore-nocount": "$1 {{GENDER:$2|vratio|vratila}} je stranicu $3",
        "restore-count-revisions": "{{PLURAL:$1|1 izmjena|$1 izmjene|$1 izmjena}}",
index 117b558..2c6e63f 100644 (file)
        "parser-template-loop-warning": "ئەڵقەی داڕێژە دۆزرایەوە: [[$1]]",
        "parser-template-recursion-depth-warning": "سنووری قووڵی گەڕانەوەی داڕێژە تێپەڕیوە ($1)",
        "undo-success": "دەکرێ دەستکاریەکە پووچەڵبکرێتەوە.\nتکایە چاو لەو هەڵسەنگاندنەی خوارەوە بکە تا دڵنیا بیت ئەمە ئەوەیە کە‌ دەتویست بیکەی و دواتر گۆڕانکارییەکانی خوارەوە پاشەکەوت بکە بۆ تەواوکردنی پووچەڵکردنەوەکە.",
-       "undo-failure": "Ù\84Û\95بÛ\95ر Ú©Û\8eØ´Û\95Û\8c Ø¯Û\95ستâ\80\8cتÛ\8eâ\80\8cÙ\88Û\95رداÙ\86Ø\8c Ù\86اتÙ\88اÙ\86Û\8c Ø¯Û\95ستکارÛ\8cÛ\95Ú©Û\95 Ø¦Û\95Ù\86جاÙ\85â\80\8cÙ\86Û\95دراÙ\88 Ø¨Ú©Û\95Û\8cت.",
+       "undo-failure": "Ù\86Û\95تÙ\88اÙ\86درا Ø¯Û\95ستکارÛ\8cÛ\8cÛ\95Ú©Û\95 Ù¾Ù\88Ù\88Ú\86Û\95Úµ Ø¨Ú©Ø±Û\8eتÛ\95Ù\88Û\95 Ù\84Û\95بÛ\95ر Ú©Û\8eØ´Û\95Û\8c Ø¯Û\95ستتÛ\8eÙ\88Û\95رداÙ\86.",
        "undo-norev": "ناتوانی دەستکاریەکە ئەنجام‌نەدراو بکەی لەبەر ئەوەی بوونی نیە یا سڕدراوەتەوە.",
        "undo-nochange": "وا دیارە دەستکارییەکە پووچەڵ کراوەتەوە.",
        "undo-summary": "گەڕاندنەوەی پێداچوونەوەی $1 لە لایەن [[Special:Contributions/$2|$2]] ([[User talk:$2|لێدوان]])",
        "rcfilters-filterlist-title": "فیلتەرەکان",
        "rcfilters-filterlist-whatsthis": "ئەمە چییە؟",
        "rcfilters-highlightmenu-title": "ڕەنگێکی نوێ ھەڵبژێرە",
-       "rcfilters-filter-registered-label": "تۆمارکراو",
-       "rcfilters-filter-registered-description": "ئەو بەکارھێنەرانەی لە ژوورەوەن",
-       "rcfilters-filter-unregistered-label": "تۆمارنەکراوەکان",
-       "rcfilters-filter-unregistered-description": "ئەو بەکارھێنەرانەی لە ژوورەوە نین",
        "rcfilters-filter-editsbyself-label": "مافەکانی خۆت",
        "rcfilters-filter-editsbyself-description": "دەستکارییەکانی خۆت.",
        "rcfilters-filter-editsbyother-label": "دەستکارییەکانی کەسانی تر",
        "rcfilters-filter-editsbyother-description": "ھەموو گۆڕانکارییەکان بێجگە لەوەی خۆت",
+       "rcfilters-filter-user-experience-level-registered-label": "تۆمارکراو",
+       "rcfilters-filter-user-experience-level-registered-description": "ئەو بەکارھێنەرانەی لە ژوورەوەن",
+       "rcfilters-filter-user-experience-level-unregistered-label": "تۆمارنەکراوەکان",
+       "rcfilters-filter-user-experience-level-unregistered-description": "ئەو بەکارھێنەرانەی لە ژوورەوە نین",
        "rcfilters-filter-user-experience-level-newcomer-label": "تازەکاران",
        "rcfilters-filter-user-experience-level-newcomer-description": "کەمتر لە ١٠ دەستکاری و ٤ ڕۆژ لە چالاک بوون",
        "rcfilters-filter-user-experience-level-experienced-label": "بەکارھێنەرانی پێشکەوتوو",
        "fileduplicatesearch-result-n": "پەڕگەی «$1» {{PLURAL:$2|١ دووپاتکراوەی کوتوموتی|$2 دووپاتکراوەی کوتوموتی}} ھەیە.",
        "fileduplicatesearch-noresults": "پەڕگەیەک بە ناوی «$1» نەدۆزرایەوە.",
        "specialpages": "پەڕە تایبەتەکان",
-       "specialpages-note": "* پەڕە تایبەتە ئاساییەکان.\n* <span class=\"mw-specialpagerestricted\">پەڕە تایبەتە بەرگریلێکراوەکان.</span>",
        "specialpages-group-maintenance": "ڕاپۆرتەکانی چاکسازی",
        "specialpages-group-other": "پەڕە تایبەتەکانی دیکە",
        "specialpages-group-login": "چوونەژوورەوە / دروستکردنی ھەژمار",
        "logentry-newusers-byemail": "ھەژماری بەکارھێنەریی $3 لە لایەن $1 {{GENDER:$2|دروست کرا}} و تێپەڕوشە بە ئیمەیل نێردرا",
        "logentry-newusers-autocreate": "ھەژماری بەکارھێنەریی $1 بە شێوەی خۆگەڕ {{GENDER:$2|دروست کرا}}",
        "logentry-protect-move_prot": "$1 {{GENDER:$2}} ڕێکخستنەکانی پاراستنی گۆڕی لە $4 بۆ $3",
+       "logentry-protect-unprotect": "$1 {{GENDER:$2|پاراستنی}} لەسەر $3 لابرد",
        "logentry-protect-protect": "$1 $3ی {{GENDER:$2|پاراست}} $4",
        "logentry-protect-modify": "$1 ئاستی پاراستنی $3ی {{GENDER:$2|گۆڕی}} $4",
        "logentry-rights-rights": "$1 ئەندامێتیی {{GENDER:$6|$3}}ی لە $4 بۆ $5 {{GENDER:$2|گۆڕی}}",
index 9d4f46f..bc4e002 100644 (file)
        "rcfilters-filter-editsbyself-description": "Vaše vlastní příspěvky.",
        "rcfilters-filter-editsbyother-label": "Změny ostatních",
        "rcfilters-filter-editsbyother-description": "Všechny změny kromě vašich.",
-       "rcfilters-filtergroup-userExpLevel": "Úroveň zkušeností (pouze registrovaných uživatelů)",
+       "rcfilters-filtergroup-userExpLevel": "Registrace a zkušenost uživatelů",
        "rcfilters-filter-user-experience-level-registered-label": "Registrovaní",
        "rcfilters-filter-user-experience-level-registered-description": "Přihlášení editoři.",
        "rcfilters-filter-user-experience-level-unregistered-label": "Neregistrovaní",
        "rcfilters-typeofchange-conflicts-hideminor": "Tento filtr podle typu změny je v konfliktu s filtrem „Malé editace“. Určité typy změn nelze označit jako malé.",
        "rcfilters-filtergroup-lastRevision": "Aktuální verze",
        "rcfilters-filter-lastrevision-label": "Aktuální verze",
-       "rcfilters-filter-lastrevision-description": "Poslední změna stránky.",
+       "rcfilters-filter-lastrevision-description": "Jen poslední změna stránky.",
        "rcfilters-filter-previousrevision-label": "Dřívější verze",
        "rcfilters-filter-previousrevision-description": "Všechny změny, které nejsou nejnovější úpravou stránky.",
        "rcfilters-view-tags": "Označené editace",
        "delete-warning-toobig": "Tato stránka má velkou historii editací, přes $1 {{PLURAL:$1|verzi|verze|verzí}}. Mazání takových stránek může narušit databázové operace {{grammar:2sg|{{SITENAME}}}}; postupujte opatrně.",
        "deleteprotected": "Tuto stránku nemůžete smazat, protože je zamčena.",
        "deleting-backlinks-warning": "<strong>Upozornění:</strong> Stránka, kterou se chystáte smazat, je [[Special:WhatLinksHere/{{FULLPAGENAME}}|na jiných stránkách]] odkazována nebo je do nich vložena.",
+       "deleting-subpages-warning": "<strong>Upozornění:</strong> Stránka, kterou se chystáte smazat, má [[Special:PrefixIndex/{{FULLPAGENAME}}|{{PLURAL:$1|podstránku|$1 podstránky|$1 podstránek|51=více než 50 podstránek}}]].",
        "rollback": "Vrátit zpět editace",
        "rollbacklink": "vrácení zpět",
        "rollbacklinkcount": "vrácení $1 {{PLURAL:$1|editace|editací}} zpět",
        "fileduplicatesearch-noresults": "Žádný soubor s názvem „$1“ nebyl nalezen.",
        "specialpages": "Speciální stránky",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Normální speciální stránky\n* <span class=\"mw-specialpagerestricted\">Speciální stránky s&nbsp;vyhrazeným přístupem</span>",
        "specialpages-group-maintenance": "Údržba",
        "specialpages-group-other": "Ostatní",
        "specialpages-group-login": "Přihlášení / vytvoření účtu",
index e0b6f72..6a78683 100644 (file)
        "headline_sample": "Tekst nadgłówka",
        "headline_tip": "Nadgłówk 2 lédżi",
        "nowiki_sample": "Wstawi tuwò niesfòrmatowóny tekst",
-       "nowiki_tip": "Ignorëjë wiki-fòrmatowanié",
+       "nowiki_tip": "Jignorëjë wiki-fòrmatowanié",
        "image_sample": "Przëmiôr.jpg",
        "image_tip": "Òbsôdzony lopk (n.p. òbrôzk)",
        "media_sample": "Przëmiôr.ogg",
        "template-protected": "(zazychrowónô)",
        "template-semiprotected": "(dzélowò zazychrowóné)",
        "hiddencategories": "Na starna przënôleżi do w {{PLURAL:$1|1 zatacony kategòrëji|$1 zataconych kategòrëjów}}:",
-       "permissionserrors": "Fela przistspù",
+       "permissionserrors": "Fela przistãpù",
        "permissionserrorstext-withaction": "Ni môsz przëstãpù do $2, z {{PLURAL:$1|nôslédny przëczënë|nôslédnych przëczënów}}:",
        "recreate-moveddeleted-warn": "<strong>Bôczënk! Chcesz usadzëc starnã, chtërna wczasni òsta rëmniãtô.</strong>\n\nÙgwësni sã, czë pònowné ùsôdzenié ti starnë je kònieczné. \nNiżi je widzec register rëmaniów i zmian pòzwë ti starnë:",
        "moveddeleted-notice": "Na starna òsta rëmniãtô.\nSpisënk rëmaniô ë zjinaków miona ti starnë je niżi.",
        "gender-female": "Białka",
        "email": "E-mail",
        "prefs-help-realname": "Prôwdzëwé miono je òptacjowé, a czej je dôsz, òstanié ùżëté do pòdpisaniô Twòjégò wkładu",
-       "prefs-help-email": "Adresa e-mail je òptacëjnô, zezwôlô równak sélac do ce nową parolã jak tã zabëjesz.\nMòżesz zezwòlëc jinszim brëkòwniką na łączbã z Tobą przez Twòją starnã abò starnã diskùsëji, bez mùszebnotë wëskrzënianiô swòjich pòdôwków.",
-       "editinguser": "Zmiana praw brëkòwnika '''[[User:$1|$1]]''' ([[User talk:$1|{{int:talkpagelinktext}}]]{{int:pipe-separator}}[[Special:Contributions/$1|{{int:contribslink}}]])",
+       "prefs-help-email": "Adresa e-mail je òptacjowô, zezwôlô równak na zresetowanié zabëti przez ce parolë.",
+       "editinguser": "Zmiana prawa przistãpù {{GENDER:$1|brëkòwnika|brëkòwniczczi}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-reason": "Przëczëna:",
        "group": "Karno:",
        "group-user": "Brëkòwnicë",
        "group-bot-member": "{{GENDER:$1|bòt}}",
        "group-sysop-member": "{{GENDER:$1|sprôwnik}}",
        "group-bureaucrat-member": "{{GENDER:$1|biórokrata|biórokratka}}",
-       "group-suppress-member": "rewizora",
+       "group-suppress-member": "{{GENDER:$1|rewizora|rewizorka}}",
        "grouppage-user": "{{ns:project}}:Brëkòwnicë",
        "grouppage-autoconfirmed": "{{ns:project}}:Aùtomatno zacwierdzeni brëkòwnicë",
        "grouppage-bot": "{{ns:project}}:Bòtë",
        "right-reupload-shared": "Môlowé nadpisëwanié egzystëjącegò lopka, we wespóldzelnych dostónkach",
        "right-upload_by_url": "Wladënk lopka z adresë URL",
        "right-purge": "Czëszczenié pòdrãczny pamiãcë starnë bez pëtaniô ò pòcwierdzenié",
-       "right-autoconfirmed": "Edicëjô dzélowò zazychrowónych starnów",
+       "right-autoconfirmed": "Bez ògrańczeniów przez òpiartë na adresë IP limitë",
        "right-bot": "Nacéchòwanié edicëjó jakno aùtomatnych",
        "right-writeapi": "Zapisënk przez jinterfejs API",
        "newuserlogpage": "Nowi brëkòwnicë",
        "newpageletter": "N",
        "boteditletter": "b",
        "rc-change-size-new": "$1 {{PLURAL:$1|bajt|bajtë|bajtów}} pò zjinace",
-       "rc-enhanced-expand": "Pòkażë detale (wëmôgô JavaScript)",
+       "rc-enhanced-expand": "Pòkażë detale",
        "rc-enhanced-hide": "Zatacë detale",
        "rc-old-title": "originalno ùsôdzoné jakno \"$1\"",
        "recentchangeslinked": "Zmianë w dolënkòwónëch",
        "uploadnologin": "Felënk logòwaniô",
        "uploadtext": "Brëkùjë negò fòrmùlara do wladënkù lopków.\nJeżlë chcesz przezdrzec abò szëkac w dotenczas wladowónëch lopkach, biéj do [[Special:FileList|lësta lopków]]. Kòżdi wladënk je registrowóny w [[Special:Log/upload|registrze wladënkù]], a rëmniãcé w [[Special:Log/delete|registrze rëmaniô]].\n\nAbë dodac lopk do starnë, ùżëjë ùniższegò lënka wedle nôslédnëch mùstrów:\n* '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:Lopk.jpg]]</nowiki></code>''' wëskrzëni całi lopk\n* '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:Lopk.png|200px|thumb|left|pòdpisënk òbrôzka]]</nowiki></code>''' wëskrzëni z lewi starnë, przë ùbrzégù, miniaturkã w szérzë 200 pikslów w ramie, z nôdpisã 'pòdpisënk òbrôzka'\n* '''<code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:Lopk.ogg]]</nowiki></code>''' òtemknie prosti lënk do lopka bez wëskrzënianiô sómegò lopka",
        "uploadlogpage": "Dołączoné",
-       "uploadlogpagetext": "Hewò je lësta slédno wladowónëch lopków.\nWszëtczé gòdzënë tikają conë ùniwersalnégò czasë.",
+       "uploadlogpagetext": "Lësta slédno wladowónëch lopków.\nBiéj do [[Special:NewFiles|galerëji nowich lopków]] abë òbôczëc jich miniaturczi.",
        "filename": "Miono lopka",
        "filedesc": "Òpisënk",
        "fileuploadsummary": "Pòdrechòwanié:",
        "statistics-edits-average": "Strzédnô lëczba edicji na starnã",
        "statistics-users": "Zaregistrowónëch [[Special:ListUsers|brëkòwników]]",
        "statistics-users-active": "Aktiwnëch brëkòwników",
-       "statistics-users-active-desc": "Brekòwnicë, jaczi bëlë aktiwni òb òstatné $1 dni",
+       "statistics-users-active-desc": "Brekòwnicë, chtërni bëlë aktiwny {{PLURAL:$1|slédnegò dnia|slédnych $1 dni}}",
        "doubleredirects": "Dëbeltné przeczérowania",
        "double-redirect-fixer": "Naprôwiôcz przeczérowaniów",
        "brokenredirects": "Zerwóné przeczerowania",
        "categories": "Kategòrëje",
        "deletedcontributions": "Rëmniãti wkłôd brëkòwnika",
        "deletedcontributions-title": "Rëmniãti wkłôd brëkòwnika",
-       "linksearch": "Bùtnowé lënczi",
+       "linksearch": "Szëkba bùtnowich lënków",
        "activeusers": "Lësta aktiwnëch brëkòwników",
        "listgrouprights-members": "(lësta nôlëżników karna)",
        "emailuser": "Wëslë e-maila do negò brëkòwnika",
-       "defemailsubject": "E-mail òd {{SITENAME}}",
+       "defemailsubject": "{{SITENAME}} – e‐mail òd brëkòwnika \"$1\"",
        "noemailtitle": "Felënk email-adresë",
        "emailusername": "Pòzwa brëkòwnika",
        "emailfrom": "Òd:",
        "mywatchlist": "Lësta ùzérónëch artiklów",
        "watchlistfor2": "Dlô $1 $2",
        "watchnologin": "Felënk logòwóniô",
-       "addedwatchtext": "Starna \"[[:$1]]\" òsta dodónô do twòji [[Special:Watchlist|lëstë ùzérónëch artiklów]].\nNa ti lësce są registre przińdnëch zjinak ti starne ë na ji starnie dyskùsëji, a samò miono starnë mdze '''wëtłëszczone''' na [[Special:RecentChanges|lësce slédnich edicëji]], bë të mògł to òbaczëc.\n\nCzej chcesz remôc starnã z lëste ùzéronëch artiklów, klikni ''Òprzestôj ùzérac''.",
-       "removedwatchtext": "Starna \"[[:$1]]\" òsta rëmniãtô z Twòji [[Special:Watchlist|lëstë ùzérónych]].",
+       "addedwatchtext": "Starna \"[[:$1]]\" òsta dodónô do twòji [[Special:Watchlist|lëstë ùzérónëch artiklów]].",
+       "removedwatchtext": "Starna \"[[:$1]]\" ze starną diskùsëji òsta rëmniãtô z Twòji [[Special:Watchlist|lëstë ùzérónych artiklów]].",
        "watch": "Ùzérôj",
        "watchthispage": "Ùzérôj ną starnã",
        "unwatch": "Òprzestôj ùzerac",
        "rollbackfailed": "Nie szło copnąc zmianë",
        "alreadyrolled": "Ni mòże copnąc slédny edicëji starnë [[:$1]], chtërny ùsôdzcą je [[User:$2|$2]] ([[User talk:$2|Diskùsëjô]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nchtos jiny ju zeditowôł starnã abò copnął zmianë.\n\nSlédnym ùsódzcą starnë bëł [[User:$3|$3]] ([[User talk:$3|Diskùsëjô]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "revertpage": "Edicje brëkòwnika [[Special:Contributions/$2|$2]] ([[User talk:$2|diskùsjô]]) òstałë òdrzucóné. Aùtorã przëwrócóny wersji je [[User:$1|$1]].",
-       "rollback-success": "Edicje brëkòwnika $1 òstałë òdrzucóné; \nòsta przëwrócónô òstatnô wersjô, aùtorã chtërny je $2.",
+       "rollback-success": "Copniãto edicëje{{GENDER:$3|brëkòwnmika|brëkòwniczczi}} $1;\ndoprowpdzono nazôd slédną wersëjã ùsôdzcë {{GENDER:$4|$2}}.",
        "rollback-success-notify": "Edicje brëkòwnika $1 òstałë òdrzucóné; \nòsta przëwrócónô òstatnô wersjô, aùtorã chtërny je $2. [$3 Pòkażë zjinaczi]",
        "protectlogpage": "Zazychrowóné",
        "protectedarticle": "zazychrowónô [[$1]]",
        "modifiedarticleprotection": "zmienionô léga zazychrowaniô [[$1]]",
-       "unprotectedarticle": "òdzychrowóny [[$1]]",
+       "unprotectedarticle": "òdzychrowôÅ\82(wa) \"[[$1]]\"",
        "protectedarticle-comment": "{{GENDER:$2|Zazychrowôł|Zazychrowała}} „[[$1]]”",
        "prot_1movedto2": "$1 przeniesłé do $2",
        "protect-legend": "Pòcwierdzë zazychrowanié",
        "protect_expiry_old": "Czas wëgasniãcô leżi w przińdnocë.",
        "protect-text": "Mòżesz tuwò sprôwdzëc ë zjinaczëc légã zazychrowaniô starnë '''$1'''.",
        "protect-locked-access": "Ni môsz dosc prawa do zjinaczi lédżi zazychrowaniô starnë. Aktualny nastôw dlô starnë '''$1''':",
-       "protect-cascadeon": "Na starna je zazychrowónô przed edicëją, dlôte że je brëkòwónô przez {{PLURAL:$1|nôslédną starnã, chtërnô òsta zazychrowónô|nôslédné starnë, chtërné òstałe zazychrowóné}} z aktiwną kaskadową òpatcëją zazychrowëwaniô.\nMòżesz zmienic légã zazychrowaniô, nie bãdze to równak miało cëskù na kaskadowé zazychrowanié.",
+       "protect-cascadeon": "Na starna je zazychrowónô przed edicëją, dlôte że je brëkòwónô przez {{PLURAL:$1|nôslédną starnã, chtërnô òsta zazychrowónô|nôslédné starnë, chtërné òstałe zazychrowóné}} z aktiwną kaskadową òpatcëją zazychrowëwaniô.\nZmiana miarë zazychrowaniô ni mô cëskù na kaskadowé zazychrowanié.",
        "protect-default": "Zezwòlë wszëtczim brëkòwnikòm",
-       "protect-fallback": "Wëmôgô prawów \"$1\"",
-       "protect-level-autoconfirmed": "Blokùjë nowich ë nieregistrowónëch brëkòwników",
-       "protect-level-sysop": "blós sprôwnicë (sysopë)",
+       "protect-fallback": "Zezwòlë blós brëkòwnikòm z prawama \"$1\"",
+       "protect-level-autoconfirmed": "Zezwòlë blós aùtomatno zacwierdzonym brëkòwnikòm",
+       "protect-level-sysop": "Zezwòlë blós sprôwnikòm",
        "protect-summary-cascade": "kaskadowanié",
        "protect-expiring": "wëgasô $1 (UTC)",
        "protect-expiry-indefinite": "na wiedno",
        "sp-contributions-newbies": "Pòkażë edicëjã blós nowich brëkòwników",
        "sp-contributions-newbies-sub": "Dlô nowich brëkòwników",
        "sp-contributions-blocklog": "historëjô blokòwaniô",
-       "sp-contributions-deleted": "rëmniãti wkłôd brëkòwnika",
+       "sp-contributions-deleted": "rëmniãtô robòta {{GENDER:$1|brëkòwnika|brëkòwniczczi}}",
        "sp-contributions-uploads": "Wësłóné lopczi",
        "sp-contributions-logs": "Rejestr logòwaniô",
-       "sp-contributions-talk": "diskùsjô",
+       "sp-contributions-talk": "diskùsëjô",
        "sp-contributions-blocked-notice-anon": "Ta adresa IP je w tim sztërkù zablokòwónô.\nSlédny wpisënk z registru blokòwaniów je widzec niżi:",
        "sp-contributions-search": "Szëkba za edicëjama",
        "sp-contributions-username": "Adresa IP abò miono brëkòwnika:",
        "whatlinkshere-hidelinks": "$1 lënczi",
        "whatlinkshere-hideimages": "$1 lënk z lopków",
        "whatlinkshere-filters": "Filtrë",
-       "blockip": "Zascëgôj IP-adresã",
+       "blockip": "Blokùjë {{GENDER:$1|brëkòwnika|brëkòwniczkã}}",
        "blockiptext": "Brëkùje formùlarza niżi abë zascëgòwac prawò zapisënkù spòd gwësny adresë IP. To robi sã blós dlôte abë zascëgnąc wandalëznom, a bëc w zgòdze ze [[{{MediaWiki:Policy-url}}|wskôzama]]. Pòdôj przëczënã (np. dając miona starn, na chtërnëch dopùszczono sã wandalëzny).",
        "ipbreason": "Przëczëna:",
        "ipboptions": "2 gòdzënë:2 hours,1 dzéń:1 day,3 dni:3 days,1 tidzéń:1 week,2 tigòdnie:2 weeks,1 ksãżëc:1 month,3 ksãżëcë:3 months,6 ksãżëców:6 months,1 rok:1 year,na wiedno:infinite",
        "badipaddress": "IP-adresa nie je richtich pòdónô.",
        "blockipsuccesssub": "Zascëgónié dało sã",
        "blockipsuccesstext": "Brëkòwnik [[Special:Contributions/$1|$1]] òstał zascëgóny.<br />\nBiéj do [[Special:BlockList|lëstë zascëgónëch adresów IP]] abë òbaczëc zascëdżi.",
-       "ipblocklist": "Lësta zablokòwónëch adresów IP ë mionów brëkòwników",
+       "ipblocklist": "Zablokòwóni brëkòwnicë",
        "blocklist-timestamp": "Czasowô sygnatura",
        "blocklist-target": "Cél",
        "blocklist-expiry": "Ùpłiwô",
        "unblocklink": "òdblokùjë",
        "change-blocklink": "zmieni blokòwanié",
        "contribslink": "wkłôd",
-       "autoblocker": "Zablokòwóno ce aùtomatnie, ga brëkùjesz ti sami adresë IP co brëkòwnik \"[[User:$1|$1]]\". Przëczënô blokòwóniô $1 to: \"'''$2'''\".",
+       "autoblocker": "Zablokòwóno ce aùtomatno bò brëkùjesz ti sómy adresë IP co brëkòwnik \"[[User:$1|$1]]\". \nPrzëczënô blokòwaniô $1 to: \"$2\"",
        "blocklogpage": "Historëjô blokòwaniô",
        "blocklogentry": "zablokòwôł [[$1]], czas blokadë: $2 $3",
        "reblock-logentry": "{{GENDER:$2|zjinacził|zjinacziła}} unastôw blokadë dlô [[$1]], czas blokadë: $2 $3",
        "proxyblocker": "Blokòwanié proxy",
        "lockbtn": "Zascëgôj bazã pòdôwków",
        "move-page-legend": "Przeniesë starnã",
-       "movepagetext": "Z pòmòcą ùiższegò fòrmùlôra zjinaczisz miono starnë, przenosząc równoczasno ji historëjã.\nPòd stôrim titlã bãdze ùsôdzonô przeczérowùjącô starna.\nMòżesz aùtomatno zaktualniac przeczérowania wskazëwôjące titel przed zjinaką.\nJeżlë nie wëbiérzesz ti òptacëji, ùgwësni sã pò przenieseniu starnë, czë nie òstałé ùsôdzoné [[Special:DoubleRedirects|dëbeltné]] abò [[Special:BrokenRedirects|zerwóné przeczérowania]].\nJes òdpòwiedzalny za to, abë lënczi dali robiłë tam dze mają.\n\nStarna '''ni''' bãdze przeniosłô, jeżlë starna ò nowim mionie ju je, chòba że je òna pùstô abò je przeczérowaniém ë mô pùstą historëjã edicëji.\nTo òznôczô, że lëchą òperacëjã zjinaczi miona mòże doprowôdzëc bezpieczno nazôd, zjinaczając nowé miono starnë nawczasniészą, ë że ni mòże nadpisac stranë chtërną ju dô.\n\n'''BÔCZËNK!'''\nTo mòże bëc drasticznô abò nieprzewidëwólnô zjinaka w przëtrôfkù pòpùlarnych starnów.\nÙgwësni sã co do skùtków ti òperacëji, niglë to zrobisz.",
+       "movepagetext": "Z pòmòcą ùiższegò fòrmùlôra zjinaczisz miono starnë, przenosząc równoczasno ji historëjã.\nPòd stôrim titlã bãdze ùsôdzonô przeczérowùjącô starna.\nMòżesz aùtomatno zaktualniac przeczérowania wskazëwôjące titel przed zjinaką.\nJeżlë nie wëbiérzesz ti òptacëji, ùgwësni sã pò przenieseniu starnë, czë nie òstałé ùsôdzoné [[Special:DoubleRedirects|dëbeltné]] abò [[Special:BrokenRedirects|zerwóné przeczérowania]].\nJes òdpòwiedzalny za to, abë lënczi dali robiłë tam dze mają.\n\nStarna <strong>ni</strong> bãdze przeniosłô, jeżlë starna ò nowim mionie ju je, chòba że je òna pùstô abò je przeczérowaniém ë mô pùstą historëjã edicëji.\nTo òznôczô, że lëchą òperacëjã zjinaczi miona mòże doprowôdzëc bezpieczno nazôd, zjinaczając nowé miono starnë nawczasniészą, ë że ni mòże nadpisac stranë chtërną ju dô.\n\n<strong>BÔCZËNK!</strong>\nTo mòże bëc drasticznô abò nieprzewidëwólnô zjinaka w przëtrôfkù pòpùlarnych starnów.\nÙgwësni sã co do skùtków ti òperacëji, niglë to zrobisz.",
        "movepagetalktext": "Sparłãczonô starna diskùsëji, jeżlë ju je, to bãdze przeniosłô aùtomatno, chòba że:\n*niepùstô starna diskùsëji ju je z nowim mionã\n*rëmniész nacéchòwanié z niższegò pòla wëbiérkù\n\nW taczich przëtrôfkach zamkłosc diskùsëji mòże przeniesc blós rãczno.",
        "newtitle": "Nowi titel:",
        "move-watch": "Ùzérôj tã starnã",
        "allmessagesnotsupportedDB": "'''{{ns:special}}:Allmessages''' nie mòże bëc brëkòwónô, temù że '''$wgUseDatabaseMessages''' je wëłączony.",
        "thumbnail-more": "Zwikszi",
        "import": "Impòrtëjë starnë",
-       "importlogpage": "Log impòrtu",
+       "importlogpage": "Log jimpòrtu",
        "tooltip-pt-userpage": "{{GENDER:|Twòja}} starna brëkòwnika",
        "tooltip-pt-mytalk": "{{GENDER:|Mòjô}} starna diskùsëji",
        "tooltip-pt-anontalk": "Diskùsjô brëkòwnika dlô ti adresë IP",
        "tooltip-summary": "Wpiszë wãzłowati òpisënk",
        "anonymous": "Anonimòwi {{PLURAL:$1|brëkòwnik|brëkòwnicë}} na {{SITENAME}}",
        "siteuser": "Brëkòwnik {{SITENAME}} $1",
-       "lastmodifiedatby": "Na starna bëła slédno editowónô $2, $1 przez $3.",
+       "lastmodifiedatby": "Slédno edicëjô ti starnë: $2, $1, ùsôdzca: $3.",
        "othercontribs": "Òpiarté na prôcë $1.",
        "others": "jiné",
        "spamprotectiontitle": "Anti-spamòwi filter",
        "pageinfo-robot-policy": "Jindeksowanié przez robòtë",
        "pageinfo-robot-index": "Zezwòloné",
        "pageinfo-robot-noindex": "Niedozwóloné",
-       "pageinfo-watchers": "Wielëna ùżérających",
+       "pageinfo-watchers": "Wielëna ùzérających",
        "pageinfo-few-watchers": "Mni jak $1 {{PLURAL:$1|ùzyrający|ùzyrających}}",
        "pageinfo-redirects-name": "Wielëna przeczérowaniów do ti starnë",
        "pageinfo-subpages-name": "Wielëna pòdstarnów ti starnë",
        "patrol-log-page": "Log patrolowaniô",
        "previousdiff": "← Pòprzédnô edicëjô",
        "nextdiff": "Nôslédnô edicëjô →",
-       "imagemaxsize": "Ògrańczë na starnie òpisënkù òbrôzków jich miarã do:",
+       "imagemaxsize": "Ograńczenié wielgòscë òbrôzków:<br /><em>(na starnach òpisënkù lopków)</em>",
        "thumbsize": "Miara miniaturków:",
        "widthheightpage": "$1 × $2, $3 {{PLURAL:$3|starna|starnë|starnów}}",
        "file-info-size": "$1 × $2 pikslów, miara lopka: $3, ôrt MIME: $4",
        "imgmultigo": "Biéj!",
        "imgmultigoto": "Biéj do starnë $1",
        "autoredircomment": "Przeczérowanié do [[$1]]",
-       "autosumm-new": "Pòwsta nowô starna:",
+       "autosumm-new": "Ùsôdzonô nowô starna \"$1\"",
        "watchlisttools-clear": "Wëczësczë ùzérówną lëstã",
        "watchlisttools-view": "Òbaczë wôżnészé zmianë",
        "watchlisttools-edit": "Òbaczë a editëjë lëstã ùzérónëch artiklów",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|diskùsëjô]])",
        "version": "Wersëjô",
        "redirect": "Przeczérëjë z jidentyfikatora lopka, brëkòwnika, starnë, wersëji abò wpisënka loga",
-       "redirect-summary": "Na szpecjalnô starna przczerowùje do: lopka(ò pòdónym mionie), do sstarny (ò pòdónym numrze wersëji abò jidentyfikatorze starë), do starnë brëkòwnika (ò pòdónym numerowim jidentyfikatorze) abò do rejestru (ò pòdónym numrze akcëji). Òrt ùżëcô: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]] abò [[{{#Special:Redirect}}/logid/186]].",
+       "redirect-summary": "Na specjalnô starna przczerowùje do: lopka(ò pòdónym mionie), do starny (ò pòdónym numrze wersëji abò jidentyfikatorze starë), do starnë brëkòwnika (ò pòdónym numerowim jidentyfikatorze) abò do rejestru (ò pòdónym numrze akcëji). Òrt ùżëcô: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]] abò [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Biéj",
        "redirect-lookup": "Szëkôj:",
        "redirect-value": "Wôrtnota:",
index de353e4..d4ad69f 100644 (file)
        "fileduplicatesearch-noresults": "Ni ddaethpwyd o hyd i ffeil o'r enw \"$1\".",
        "specialpages": "Tudalennau arbennig",
        "specialpages-note-top": "Allwedd",
-       "specialpages-note": "* Tudalennau arbennig ar gael i bawb.\n* <span class=\"mw-specialpagerestricted\">Tudalennau arbennig cyfyngedig.</span>",
        "specialpages-group-maintenance": "Adroddiadau cynnal a chadw",
        "specialpages-group-other": "Eraill",
        "specialpages-group-login": "Mewngofnodi / creu cyfrif",
index 68727dd..2c080f6 100644 (file)
@@ -62,7 +62,8 @@
                        "Jhertel",
                        "IBDJ",
                        "SimmeD",
-                       "BoBrandt"
+                       "BoBrandt",
+                       "R12ntech"
                ]
        },
        "tog-underline": "Understreg henvisninger:",
        "rcfilters-highlightmenu-help": "Vælg en farve for at fremhæve denne egenskab",
        "rcfilters-filterlist-noresults": "Ingen filtre fundet",
        "rcfilters-noresults-conflict": "Ingen resultater fundet fordi søgekriterierne er i konflikt",
-       "rcfilters-filtergroup-registration": "Brugerregistrering",
-       "rcfilters-filter-registered-label": "Registrerede",
-       "rcfilters-filter-registered-description": "Indloggede brugere",
-       "rcfilters-filter-unregistered-label": "Uregistrerede",
-       "rcfilters-filter-unregistered-description": "Redaktører, der ikke er logget ind.",
        "rcfilters-filtergroup-authorship": "Bidragets forfatter",
        "rcfilters-filter-editsbyself-label": "Ændringer af dig",
        "rcfilters-filter-editsbyself-description": "Dine egne bidrag.",
        "rcfilters-filter-editsbyother-label": "Ændringer af andre",
        "rcfilters-filter-editsbyother-description": "Alle ændringer undtagen din egen.",
        "rcfilters-filtergroup-userExpLevel": "Erfaringsniveau (kun for registrerede brugere)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Erfaringsfiltre finder kun registrerede brugere, så dette filter er i konflikt med filtret \"Uregistrerede\".",
+       "rcfilters-filter-user-experience-level-registered-label": "Registrerede",
+       "rcfilters-filter-user-experience-level-registered-description": "Indloggede brugere",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Uregistrerede",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Redaktører, der ikke er logget ind.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Nybegyndere",
        "rcfilters-filter-user-experience-level-newcomer-description": "Færre end 10 redigeringer og 4 dages aktivitet",
        "rcfilters-filter-user-experience-level-learner-label": "Let øvede",
        "version-libraries-license": "Licens",
        "version-libraries-description": "Beskrivelse",
        "version-libraries-authors": "Forfattere",
-       "redirect": "Omdirigering pga. fil, bruger-, side- eller udgave-ID",
-       "redirect-summary": "Denne specialside omdirigerer til en fil (hvis filnavnet er angivet), en side (hvis udgave ID'et eller side ID'et er angivet) eller en brugerside (hvis et numerisk brugernummer er angivet). Eksempler på brug: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]],[[{{#Special:Redirect}}/revision/328429]] eller [[{{#Special:Redirect}}/user/101]].",
+       "redirect": "Omdiriger via filnavn, bruge-, side-, revision- eller log-ID.",
+       "redirect-summary": "Denne spesialsiden omdirigerer til en fil (hvis et filnavn angis), en lave (om revisions- eller side-ID angis), en brugerside (om bruge-ID angis), eller en loggoppføring (om log-ID angis). Bruk: [[{{#Special:Redirect}}/file/Eksempel.jpg]], [[{{#Special:Redirect}}/page/#64308]], [[{{#Special:Redirect}}/revision/#328429]], [[{{#Special:Redirect}}/user/#101]] eller [[{{#Special:Redirect}}/logid/#186]].",
        "redirect-submit": "Vis",
        "redirect-lookup": "Slå op:",
        "redirect-value": "Værdi:",
        "fileduplicatesearch-noresults": "Ingen fil med navnet \"$1\" blev fundet.",
        "specialpages": "Specialsider",
        "specialpages-note-top": "Forklaring",
-       "specialpages-note": "* Normale specialsider.\n* <span class=\"mw-specialpagerestricted\">Specialsider med begrænset adgang.</span>",
        "specialpages-group-maintenance": "Vedligeholdelsesside",
        "specialpages-group-other": "Andre specialsider",
        "specialpages-group-login": "Log på / opret bruger",
        "htmlform-title-not-exists": "$1 findes ikke.",
        "logentry-delete-delete": "$1 {{GENDER:$2|slettede}} siden $3",
        "logentry-delete-delete_redir": "$1 {{GENDER:$2|slettede}} omdirigering $3 ved overskrivning",
-       "logentry-delete-restore": "$1 {{GENDER:$2|gendannede}} siden $3",
+       "logentry-delete-restore": "$1 {{GENDER:$2|gendannede}} siden $3 ($4)",
        "logentry-delete-event": "$1 {{GENDER:$2|ændrede}} synligheden af {{PLURAL:$5|en loghændelse|$5 loghændelser}} for siden $3: $4",
        "logentry-delete-revision": "$1 {{GENDER:$2|ændrede}} synligheden af {{PLURAL:$5|en version|$5 versioner}} af siden $3: $4",
        "logentry-delete-event-legacy": "$1 {{GENDER:$2|ændrede}} synligheden af loghændelser for siden $3",
index 61b4e9b..0969376 100644 (file)
        "rcfilters-hideminor-conflicts-typeofchange-global": "Der Filter „Kleine Bearbeitungen“ kollidiert mit einem oder mehreren Änderungstypfiltern, da bestimmte Änderungstypen nicht als „klein“ festgelegt werden können. Die kollidierenden Filter sind oben im Bereich der aktiven Filter markiert.",
        "rcfilters-hideminor-conflicts-typeofchange": "Bestimmte Änderungstypen können nicht als „klein“ festgelegt werden, so dass dieser Filter mit den folgenden Änderungstypfiltern kollidiert: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Dieser Änderungstypfilter kollidiert mit dem Filter „Kleine Bearbeitungen“. Bestimmte Änderungstypen können nicht als „klein“ festgelegt werden.",
-       "rcfilters-filtergroup-lastRevision": "Letzte Version",
-       "rcfilters-filter-lastrevision-label": "Letzte Version",
-       "rcfilters-filter-lastrevision-description": "Die aktuellste Änderung an einer Seite.",
-       "rcfilters-filter-previousrevision-label": "Frühere Versionen",
-       "rcfilters-filter-previousrevision-description": "Alle Änderungen, die nicht die aktuellste Änderung an einer Seite sind.",
+       "rcfilters-filtergroup-lastRevision": "Aktuellste Versionen",
+       "rcfilters-filter-lastrevision-label": "Aktuellste Version",
+       "rcfilters-filter-lastrevision-description": "Nur die aktuellste Änderung an einer Seite.",
+       "rcfilters-filter-previousrevision-label": "Nicht die aktuellste Version",
+       "rcfilters-filter-previousrevision-description": "Alle Änderungen, die nicht die „aktuellste Version“ sind.",
        "rcfilters-filter-excluded": "Ausgeschlossen",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:nicht</strong> $1",
        "rcfilters-exclude-button-off": "Ausgewählte ausschließen",
        "fileduplicatesearch-noresults": "Es wurde keine Datei namens „$1“ gefunden.",
        "specialpages": "Spezialseiten",
        "specialpages-note-top": "Legende",
-       "specialpages-note": "* Reguläre Spezialseiten\n* <span class=\"mw-specialpagerestricted\">Zugriffsbeschränkte Spezialseiten</span>",
+       "specialpages-note-restricted": "* Reguläre Spezialseiten\n* <span class=\"mw-specialpagerestricted\">Zugriffsbeschränkte Spezialseiten</span>",
        "specialpages-group-maintenance": "Wartungslisten",
        "specialpages-group-other": "Andere Spezialseiten",
        "specialpages-group-login": "Benutzerkonto",
index b9b8980..eed494d 100644 (file)
        "oct": "Pthi",
        "nov": "Ptht",
        "dec": "Pthr",
-       "pagecategories": "{{PLURAL:$1|bekätakthook|bekätakthuɔk}}",
+       "pagecategories": "{{PLURAL:$1|Bekätakthook|Bekätakthuɔk}}",
        "category_header": "Apääm në bekätakthook \"$1\"ic",
        "subcategories": "Bekätakthuɔkkor",
-       "category-media-header": "Kuat në bekätakthook  $1 yic",
+       "category-media-header": "Kuat në bekätakthook \"$1\" yic",
        "hidden-categories": "{{PLURAL:$1|Bekätakthook cï thiaan|Bekätakthuɔk cï thiaan}}",
        "category-subcat-count": "{{PLURAL:$2|Bekätakthookë anɔŋ bekätakthookkorkɛ̈ kepɛ̈c.|Bekätakthookë anɔŋ {{PLURAL:$1|bekätakthookkorë|$1 bekätakthuɔkkorkɛ̈}}, në $2 yic̈;}}",
        "category-article-count": "{{PLURAL:$2|Bekätakthookë anɔŋic yärë yetök.|{{PLURAL:$1|Yärë atɔ̈|$1 yɔ̈rkɛ̈ aatɔ̈}} bekätakthook thiöökë yic, në $2 yic.}}",
        "recentchangeslinked-toolbox": "Kaceyiicwar nɔŋ kar",
        "recentchangeslinked-title": "Weer thöŋ kekë \"$1\"",
        "recentchangeslinked-summary": "Kän areny de wɛ̈r cïloi wɛ̈ramɛn tënɔŋ apam nuɛtke apam nhic (nadëk ka nuɛtke kɔcakuut de bekätakthook nhic).\nApam tɔ̈ [[Special:Watchlist|abërtïtdu]] aa <strong>gɔ̈tdïtnyin</strong>.",
-       "recentchangeslinked-page": "Rin ë akap:",
+       "recentchangeslinked-page": "Rin ë apam:",
        "recentchangeslinked-to": "Nyuɔɔthë kä cï ke waar në apɛ̈m cï nuɛ̈ɛ̈t ke apam tiöökë, ku acie kä cï ke waar në yen apam thiöökë yic",
        "upload": "Wälë apamduööt",
        "filedesc": "Cuutyic",
        "watchlist": "Abërtït",
        "mywatchlist": "Abërtït",
        "watch": "Ɣoi",
+       "watchthispage": "Watch this page",
        "watchlist-hide": "Thaan",
        "watchlist-submit": "Nyooth",
        "historyaction-submit": "Nyooth",
index c87a676..6fec714 100644 (file)
        "specialpages": "Special pages",
        "specialpages-summary": "",
        "specialpages-note-top": "Legend",
-       "specialpages-note": "* Normal special pages.\n* <span class=\"mw-specialpagerestricted\">Restricted special pages.</span>",
+       "specialpages-note-restricted": "* Normal special pages.\n* <span class=\"mw-specialpagerestricted\">Restricted special pages.</span>",
+       "specialpages-note-cached": "-",
        "specialpages-group-maintenance": "Maintenance reports",
        "specialpages-group-other": "Other special pages",
        "specialpages-group-login": "Login / create account",
index 9dbfd7f..965bc54 100644 (file)
        "rcfilters-legend-heading": "<strong>Lista de abreviaturas:</strong>",
        "rcfilters-activefilters": "Filtros activos",
        "rcfilters-advancedfilters": "Filtros avanzados",
+       "rcfilters-limit-title": "Cambios para mostrar",
+       "rcfilters-limit-shownum": "Mostrar los últimos $1 cambios",
+       "rcfilters-days-title": "Días recientes",
+       "rcfilters-hours-title": "Horas recientes",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|día|días}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|hora|horas}}",
        "rcfilters-quickfilters": "Filtros guardados",
        "rcfilters-quickfilters-placeholder-title": "Ningún enlace guardado aún",
        "rcfilters-quickfilters-placeholder-description": "Para guardar tus ajustes de filtro y reutilizarlos más tarde, pulsa en el icono del marcador en el área de Filtro activo que se encuentra a continuación.",
        "rcfilters-invalid-filter": "Filtro no válido",
        "rcfilters-empty-filter": "No hay filtros activos. Se muestran todas las contribuciones.",
        "rcfilters-filterlist-title": "Filtros",
-       "rcfilters-filterlist-whatsthis": "¿Qué es esto?",
+       "rcfilters-filterlist-whatsthis": "¿Cómo funcionan?",
        "rcfilters-filterlist-feedbacklink": "Comparte tus comentarios sobre los filtros (beta) nuevos",
        "rcfilters-highlightbutton-title": "Resaltar los resultados",
        "rcfilters-highlightmenu-title": "Selecciona un color",
        "rcfilters-hideminor-conflicts-typeofchange-global": "El filtro \"Ediciones menores\" está en conflicto con uno o más Tipos de filtros de Cambio, ya que ciertos tipos de cambio no pueden ser designados como \"menores\". Los filtros en conflicto están marcados en el área Filtros activos, anterior.",
        "rcfilters-hideminor-conflicts-typeofchange": "Ciertos tipos de cambio no pueden ser designados como \"menores\", por lo que este filtro entra en conflicto con los siguientes  Tipos de filtros de Cambio: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Este filtro «Tipo de cambio» entra en conflicto con el filtro «Ediciones menores». Hay ciertos tipos de cambios que no pueden denominarse «menores».",
-       "rcfilters-filtergroup-lastRevision": "Revisión actual",
-       "rcfilters-filter-lastrevision-label": "Revisión actual",
-       "rcfilters-filter-lastrevision-description": "El cambio más reciente a una página.",
-       "rcfilters-filter-previousrevision-label": "Revisiones anteriores",
-       "rcfilters-filter-previousrevision-description": "Todos los cambios que no son los más recientes cambian a una página.",
+       "rcfilters-filtergroup-lastRevision": "Últimas revisiones",
+       "rcfilters-filter-lastrevision-label": "Última revisión",
+       "rcfilters-filter-lastrevision-description": "Solo el cambio más reciente a una página.",
+       "rcfilters-filter-previousrevision-label": "No la última revisión",
+       "rcfilters-filter-previousrevision-description": "Todos los cambios que no son la \"última revisión\".",
        "rcfilters-filter-excluded": "Excluido",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>Estado:</strong> $1",
+       "rcfilters-exclude-button-off": "Excluir los seleccionados",
+       "rcfilters-exclude-button-on": "Excluyendo los seleccionados",
        "rcfilters-view-tags": "Ediciones etiquetadas",
        "rcfilters-view-namespaces-tooltip": "Filtrar resultados por espacio de nombres",
        "rcfilters-view-tags-tooltip": "filtrado de resultados usando etiquetas de edición",
        "fileduplicatesearch-noresults": "Ningún archivo con el nombre «$1» encontrado.",
        "specialpages": "Páginas especiales",
        "specialpages-note-top": "Leyenda",
-       "specialpages-note": "* Páginas especiales normales.\n* <span class=\"mw-specialpagerestricted\">Páginas especiales restringidas.</span>",
        "specialpages-group-maintenance": "Informes de mantenimiento",
        "specialpages-group-other": "Otras páginas especiales",
        "specialpages-group-login": "Acceder/crear cuenta",
index b3bed3d..a8d1e96 100644 (file)
        "recentchanges-legend-heading": "<strong>Seletus:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (vaata ka [[Special:NewPages|uute lehekülgede loendit]])",
        "recentchanges-submit": "Näita",
+       "rcfilters-legend-heading": "<strong>Lühendite loetelu:</strong>",
        "rcfilters-activefilters": "Aktiivsed filtrid",
        "rcfilters-advancedfilters": "Täpsemad filtrid",
+       "rcfilters-limit-title": "Näita nii mitut muudatust",
+       "rcfilters-limit-shownum": "Näita viimast $1 muudatust",
+       "rcfilters-days-title": "Viimased päevad",
+       "rcfilters-hours-title": "Viimased tunnid",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|päev|päeva}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|tund|tundi}}",
        "rcfilters-quickfilters": "Salvestatud filtrid",
        "rcfilters-quickfilters-placeholder-title": "Linke pole veel salvestatud",
        "rcfilters-quickfilters-placeholder-description": "Et filtri sätted salvestada ja et neid hiljem uuesti kasutada, klõpsa alloleva aktiivsete filtrite loendi juures järjehoidjaikooni.",
        "rcfilters-invalid-filter": "Vigane filter",
        "rcfilters-empty-filter": "Aktiivsed filtrid puuduvad. Näidatakse kogu kaastööd.",
        "rcfilters-filterlist-title": "Filtrid",
-       "rcfilters-filterlist-whatsthis": "Mis see on?",
+       "rcfilters-filterlist-whatsthis": "Kuidas see töötab?",
        "rcfilters-filterlist-feedbacklink": "Anna uute filtrite beetaversiooni kohta tagasisidet",
        "rcfilters-highlightbutton-title": "Tulemuste esiletõst",
        "rcfilters-highlightmenu-title": "Vali värvus",
        "rcfilters-noresults-conflict": "Tulemusi ei leitud, sest otsikriteeriumid on konfliktsed.",
        "rcfilters-state-message-subset": "See filter ei tee midagi, sest selle tulemused on kaasatud {{PLURAL:$2|järgmise laiema filtri|järgmiste laiemate filtrite}} tulemustes (tulemuste eristamiseks proovi esiletõstu): $1",
        "rcfilters-state-message-fullcoverage": "Ühe rühma kõigi filtrite valimine on samaväärne mitte ühegi filtri valimisega, mistõttu ei tee see filter midagi. Rühmas on: $1",
-       "rcfilters-filtergroup-registration": "Registreerumine",
-       "rcfilters-filter-registered-label": "Registreerunud",
-       "rcfilters-filter-registered-description": "Sisse logitud kasutajad.",
-       "rcfilters-filter-unregistered-label": "Registreerumata",
-       "rcfilters-filter-unregistered-description": "Kasutajad, kes pole sisse logitud.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "See filter on konfliktis {{PLURAL:$2|järgmise kogemustaseme filtriga|järgmiste kogemustasemete filtritega}}, mis {{PLURAL:$2|leiab|leiavad}} ainult registreerunud kasutajaid: $1",
        "rcfilters-filtergroup-authorship": "Kaastöö autorsus",
        "rcfilters-filter-editsbyself-label": "Enda muudatused",
        "rcfilters-filter-editsbyself-description": "Sinu enda muudatused.",
        "rcfilters-filter-editsbyother-label": "Teiste muudatused",
        "rcfilters-filter-editsbyother-description": "Kõik muudatused peale sinu enda omade.",
-       "rcfilters-filtergroup-userExpLevel": "Kogemustase (ainult registreerunud kasutajate puhul)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Kogemustaseme filtrid leiavad ainult registreerunud kasutajaid, mistõttu on see filter konfliktis filtriga \"{{int:rcfilters-filter-unregistered-label}}\".",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Filter \"{{int:rcfilters-filter-unregistered-label}}\" on konfliktis vähemalt ühe kogemustaseme filtriga, mis leiab ainult registreerunud kasutajaid. Konfliktsed filtrid on ära märgitud ülal aktiivsete filtrite loendis.",
+       "rcfilters-filtergroup-userExpLevel": "Registreerumine ja kasutaja kogemus",
+       "rcfilters-filter-user-experience-level-registered-label": "Registreerunud",
+       "rcfilters-filter-user-experience-level-registered-description": "Sisse logitud kasutajad.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Registreerumata",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Kasutajad, kes pole sisse logitud.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Äsjaalustanud",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Alla 10 muudatuse või tegutsenud alla 4 päeva.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Registreerunud toimetajad, teinud alla 10 muudatuse või tegutsenud alla 4 päeva.",
        "rcfilters-filter-user-experience-level-learner-label": "Tutvujad",
-       "rcfilters-filter-user-experience-level-learner-description": "Rohkem kogemust kui äsjaalustanutel, aga vähem kui kogenud kasutajatel.",
+       "rcfilters-filter-user-experience-level-learner-description": "Registreerunud toimetajad, kellel on rohkem kogemust kui äsjaalustanutel, aga vähem kui kogenud kasutajatel.",
        "rcfilters-filter-user-experience-level-experienced-label": "Kogenud kasutajad",
-       "rcfilters-filter-user-experience-level-experienced-description": "Üle 500 muudatuse ja tegutsenud üle 30 päeva.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Registreerunud toimetajad, teinud üle 500 muudatuse ja tegutsenud üle 30 päeva.",
        "rcfilters-filtergroup-automated": "Automaatne kaastöö",
        "rcfilters-filter-bots-label": "Robot",
        "rcfilters-filter-bots-description": "Automaattööriistade tehtud muudatused.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "Filter \"{{int:rcfilters-filter-minor-label}}\" on konfliktis vähemalt ühe muudatuste tüübifiltriga, sest teatud tüüpi muudatusi ei saa märkida pisimuudatusteks. Konfliktsed filtrid on ära märgitud ülal aktiivsete filtrite loendis.",
        "rcfilters-hideminor-conflicts-typeofchange": "Teatud tüüpi muudatusi ei saa märkida pisimuudatusteks. Seetõttu on see filter konfliktis järgmiste tüübifiltritega: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "See muudatuste tüübifilter on konfliktis filtriga \"{{int:rcfilters-filter-minor-label}}\". Teatud tüüpi muudatusi ei saa märkida pisimuudatusteks.",
-       "rcfilters-filtergroup-lastRevision": "Viimane redaktsioon",
+       "rcfilters-filtergroup-lastRevision": "Viimased redaktsioonid",
        "rcfilters-filter-lastrevision-label": "Viimane redaktsioon",
-       "rcfilters-filter-lastrevision-description": "Muudatus, mis on leheküljel kõige viimane.",
-       "rcfilters-filter-previousrevision-label": "Varasemad redaktsioonid",
-       "rcfilters-filter-previousrevision-description": "Kõik muudatused, mis pole leheküljel kõige viimased.",
+       "rcfilters-filter-lastrevision-description": "Ainult muudatus, mis on leheküljel kõige viimane.",
+       "rcfilters-filter-previousrevision-label": "Pole viimane redaktsioon",
+       "rcfilters-filter-previousrevision-description": "Kõik muudatused, mis pole kõige viimased.",
        "rcfilters-filter-excluded": "Välja arvatud",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:mitte</strong> $1",
+       "rcfilters-exclude-button-off": "Jäta valitud välja",
+       "rcfilters-exclude-button-on": "Valitud välja jäetud",
        "rcfilters-view-tags": "Märgistatud muudatused",
+       "rcfilters-view-namespaces-tooltip": "Filtri tulemusi nimeruumide lõikes",
+       "rcfilters-view-tags-tooltip": "Filtri tulemusi muudatusmärgiste lõikes",
+       "rcfilters-view-return-to-default-tooltip": "Naase filtri peamenüüsse",
+       "rcfilters-liveupdates-button": "Uuendused reaalajas",
        "rcnotefrom": "Allpool on toodud {{PLURAL:$5|muudatus|muudatused}} alates: <strong>$3, kell $4</strong> (näidatakse kuni <strong>$1</strong> muudatust)",
        "rclistfromreset": "Lähtesta kuupäeva valik",
        "rclistfrom": "Näita muudatusi alates: $3, kell $2",
        "delete-warning-toobig": "See lehekülg on pika redigeerimislooga – üle {{PLURAL:$1|ühe muudatuse|$1 muudatuse}}.\nEttevaatust, selle kustutamine võib esile kutsuda häireid {{GRAMMAR:genitive|{{SITENAME}}}} andmebaasi töös.",
        "deleteprotected": "Seda lehekülge ei saa kustutada, sest see on kaitstud.",
        "deleting-backlinks-warning": "<strong>Hoiatus:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|Teised leheküljed]] viitavad leheküljele, mida oled kustutamas, või see lehekülg on kasutuses mallina.",
+       "deleting-subpages-warning": "<strong>Hoiatus:</strong> Oled kustutamas lehekülge, millel on [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|alamlehekülg|$1 alamlehekülge|51=üle 50 alamlehekülje}}]].",
        "rollback": "Tühista muudatused",
        "rollbacklink": "tühista",
        "rollbacklinkcount": "tühista {{PLURAL:$1|üks muudatus|$1 muudatust}}",
        "undelete-search-title": "Kustutatud lehekülgede otsimine",
        "undelete-search-box": "Kustutatud lehekülgede otsimine",
        "undelete-search-prefix": "Näita lehekülgi, mille pealkiri algab nii:",
+       "undelete-search-full": "Näita leheküljepealkirju, milles sisaldub:",
        "undelete-search-submit": "Otsi",
        "undelete-no-results": "Kustutatud lehekülgede arhiivist sellist lehekülge ei leidunud.",
        "undelete-filename-mismatch": "Failiversiooni ajatempliga $1 ei saa taastada, sest failinimed ei klapi.",
        "fileduplicatesearch-noresults": "Faili nimega \"$1\" ei leidu.",
        "specialpages": "Erileheküljed",
        "specialpages-note-top": "Seletus",
-       "specialpages-note": "* Harilikud erileheküljed.\n* <span class=\"mw-specialpagerestricted\">Piiranguga erileheküljed.</span>",
        "specialpages-group-maintenance": "Hooldusaruanded",
        "specialpages-group-other": "Teised erileheküljed",
        "specialpages-group-login": "Sisselogimine ja konto loomine",
index 30c00cb..12c7fbd 100644 (file)
        "rcfilters-filter-newpages-description": "Orri berriak egiten dituzten aldaketak",
        "rcfilters-filter-categorization-label": "Kategoria aldaketak",
        "rcfilters-filter-logactions-label": "Erregistratutako ekintzak",
-       "rcfilters-filtergroup-lastRevision": "Azken berrikuspena",
+       "rcfilters-filtergroup-lastRevision": "Azken berrikuspenak",
        "rcfilters-filter-lastrevision-label": "Azken berrikuspena",
        "rcfilters-filter-lastrevision-description": "Orrialde bati eginiko aldaketarik berriena.",
-       "rcfilters-filter-previousrevision-label": "Aurreko berrikuspenak",
+       "rcfilters-filter-previousrevision-label": "Ez da azken berrikuspena",
+       "rcfilters-filter-previousrevision-description": "\"Azken berrikuspena\" ez diren aldaketa guztiak.",
        "rcfilters-filter-excluded": "Baztertua",
        "rcfilters-exclude-button-off": "Baztertzea aukeratuta",
        "rcfilters-exclude-button-on": "Baztertzea aukeratuta",
        "fileduplicatesearch-noresults": "Ez da aurkitu \"$1\" izeneko fitxategirik.",
        "specialpages": "Orri bereziak",
        "specialpages-note-top": "Azalpenak",
-       "specialpages-note": "* Orri berezi arruntak.\n* <strong class=\"mw-specialpagerestricted\">Mugatutako orri bereziak.</strong>",
+       "specialpages-note-restricted": "* Orrialde arrunt bereziak.\n* <span class=\"mw-specialpagerestricted\">Restricted special pages.</span>",
        "specialpages-group-maintenance": "Mantentze-oharrak",
        "specialpages-group-other": "Beste orri berezi batzuk",
        "specialpages-group-login": "Hasi saioa / sortu kontua",
index e1ed208..900871b 100644 (file)
        "rcfilters-hideminor-conflicts-typeofchange-global": "Le filtre « Modifications mineures » est en conflit avec au moins un filtre de Type de modification, parce que certains types de modification ne peuvent être marqués comme « mineurs ». Les filtres en conflit sont marqués dans la zone Filtres actifs ci-dessus.",
        "rcfilters-hideminor-conflicts-typeofchange": "Certains types de modification ne peuvent pas être qualifiés de « mineurs », donc ce filtre est en conflit avec les filtres de Type de modification suivants : $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Ce filtre de Type de modification est en conflit avec le filtre « Modifications mineures ». Certains type sde modification ne peuvent pas être indiqués comme « mineurs ».",
-       "rcfilters-filtergroup-lastRevision": "Version actuelle",
-       "rcfilters-filter-lastrevision-label": "Version actuelle",
-       "rcfilters-filter-lastrevision-description": "Dernière modification apportée à une page.",
-       "rcfilters-filter-previousrevision-label": "Versions précédentes",
-       "rcfilters-filter-previousrevision-description": "Toutes les modifications apportées à une page et qui ne sont pas la dernière.",
+       "rcfilters-filtergroup-lastRevision": "Dernières révisions",
+       "rcfilters-filter-lastrevision-label": "Dernière révision",
+       "rcfilters-filter-lastrevision-description": "Uniquement la dernière modification apportée à une page.",
+       "rcfilters-filter-previousrevision-label": "Pas la dernière version",
+       "rcfilters-filter-previousrevision-description": "Toutes les modifications apportées à une page et qui ne concernent pas la « dernière version ».",
        "rcfilters-filter-excluded": "Exclu",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:not</strong> $1",
        "rcfilters-exclude-button-off": "Exclure les sélectionnés",
        "fileduplicatesearch-noresults": "Aucun fichier nommé « $1 » n'a été trouvé.",
        "specialpages": "Pages spéciales",
        "specialpages-note-top": "Légende",
-       "specialpages-note": "* Pages spéciales normales.\n* <span class=\"mw-specialpagerestricted\">Pages spéciales restreintes.</span>",
+       "specialpages-note-restricted": "* Pages spéciales normales.\n* <span class=\"mw-specialpagerestricted\">Pas spéciales restreintes.</span>",
        "specialpages-group-maintenance": "Rapports de maintenance",
        "specialpages-group-other": "Autres pages spéciales",
        "specialpages-group-login": "S'identifier / s'inscrire",
index c811a1b..f2fe856 100644 (file)
        "errorpagetitle": "Diar as wat skiaf gingen",
        "returnto": "Turag tu sidj $1.",
        "tagline": "Faan {{SITENAME}}",
-       "help": "Halep",
+       "help": "MediaWiki Halep",
        "search": "Schük",
        "search-ignored-headings": " #<!-- Detdiar rä ei feranre --> <pre>\n# Auerskraften, diar bi't schüken ei beaachtet wurd.\n# Jodiar feranrangen wurd seekert, wan det sidj mä det auerskraft indeksiaret wurden as.\n# Dü könst det sidjenindeksiarang föörtji, wan dü en nul-edit maagest.\n# Syntax:\n#   * Ales, wat bääft en dobelkrüs („#“) stäänt, as en komentaar.\n#   * Arke rä, wat ei leesag as, as di akeroot tiitel, diar ei beaachtet woort.\nFutnuuten\nFerwisangen\nLuke uk diar\n #</pre> <!-- Detdiar rä ei feranre -->",
        "searchbutton": "Schük",
        "fileduplicatesearch-noresults": "Nian datei mä di nööm „$1“ fünjen.",
        "specialpages": "Spezial-sidjen",
        "specialpages-note-top": "Legend",
-       "specialpages-note": "* Normool spezial-sidjen\n* <span class=\"mw-specialpagerestricted\">Spezial-sidjen mä tugripsrochten</span>",
        "specialpages-group-maintenance": "Werksteedsidjen",
        "specialpages-group-other": "Ööder spezial-sidjen",
        "specialpages-group-login": "Melde di uun of skriiw di iin",
index 2be194e..012e8f8 100644 (file)
        "rcfilters-legend-heading": "<strong>Lista de abreviaturas:</strong>",
        "rcfilters-activefilters": "Filtros activos",
        "rcfilters-advancedfilters": "Filtros avanzados",
+       "rcfilters-limit-title": "Modificacións a amosar",
+       "rcfilters-limit-shownum": "Amosar as últimas $1 modificacións",
+       "rcfilters-days-title": "Últimos días",
+       "rcfilters-hours-title": "Últimas horas",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|día|días}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|hora|horas}}",
        "rcfilters-quickfilters": "Filtros gardados",
        "rcfilters-quickfilters-placeholder-title": "Aínda non se gardou ningunha ligazón",
        "rcfilters-quickfilters-placeholder-description": "Para gardar a configuración dos seus filtros e reutilizala máis tarde, prema na icona do marcador na área de Filtro activo que se atopa a abaixo.",
        "rcfilters-invalid-filter": "Filtro no válido",
        "rcfilters-empty-filter": "Non hai filtros activos. Móstranse tódalas contribucións.",
        "rcfilters-filterlist-title": "Filtros",
-       "rcfilters-filterlist-whatsthis": "Que é isto?",
+       "rcfilters-filterlist-whatsthis": "Como funciona isto?",
        "rcfilters-filterlist-feedbacklink": "Deixar comentarios sobre os novos filtros (en fase beta)",
        "rcfilters-highlightbutton-title": "Resaltar resultados",
        "rcfilters-highlightmenu-title": "Seleccione unha cor",
        "rcfilters-filter-editsbyself-description": "As súas contribucións",
        "rcfilters-filter-editsbyother-label": "Modificacións doutros.",
        "rcfilters-filter-editsbyother-description": "Tódolos cambios, excepto os seus.",
-       "rcfilters-filtergroup-userExpLevel": "Nivel de experiencia (só para usuarios rexistrados)",
-       "rcfilters-filter-user-experience-level-registered-label": "Rexistrado",
+       "rcfilters-filtergroup-userExpLevel": "Rexistro de usuarios e experiencia",
+       "rcfilters-filter-user-experience-level-registered-label": "Rexistrados",
        "rcfilters-filter-user-experience-level-registered-description": "Editores autenticados.",
-       "rcfilters-filter-user-experience-level-unregistered-label": "Non rexistrado",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Non rexistrados",
        "rcfilters-filter-user-experience-level-unregistered-description": "Editores que non están autenticados.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Chegados recentemente",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Menos de 10 edicións e 4 días de actividade.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Editores rexistrados con menos de 10 edicións e 4 días de actividade.",
        "rcfilters-filter-user-experience-level-learner-label": "Aprendices",
-       "rcfilters-filter-user-experience-level-learner-description": "Máis experimentado que os \"usuarios novatos\" pero menos que os \"usuarios experimentados\".",
+       "rcfilters-filter-user-experience-level-learner-description": "Editores rexistrados cuxa experiencia está entre os \"usuarios novatos\" e os \"usuarios experimentados\".",
        "rcfilters-filter-user-experience-level-experienced-label": "Usuarios experimentados",
-       "rcfilters-filter-user-experience-level-experienced-description": "Máis de 30 días de actividade e 500 edicións.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Editores rexistrados con máis de 500 edicións e 30 días de actividade.",
        "rcfilters-filtergroup-automated": "Contribucións automatizadas",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Edicións realizadas por ferramentas automatizadas.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "O filtro \"edicións menores\" está en conflito con un ou máis filtros Tipo de modificación, porque certos tipos de modificación non poden designarse como \"menores\". Os filtros en conflito están marcados na zona Filtros activos, arriba.",
        "rcfilters-hideminor-conflicts-typeofchange": "Certos tipos de modificación non poden designarse como \"menores\", polo que este filtro entra en conflito cos seguintes filtros Tipo de modificaciónː $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Este filtro Tipo de modificación entra en conflito co filtro \"Modificacións menores\". Certos tipos de modificación non poden designarse como \"menores\".",
-       "rcfilters-filtergroup-lastRevision": "Versión actual",
-       "rcfilters-filter-lastrevision-label": "Versión actual",
-       "rcfilters-filter-lastrevision-description": "A última modificación a unha páxina.",
-       "rcfilters-filter-previousrevision-label": "Versións anteriores",
-       "rcfilters-filter-previousrevision-description": "Tódolos cambios realizados nunha páxina e que non son os máis recentes.",
+       "rcfilters-filtergroup-lastRevision": "Últimas revisións",
+       "rcfilters-filter-lastrevision-label": "Últimas revisións",
+       "rcfilters-filter-lastrevision-description": "Só a última modificación a unha páxina.",
+       "rcfilters-filter-previousrevision-label": "Non a última edición",
+       "rcfilters-filter-previousrevision-description": "Tódolos cambios realizados nunha páxina e que non son a \"última modificación\".",
        "rcfilters-filter-excluded": "Excluído",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:non</strong> $1",
+       "rcfilters-exclude-button-off": "Excluír os seleccionados",
+       "rcfilters-exclude-button-on": "Excluíndo os seleccionados",
        "rcfilters-view-tags": "Edicións marcadas",
        "rcfilters-view-namespaces-tooltip": "Filtrar resultados por espazo de nomes",
        "rcfilters-view-tags-tooltip": "Filtrar resultados usando etiquetas de edición",
        "delete-warning-toobig": "Esta páxina conta cun historial de edicións longo, de máis {{PLURAL:$1|dunha revisión|de $1 revisións}}.\nAo eliminala pódense provocar problemas de funcionamento nas operacións da base de datos de {{SITENAME}};\nproceda con coidado.",
        "deleteprotected": "Non pode borrar esta páxina porque está protexida.",
        "deleting-backlinks-warning": "<strong>Atención:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|Outras páxinas]] conteñen unha ligazón ou unha transclusión da páxina que está a piques de borrar.",
+       "deleting-subpages-warning": "<strong>Aviso:</strong> A páxina que quere eliminar ten [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|unha subpáxina|$1 subpáxinas|51=máis de 50 subpáxinas}}]].",
        "rollback": "Reverter as edicións",
        "rollbacklink": "reverter",
        "rollbacklinkcount": "reverter $1 {{PLURAL:$1|edición|edicións}}",
        "fileduplicatesearch-noresults": "Non se atopou ningún ficheiro chamado \"$1\".",
        "specialpages": "Páxinas especiais",
        "specialpages-note-top": "Lenda",
-       "specialpages-note": "* Páxinas especiais normais.\n* <span class=\"mw-specialpagerestricted\">Páxinas especiais restrinxidas.</span>",
+       "specialpages-note-restricted": "* Páxinas especiais normais.\n* <span class=\"mw-specialpagerestricted\">Páxinas especiais restrinxidas.</span>",
        "specialpages-group-maintenance": "Informes de mantemento",
        "specialpages-group-other": "Outras páxinas especiais",
        "specialpages-group-login": "Rexistro",
index bafdb74..b1c3880 100644 (file)
        "newarticletext": "Yi'o lodudu'a wumbuta ode halaman diya'a. \nWonu mohutu halaman botiye, ketik tuwango halaman to kotak to tibawa botiye (bilohi [$1 halaman wubodu] ode habari wumbutiyo). \nWonu Yi'o ja sangaja tilumuwota ode halaman botiye, kutiya tombol <strong>mohuwalingo</strong>.",
        "noarticletext": "Sa'ati botiye diya'a teks to halaman botiye.\nYi'o mowali [[Special:Search/{{PAGENAME}}|mololohu  judul halaman botiye]] to halaman-halaman uweewo, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} mololohu log a'ayita], meyalo [{{fullurl:{{FULLPAGENAME}}|action=edit}} mohutu halaman botiye]</span>.",
        "noarticletext-nopermission": "!Sa'ati botiye diya'a teks to halaman boptiye.\nYi'o mowali [[Special:Search/{{PAGENAME}}|mololohu judul halaman botiye]] to halaman-halaman uweewo, meyalo <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} mololohu log a'ayita]</span>, dabo Yi'o ja o ijin mohutu halaman botiye.",
+       "userpage-userdoesnotexist-view": "Ta ohu'uwo \"$1\" diyaalu to daputari.",
        "editing": "Momoli'o $1",
        "creating": "Mohutu $1",
        "editingsection": "Momoli'o $1 (tayadu)",
        "moveddeleted-notice": "Halaman botiye ma yiluluto.\nSebagai referensi, botiya log piloluluta wawu piloheyiya halaman botiye.",
        "postedit-confirmation-saved": "Biloli'umu ma tilahu.",
        "edit-already-exists": "Ja mowali mohutu halaman bohu. Ma woluwo.",
+       "content-model-wikitext": "tuladu wiki",
        "viewpagelogs": "Bilohi log lo halaman botiye",
        "currentrev-asof": "Biloli'o pulitiyo to $1",
        "revisionasof": "Biloli'o to $1",
        "history-fieldset-title": "Lolohe u biloli'o",
        "histfirst": "mohihewo da'a",
        "histlast": "bohu da'a",
+       "history-feed-title": "Riwayati lo'u biloli'o",
+       "history-feed-description": "Riwayati bilolio to halaman wiki botiye",
+       "history-feed-item-nocomment": "$1 to $2",
        "rev-delundel": "popobilohe/wanto'a",
        "history-title": "Riwayati lo'u loboli'a lonto \"$1\"",
        "difference-title": "$1 hihede revisi",
        "lineno": "Baarisi $1:",
+       "compareselectedversions": "Popotadenga u tilulawoto",
        "editundo": "pohuwalinga",
        "diff-multi-sameuser": "({{PLURAL:$1|$1 revisi wolota}} pilohutu lo tawu ngota ja pilopobilohu)",
        "searchresults": "U yilotapu",
        "search-result-size": "$1 ({{PLURAL:$2|1 tahe|$2 tahe}})",
        "search-redirect": "(pilobale lonto $1)",
        "search-section": "(tayadu) $1",
+       "search-file-match": "(sama lo tuwango berkas)",
        "search-suggest": "Patujumu yito:$1",
        "searchall": "nga'amila",
        "search-showingresults": "{{PLURAL:$4|hASIL <strong>$1</strong> of <strong>$3</strong>|Hasil <strong>$1 - $2</strong> lonto <strong>$3</strong>}}",
        "search-nonefound": "Diya'a hasili mohumayawa lo kriteria",
        "powersearch-toggleall": "Nga'amila",
        "powersearch-togglenone": "Diya'a",
-       "powersearch-remember": "Eelayi u tilulawoto wonu mololohe pe'eentamayi",
+       "powersearch-remember": "Toloma u tilulawoto wonu mololohe pe'eenta mayi",
        "mypreferences": "Preperensi",
        "prefs-skin": "Alipo",
        "searchresultshead": "Lolohe",
        "prefs-searchoptions": "Lolohe",
        "prefs-namespaces": "Huwali lo tanggulo",
-       "default": "Kakali",
+       "default": "kakali",
        "yourrealname": "Tanggula banari",
        "yourlanguage": "Bahasa",
        "grouppage-bot": "{{ns:project}}:Bot",
        "recentchangeslinked-page": "Tanggulo halaman:",
        "recentchangeslinked-to": "Poppobilohe loboli'a to halaman wayitiyo wolo halaman hepoposadiyalo",
        "upload": "Detohe berkas",
+       "uploadlogpage": "Detohu log",
        "filedesc": "Limbu'o",
+       "license": "Lisensi",
        "license-header": "Tayadu lisensi",
        "imgfile": "berkas",
        "file-anchor-link": "Berkas",
        "linkstoimage": "{{PLURAL:$1|halaman lapatiyoma'o}} o wumbuta ode berkas botiye:",
        "nolinkstoimage": "Diya'a halaman u owumbuta ode berkas botiye",
        "sharedupload-desc-here": "Berkas botiye lonto $1 wawu hepohunaliyo to poroyek uweewo.\nDeskripsi lonto [$2 halaman deskripsiliyo] woluwo to tibawa botiya.",
+       "filepage-nofile": "Diya'a berkas lo tanggula botiye",
        "upload-disallowed-here": "Yi'o diila mowali modeehe berkas botiye",
        "randompage": "Halaman totonula",
        "statistics": "Statistik",
        "nmembers": "$1 {{PLURAL:$1|tuwango}}",
        "listusers": "Daputari ta ohu'uwo",
        "newpages": "Halaman bohu",
+       "move": "Heyiya",
        "pager-newer-n": "{{PLURAL:$1|bohu da'a|$1bohu da'a}}",
        "pager-older-n": "{{PLURAL:$1|$1 mohihewo}}",
        "booksources": "Bungo buku",
        "booksources-search-legend": "Lolohe to bungo lo buku",
        "booksources-search": "Lolohe",
        "log": "Log",
+       "all-logs-page": "Nga'amila log publik",
+       "allpages": "Nga'amila halaman",
        "allarticles": "Nga'amila halaman",
        "allpagessubmit": "Ntali",
        "categories": "Kategori",
+       "usermessage-editor": "Sistem lo tahuli",
+       "watchlist": "U he'awasiyalo",
        "mywatchlist": "Daputari he'awasiyalo",
        "watch": "Dahayi",
+       "unwatch": "Batali mongawasi",
        "wlshowlast": "Popobilohe $1 jam $2 dulahe pulitiyo",
        "dellogpage": "Log loluluto",
        "rollbacklink": "wuwalinga",
        "protectlogpage": "Log mopo'aamani",
        "protectedarticle": "modaha \"[[$1]]\"",
        "protect-default": "Poluliya nga'amila ta ohu'uwo",
+       "restriction-edit": "Boli'a",
+       "restriction-move": "Heyiya",
        "namespace": "Huwali lo tanggulo",
        "invert": "Pohuwalinga tilulawoto",
        "tooltip-invert": "Centang kotak botiye u mopowanto'o halaman yiloboli'a to delomo huwali lo tanggulo tilulawoto (wawu huwali lo tanggulo a'ayita wanu dicentang)",
        "contributions": "Kontribusi {{GENDER:$1|Ta ohu'uwo}}",
        "mycontris": "Kontribusi",
        "anoncontribs": "Kontribusi",
+       "contribsub2": "Ode {{GENDER:$3|$1}} ($2)",
        "uctop": "(masatiya)",
        "month": "Lonto hulalo (wawu to'udiipo)",
        "year": "Lonto taawunu (wawu to'udiipo)",
+       "sp-contributions-newbies": "Popobilohe bo lonto ta ohu'uwo bohu",
+       "sp-contributions-blocklog": "bubuli log",
+       "sp-contributions-uploads": "u diletohu",
        "sp-contributions-logs": "log",
        "sp-contributions-talk": "lo'iya",
        "sp-contributions-search": "Lolohe kontribusi",
+       "sp-contributions-username": "Alamat IP meyalo tanggulo ta ohu'uwo",
+       "sp-contributions-toponly": "Popobiloho bo biloli'a to yitaato",
+       "sp-contributions-newonly": "Popobilohe biloli'o bo u lohutu halaman",
        "sp-contributions-submit": "Lolohe",
        "whatlinkshere": "Wumbuta",
        "whatlinkshere-title": "Halaman botiye o wumbuta ode \"$1\"",
        "whatlinkshere-hidelinks": "$1 wumbuta",
        "whatlinkshere-hideimages": "$1 berkas wumbuta",
        "whatlinkshere-filters": "U'ayahu",
+       "ipboptions": "2 jam:2 hours,1 huyi:1 day,3 huyi:3 days,1 diminggu:1 week,2 diminggu:2 weeks,1 hula:1 month,3 hula:3 months,6 hula:6 months,1 taawunu:1 year,layito:infinite",
        "blocklink": "tangguwalo",
        "contribslink": "kontrib",
+       "blocklogpage": "Bubuli log",
+       "blocklogentry": "momubulo [[$1]] wolo pulito wakutu $2 $3",
+       "proxyblocker": "Bubulo proxi",
        "movelogpage": "Log piloheyiya",
        "export": "Ekspor halaman",
        "thumbnail-more": "Po'odamanga",
        "tooltip-ca-nstab-special": "Utiye halaman istimewa, wawu ja mowali boli'olo",
        "tooltip-ca-nstab-project": "Bilohi halaman poroyek",
        "tooltip-ca-nstab-image": "Bilohi berkas lo halaman",
+       "tooltip-ca-nstab-mediawiki": "Bilohi tahuli lo sistem",
        "tooltip-ca-nstab-template": "Bilohi template",
        "tooltip-ca-nstab-category": "Bilohi kategori halaman",
        "tooltip-save": "Tahuwa u biloli'umu",
        "tooltip-preview": "Bilohipo u biloli'umu. Popopasiya utiye to'u diipo molahu.",
        "tooltip-diff": "Bilohi u loboli'o pilohutumu",
        "tooltip-compareselectedversions": "Bilohi hihede lohalaman duluwo u tilulawoto.",
+       "tooltip-watch": "Poduhengama'o halaman botiye to daputari he'awasiyalo",
        "tooltip-rollback": "\"Wuwalingo\" lopobatali u pilo'opiyohu to halaman botiye ode kontributor pulitiyo pe'enta lo klik.",
        "tooltip-undo": "\"wuwalingo\" lopobatali u biloli'a botiye wawu lomu'o kotak momoli'o wolo mode pratayang. Alasani mowali duhengalo to kotak limbu-limbu'o.",
        "tooltip-summary": "Tuwota tulade limbu-limbu'o",
        "simpleantispam-label": "Momarakisa anti-spam.\n<strong>kekeya</strong> tuwangalo!",
+       "pageinfo-title": "Informasi untuk \"$1\"",
+       "pageinfo-header-basic": "Bungo lo habari",
+       "pageinfo-header-restrictions": "Dudaha halaman",
+       "pageinfo-display-title": "Judul bibilohu",
+       "pageinfo-length": "Haya'o halaman (to delomo bita)",
+       "pageinfo-article-id": "ID Halaman",
+       "pageinfo-language": "Bahasa tuwango halaman",
+       "pageinfo-robot-policy": "Pengindeksan monto robot",
+       "pageinfo-watchers": "Jumula lo ta hemongawasi halaman",
+       "pageinfo-redirects-name": "Jumula u pilobale ode halaman botiya",
+       "pageinfo-firsttime": "Tanggal pilohutuwa halaman",
+       "pageinfo-recent-edits": "Jumula boheli biloli'a mola (to delomo $1 pulitiyo)",
        "pageinfo-toolboxlink": "Halaman habari",
+       "pageinfo-contentpage-yes": "Jo",
        "previousdiff": "← Biloli'o to'udiipo",
        "nextdiff": "Biloli'o lapatiyoma'o →",
        "file-info-size": "$1 x $2 piksel, tu'udu berkas:$3, MIME tipe: $4",
        "namespacesall": "nga'amila",
        "monthsall": "nga'amila",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|bisala]])",
+       "redirect-submit": "Ntali",
+       "redirect-lookup": "Yilolohu",
+       "redirect-page": "ID Halaman",
+       "redirect-revision": "Halaman biloli'o",
+       "redirect-file": "Tanggulo berkas",
        "specialpages": "Halaman Spesial",
        "tag-filter": "[[Special:Tags|Tag]]filter:",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Tag}}]]: $2)",
        "tags-active-yes": "Jo",
        "logentry-delete-delete": "$1 {{GENDER:$2|moluluto}}halaman $3",
+       "revdelete-content-hid": "tuwango yilanto'o",
        "logentry-move-move": "$1 {{GENDER:$2|moheyi}} halaman $3 ode $4",
+       "logentry-move-move-noredirect": "$1 {{GENDER:$2|loheyi}} halaman $3 ode $4 ja lohutu pengalihan",
+       "logentry-move-move_redir": "$1 {{GENDER:$2|loheyi}} halaman $3 ode $4 lodeehu pengalihan",
        "logentry-newusers-create": "Ta ohu'uwo akun $1 {{GENDER:$2|mohutu}}",
+       "logentry-newusers-autocreate": "Akun $1 {{GENDER:$2|pilohutu}} otomatis",
        "logentry-upload-upload": "$1 {{GENDER:$2|mengunggah}} $3",
-       "searchsuggest-search": "Lolohe {{SITENAME}}"
+       "searchsuggest-search": "Lolohe {{SITENAME}}",
+       "duration-days": "$1 {{PLURAL:$1|huyi}}",
+       "randomrootpage": "Halaman totonulalo"
 }
index a81f2df..9306bff 100644 (file)
        "accmailtext": "E zuefällig generiert Passwort fir [[User talk:$1|$1]] isch an $2 gschickt wore.\n\nS Passwort fir des nej Benutzerkonto cha uf dr Spezialsyte „[[Special:ChangePassword|Passwort ändere]]“ gänderet wäre.",
        "newarticle": "(Nej)",
        "newarticletext": "Du bisch eme Link nogange zuen ere Syte, wu s nid git.\nZum die Syte aalege, chasch do in däm Chaschte unte aafange schrybe (lueg [$1 Hilfe] fir meh Informatione).\nWänn do nid hesch welle aane goh, no druck in Dyynem Browser uf '''Zruck'''.",
-       "anontalkpagetext": "----''Des isch e Diskussionssyte vun eme anonyme Benutzer, wu kei Zuegang aagleit het oder wu ne nit bruucht. Sälleweg mien mir di numerisch IP-Adräss bruuche zum ihn oder si z identifiziere. So ne IP-Adräss cha au vu mehrere Benutzer teilt wäre. Wenn Du ne anonyme Benutzer bisch un s Gfiel hesch, ass do irrelevanti Kommentar an di grichtet wäre, derno [[Special:CreateAccount|leg e Konto aa]] oder [[Special:UserLogin|mäld di aa]] zum in Zuekumft Verwirrige mit andere anonyme Benutzer z vermyyde.''",
+       "anontalkpagetext": "----\n<em>Des isch e Diskussionssyte vun eme anonyme Benutzer, wu kei Zuegang aagleit het oder wu ne nit bruucht.</em>\nSälleweg mien mir di numerisch IP-Adräss bruuche zum ihn oder si z identifiziere. So ne IP-Adräss cha au vu mehrere Benutzer teilt wäre. Wenn Du ne anonyme Benutzer bisch un s Gfiel hesch, ass do irrelevanti Kommentar an di grichtet wäre, derno [[Special:CreateAccount|leg e Konto aa]] oder [[Special:UserLogin|mäld di aa]] zum in Zuekumft Verwirrige mit andere anonyme Benutzer z vermyyde.",
        "noarticletext": "Uf däre Syte het s no kei Täxt. \nDu chasch uf andere Syte [[Special:Search/{{PAGENAME}}|dä Yytrag sueche]], <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} dr Logbuechyytrag sueche, wo dezue ghert],\noder [{{fullurl:{{FULLPAGENAME}}|action=edit}} die Syte erstelle]</span>.",
        "noarticletext-nopermission": "In däre Syte het s zur Zyt no kei Text.\nDu chasch dää Titel uf andre Syte [[Special:Search/{{PAGENAME}}|sueche]]\noder <span class=\"plainlinks\">in dr zuegherige [{{fullurl:{{#special:Log}}|page={{FULLPAGENAMEE}}}} Logbiecher sueche].</span> Du derfsch aber die Syte nit aalege.",
        "missing-revision": "D Version $1 vu dr Syte mit Name „{{FULLPAGENAME}}“ git s nit.\n\nDää Fähler chunnt normalerwyys dur e veraltete Link zue dr Versionsgschicht vun ere Syte, wu in dr Zwischezyt glescht woren isch.\nEinzelheite chasch im [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} Lesch-Logbuech] bschaue.",
        "userpage-userdoesnotexist": "S Benutzerkonto „<nowiki>$1</nowiki>“ git s nit. Bitte prief, eb Du die Syte wirkli wit aalege/bearbeite.",
        "userpage-userdoesnotexist-view": "S Benutzerkonto „$1“ isch nit registriert.",
        "blocked-notice-logextract": "Dää Benutzer isch zur Zyt gsperrt.\nAs Information chunnt do ne aktuälle Uuszug us em Benutzersperr-Logbuech:",
-       "clearyourcache": "'''Hiiwys:''' Noch em Spycheremuesch no dr Browser-Zwischespycher lääre go d Änderige sää.\n* '''Firefox/ Safari:''' ''Umschaltig'' drucken un glychzytig ''Aktualisiere'' aaklicken oder entwäder ''Strg+F5'' oder ''Strg+R'' (''Befehlstaste-R'' uf em Mac) drucke\n* '''Google Chrome:''' ''Umschaltig+Strg+R'' (''Befählstaschte-R'' uf em Mac) drucke\n* '''Internet Explorer:''' ''Strg+F5'' drucken oder ''Strg'' drucken un glychzytig ''Aktualisiere'' aaklicke\n* '''Opera:''' ''Extra → Internetspure lesche … → Individuäll Uuswahl → Dr komplett Cache lesche''",
+       "clearyourcache": "<strong>Hiiwys:</strong> Noch em Spycheremuesch no dr Browser-Zwischespycher lääre go d Änderige sää.\n* <strong>Firefox/ Safari:</strong> <em>Umschaltig</em> drucken un glychzytig <em>Aktualisiere</em> aaklicken oder entwäder <em>Strg+F5</em> oder <em>Strg+R</em> (<em>Befehlstaste-R</em> uf em Mac) drucke\n* <strong>Google Chrome:</strong> <em>Umschaltig+Strg+R</em> (<em>Befählstaschte-R</em> uf em Mac) drucke\n* <strong>Internet Explorer:</strong> <em>Strg+F5</em> drucken oder <em>Strg</em> drucken un glychzytig <em>Aktualisiere</em> aaklicke\n* <strong>Opera:</strong> Gang uff <em>Menü → Yystellige</em> (<em>Opera → Yystellige</em> uff eme Mac) un deno uff <em>Dateschutz & Sicherheit → Browserdate lösche → Gspyycherti Bilder un Dateie</em>.",
        "usercssyoucanpreview": "'''Tipp:''' Nimm dr „{{int:showpreview}}”-Chnopf, zum Dyy nej CSS vor em Spichere z teschte.",
        "userjsyoucanpreview": "'''Tipp:''' „Nimm dr {{int:showpreview}}”-Chnopf, zum Dyy nej JS vor em Spichere z teschte.",
        "usercsspreview": "== Vorschau vu Dyynem Benutzer-CSS. ==\n'''Wichtig:''' Noch em Spichere muesch Dyynem Browser sage, ass er die nej Version ladet:\n\n'''Mozilla:''' ''Strg-Shift-R'', '''IE:''' ''Strg-F5'', '''Safari:''' ''Cmd-Shift-R'', '''Konqueror:''' ''F5''.",
        "tooltip-feed-rss": "RSS-Feed für selli Syte",
        "tooltip-feed-atom": "Atom-Feed für selli Syte",
        "tooltip-t-contributions": "E Lischt vo de Byträg vo {{GENDER:$1|däm Benutzer}}",
-       "tooltip-t-emailuser": "Schick däm Benutzer e E-Bost",
+       "tooltip-t-emailuser": "Schigg e E-Mail aa {{GENDER:$1|de Benutzer|die Benutzeri}}",
        "tooltip-t-info": "Meh Informationen über die Syte",
        "tooltip-t-upload": "Dateien ufelade",
        "tooltip-t-specialpages": "Lischte vo allne Spezialsyte",
        "version-libraries-license": "Lizänz",
        "version-libraries-description": "Beschrybig",
        "version-libraries-authors": "Autor/inne",
-       "redirect": "Wyterleitig uf Benutzersyte, Syte, Syteversion oder Datei",
-       "redirect-summary": "Die Spezialsyte leitet wyter uf e Benutzersyte (numerischi Benutzerkännig aagee), Syte (Sytekännig aagee), Syteversion (Versionskännig aagee) oder Datei (Dateiname aagee). Benutzig: [[{{#Special:Redirect}}/user/101]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]] oder [[{{#Special:Redirect}}/file/Example.jpg]].",
+       "redirect": "Wyterleitig uf Datei, Benutzersyte, Syte, Syteversion oder Logbuechyytraag.",
+       "redirect-summary": "Die Spezialsyte leitet wyter uf e Benutzersyte (numerischi Benutzerkännig aagee), Syte (Sytekännig aagee), Syteversion (Versionskännig aagee), e Datei (Dateiname aagee) oder en Logbeuchyytrag (Logbuechkennig aagee). Benutzig: Usage: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]], oder [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Gang",
        "redirect-lookup": "Sueche:",
        "redirect-value": "Wärt:",
        "fileduplicatesearch-noresults": "S isch kei Datei mit em Name „$1“ gfunde wore.",
        "specialpages": "Spezialsytene",
        "specialpages-note-top": "Zeichenerklärig:",
-       "specialpages-note": "* Normali Spezialsyte.\n* <span class=\"mw-specialpagerestricted\">Spezialsyte mit bschränktem Zuegang.</span>",
        "specialpages-group-maintenance": "Wartigslischte",
        "specialpages-group-other": "Andri Spezialsyte",
        "specialpages-group-login": "Aamälde/Konto aalege",
        "htmlform-user-not-exists": "<strong>$1</strong> git’s nid.",
        "htmlform-user-not-valid": "<strong>$1</strong> isch ke gültige Name.",
        "logentry-delete-delete": "{{GENDER:$2|Dr|D|}} $1 het d Syte $3 glöscht",
-       "logentry-delete-restore": "{{GENDER:$2|Der $1|D $1|$1}} het d Syte $3 wider härgstellt",
+       "logentry-delete-restore": "{{GENDER:$2|Der $1|D $1|$1}} het d Syte $3 wider härgstellt ($4)",
        "logentry-delete-event": "{{GENDER:$2|Der $1|D $1|$1}} het d Sichtbarkeit {{PLURAL:$5|vumene Logbuechyytrag|vo $5 Logbuechyyträg}} gänderet uff $3: $4",
        "logentry-delete-revision": "{{GENDER:$2|Der $1|D $1|$1}} het d Sichtbarkeit {{PLURAL:$5|vunere Version|vo $5 Versione}} gänderet uff $3: $4",
        "logentry-delete-event-legacy": "{{GENDER:$2|Der $1|D $1|$1}} het d Sichtbarkeit vo Logbuechyyträg uff $3 gänderet",
index fe96f31..e5f3045 100644 (file)
        "showdiff": "ફેરફારો દર્શાવો",
        "anoneditwarning": "<strong>ચેતવણી:</strong> તમે તમારા સભ્ય નામથી પ્રવેશ કર્યો નથી.\nઆ પાનાનાં ઇતિહાસમાં તમારૂં આઇ.પી. (IP) એડ્રેસ નોંધવામાં આવશે અને તમારૂં આઈ.પી. લોકો જાહેર રીતે જોઈ શકશે. માટે <strong>[$1 પ્રવેશ કરો]</strong> અથવા તમે <strong>[$2 ખાતું બનાવો]</strong> તો ફેરફારો તમારા સભ્યનામ હેઠળ થશે અને અન્ય ફાયદાઓ પણ મળશે.",
        "anonpreviewwarning": "તમે સભ્યનામથી પ્રવેશ કર્યો નથી,આ પાનું ઈતિહાસમાંતમારા IP સરનામાના નામે  સાચવવામાં આવશે",
-       "missingsummary": "'''ચેતવણી:''' તમે ફેરફારનો સારંશ નથી આપ્યો.\nજો તમે \"$1\"  પર ક્લીક કરશો તો તમરો ફેરફારા સારાઁશાવગરાસાચવવામાં આવશે",
+       "missingsummary": "<strong>ચેતવણી:</strong> તમે ફેરફારોનો સારાંશ આપ્યો\nજો તમે \"$1\"  પર ફરી ક્લિક કરશો તો તમારા ફેરફારો સાચવવામાં આવશે.",
        "missingcommenttext": "કૃપા કરી નીચે ટીપ્પણી લખો.",
        "missingcommentheader": "'''યાદ દેવડાવું:'''તમે તમારી ટિપ્પણી ને શીર્ષક/મથાળુ આપ્યું નથી. \nજો તમે  \"$1\" પર ફરીથી ક્લિક કરશો, તો તમારા ફેરરારો મથાળા વગર સચવાશે.",
        "summary-preview": "સંપાદન સારાંશ પૂર્વાવલોકન:",
        "fileduplicatesearch-result-n": "\"$1\" ફાઇલની તેની સમાન {{PLURAL:$2|1 નકલ |$2 નકલો }} છે.",
        "fileduplicatesearch-noresults": " \"$1\" નામ ધરાવતી કોઇ ફાઇલ ન મળી",
        "specialpages": "ખાસ પાનાંઓ",
-       "specialpages-note": "* નિયમિત ખાસ પાનાં.\n* <span class=\"mw-specialpagerestricted\">પ્રતિબંધિત ખાસ પાનાં.</span>",
        "specialpages-group-maintenance": "સમારકામ અહેવાલ",
        "specialpages-group-other": "અન્ય ખાસ પાનાઓ",
        "specialpages-group-login": "પ્રવેશ / ખાતુ બનાવો",
index d9472a2..a60f65b 100644 (file)
        "fileduplicatesearch-noresults": "לא נמצא קובץ בשם \"$1\".",
        "specialpages": "דפים מיוחדים",
        "specialpages-note-top": "מקרא",
-       "specialpages-note": "* דפים מיוחדים רגילים.\n* <span class=\"mw-specialpagerestricted\">דפים מיוחדים מוגבלים.</span>",
+       "specialpages-note-restricted": "* דפים מיוחדים רגילים.\n* <span class=\"mw-specialpagerestricted\">דפים מיוחדים מוגבלים.</span>",
        "specialpages-group-maintenance": "דיווחי תחזוקה",
        "specialpages-group-other": "דפים מיוחדים אחרים",
        "specialpages-group-login": "כניסה לחשבון / הרשמה",
index b48188e..5f2ba29 100644 (file)
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|नए पन्नों की सूची]] को भी देखें)",
        "recentchanges-submit": "दिखाएँ",
        "rcfilters-activefilters": "सक्रिय फिल्टर",
+       "rcfilters-limit-shownum": "पिछले $1 बदलाव दिखायें",
+       "rcfilters-days-title": "कुछ दिनों के",
+       "rcfilters-hours-title": "कुछ घंटों के",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|दिन}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|घंटा|घंटे}}",
        "rcfilters-quickfilters": "सुरक्षित फ़िल्टर",
        "rcfilters-quickfilters-placeholder-title": "कोई कड़ी अभी तक सहेजा नहीं गया",
        "rcfilters-quickfilters-placeholder-description": "अपने फ़िल्टर सेटिंग को सहेजने और बाद में उपयोग करने के लिए नीचे दिये बूकमार्क छवि पर क्लिक करें।",
        "rcfilters-savedqueries-unsetdefault": "मूल के रूप से हटाएँ",
        "rcfilters-savedqueries-remove": "निकालें",
        "rcfilters-savedqueries-new-name-label": "नाम",
-       "rcfilters-savedqueries-apply-label": "सेटिंग संजोयें",
+       "rcfilters-savedqueries-new-name-placeholder": "फ़िल्टर का उद्देश्य समझाएँ",
+       "rcfilters-savedqueries-apply-label": "फ़िल्टर बनायें",
        "rcfilters-savedqueries-cancel-label": "रद्द करें",
        "rcfilters-savedqueries-add-new-title": "वर्तमान फ़िल्टर सेटिंग को सहेजें",
        "rcfilters-restore-default-filters": "मूलभूत फिल्टर पुनर्स्थापित करे",
        "rcfilters-invalid-filter": "अमान्य फ़िल्टर",
        "rcfilters-empty-filter": "कोई सक्रिय फिल्टर नहीं। सभी योगदान दिखाए गए है।",
        "rcfilters-filterlist-title": "फिल्टर",
-       "rcfilters-filterlist-whatsthis": "यह à¤\95à¥\8dया है?",
+       "rcfilters-filterlist-whatsthis": "यह à¤\95à¥\88सà¥\87 à¤\95ारà¥\8dय à¤\95रता है?",
        "rcfilters-filterlist-feedbacklink": "नए (बीटा) फिल्टर पर प्रतिक्रिया दें",
        "rcfilters-highlightbutton-title": "Highlight results",
        "rcfilters-highlightmenu-title": "रंग चुनें",
        "rcfilters-filter-editsbyother-label": "दूसरों के द्वारा बदलाव",
        "rcfilters-filter-editsbyother-description": "आपके बदलावों को छोड़ कर सभी के बदलाव।",
        "rcfilters-filtergroup-userExpLevel": "अनुभव स्तर (केवल पंजीकृत सदस्यों के लिए)",
-       "rcfilters-filter-user-experience-level-registered-label": "पंजीकृत:",
+       "rcfilters-filter-user-experience-level-registered-label": "पंजीकृत",
        "rcfilters-filter-user-experience-level-registered-description": "लॉग-इन संपादक।",
        "rcfilters-filter-user-experience-level-unregistered-label": "अपंजीकृत",
        "rcfilters-filter-user-experience-level-unregistered-description": "संपादक जो लॉग इन नहीं हैं।",
        "rcfilters-hideminor-conflicts-typeofchange-global": "\"लघु संपादन\" फ़िल्टर एक या एक से अधिक प्रकार के परिवर्तन फ़िल्टर के साथ संघर्ष करता है, क्योंकि कुछ प्रकार के परिवर्तन को \"लघु\" के रूप में निर्दिष्ट नहीं किया जा सकता है। परस्पर विरोधी फिल्टर ऊपर सक्रिय फिल्टर क्षेत्र में चिह्नित हैं।",
        "rcfilters-hideminor-conflicts-typeofchange": "कुछ प्रकार के परिवर्तन को \"लघु\" के रूप में निर्दिष्ट नहीं किया जा सकता है\", इसलिए यह फ़िल्टर निम्न प्रकार के परिवर्तन फिल्टर के साथ संघर्ष करता है: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "इस प्रकार का परिवर्तन फ़िल्टर \"लघु संपादन\" फ़िल्टर के साथ संघर्ष करता है। कुछ प्रकार के परिवर्तन को \"लघु\" के रूप में निर्दिष्ट नहीं किया जा सकता है।",
-       "rcfilters-filtergroup-lastRevision": "सदà¥\8dय अवतरण",
-       "rcfilters-filter-lastrevision-label": "à¤\85à¤\82तिम अवतरण",
+       "rcfilters-filtergroup-lastRevision": "नया अवतरण",
+       "rcfilters-filter-lastrevision-label": "नया अवतरण",
        "rcfilters-filter-lastrevision-description": "पृष्ठ का सबसे हाल में हुआ बदलाव",
        "rcfilters-filter-previousrevision-label": "पहले के अवतरण",
        "rcfilters-filter-previousrevision-description": "सभी परिवर्तन जो एक पृष्ठ में सबसे हाल के परिवर्तन नहीं हैं।",
        "rcfilters-filter-excluded": "अपवर्जित",
        "rcfilters-tag-prefix-namespace-inverted": " $1 <strong>:नहीं</strong>",
-       "rcfilters-view-tags": "à¤\9aिपà¥\8dपियाà¤\81",
+       "rcfilters-view-tags": "à¤\9fà¥\88à¤\97 à¤µà¤¾à¤²à¥\87 à¤¸à¤®à¥\8dपादन",
        "rcnotefrom": "नीचे <strong>$2</strong> के बाद से (<strong>$1</strong> तक) {{PLURAL:$5|हुआ बदलाव दर्शाया गया है|हुए बदलाव दर्शाए गये हैं}}।",
        "rclistfromreset": "चुने दिनांक पहले जैसा करें",
        "rclistfrom": "$3 $2 से नये बदलाव दिखाएँ",
        "fileduplicatesearch-noresults": "कोई फ़ाइल नाम \"$1\" मिला नहीं ।",
        "specialpages": "विशेष पृष्ठ",
        "specialpages-note-top": "कुंजी",
-       "specialpages-note": "* साधारण विशेष पृष्ठ।\n* <span class=\"mw-specialpagerestricted\">प्रतिबंधित विशेष पृष्ठ।</span>",
        "specialpages-group-maintenance": "अनुरक्षण रिपोर्ट",
        "specialpages-group-other": "अन्य विशेष पृष्ठ",
        "specialpages-group-login": "सत्र आरम्भ / खाता खोलें",
index 71d2828..0362e77 100644 (file)
        "fileduplicatesearch-noresults": "Nije pronađena datoteka s imenom \"$1\".",
        "specialpages": "Posebne stranice",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Normalne posebne stranice\n* <span class=\"mw-specialpagerestricted\">Posebne stranice s ograničenim pristupom.</span>",
        "specialpages-group-maintenance": "Izvješća održavanja",
        "specialpages-group-other": "Ostale posebne stranice",
        "specialpages-group-login": "Prijava/otvaranje računa",
index a6c70ed..0c75f3a 100644 (file)
        "rcfilters-legend-heading": "<strong>Rövidítések listája:</strong>",
        "rcfilters-activefilters": "Aktív szűrők",
        "rcfilters-advancedfilters": "Haladó szűrők",
+       "rcfilters-limit-title": "Megjelenítendő változtatások",
+       "rcfilters-limit-shownum": "Utolsó $1 változtatás megjelenítése",
+       "rcfilters-days-show-days": "$1 nap",
+       "rcfilters-days-show-hours": "$1 óra",
        "rcfilters-quickfilters": "Mentett szűrők",
        "rcfilters-quickfilters-placeholder-title": "Nincs mentett hivatkozás",
        "rcfilters-savedqueries-defaultlabel": "Mentett szűrők",
        "undelete-search-title": "Törölt lapok keresése",
        "undelete-search-box": "Törölt lapok keresése",
        "undelete-search-prefix": "A megadott szavakkal kezdődő oldalak megjelenítése:",
+       "undelete-search-full": "A következőt tartalmazó címek keresése:",
        "undelete-search-submit": "Keresés",
        "undelete-no-results": "Nem található a keresési feltételeknek megfelelő oldal a törlési naplóban.",
        "undelete-filename-mismatch": "Nem állítható helyre a(z) $1 időbélyeggel ellátott változat: a fájlnév nem egyezik meg",
        "fileduplicatesearch-noresults": "Nincs „$1” nevű fájl.",
        "specialpages": "Speciális lapok",
        "specialpages-note-top": "Jelmagyarázat",
-       "specialpages-note": "* Mindenki számára elérhető speciális lapok.\n* <span class=\"mw-specialpagerestricted\">Korlátozott hozzáférésű speciális lapok.</span>",
        "specialpages-group-maintenance": "Állapotjelentések",
        "specialpages-group-other": "További speciális lapok",
        "specialpages-group-login": "Bejelentkezés / fiók létrehozása",
index 1ef5576..bd41893 100644 (file)
        "fileduplicatesearch-result-n": "$1 նիշքն ունի {{PLURAL:$2|1 նույնական կրկնօրինակ|$2 նույնական կրկնօրինակ}}.",
        "fileduplicatesearch-noresults": "$1 անունով նիշք չի գտնվել",
        "specialpages": "Սպասարկող էջեր",
-       "specialpages-note": "* Հասարակ հատուկ էջեր։\n* <span class=\"mw-specialpagerestricted\">Սահմանափակված հատուկ էջեր։</span>",
        "specialpages-group-maintenance": "Տեխնիկական սպասարկման տեղեկատուներ",
        "specialpages-group-other": "Այլ հատուկ էջեր",
        "specialpages-group-login": "Մտնել / Գրանցվել",
index 37b4749..1e87766 100644 (file)
        "badtitle": "Мегаш йоаца цӀи",
        "badtitletext": "Езаш йола оагӀон цӀи нийса яц, яьсса я, е харцахь йоалаяй меттий юкъера цIи е интервики цӀи. Иштта, цӀера юкъе оттаде мегаш доаца хьаракаш нийсаденна хила мегаш да.",
        "viewsource": "Хьажар",
+       "viewsource-title": "Оагӏон $1 духхьара текстага хьажар",
        "actionthrottled": "Сухалах доазув дар",
        "protectedpagetext": "Ер оагIув лораяь я цу тIа хувцамаш е кхы дола ардамаш дергдоацаш.",
        "virus-unknownscanner": "йовзанза антивирус:",
        "newarticletext": "Шо тIахьожаяргаца дехьадаьннад йоаца оагӀон тӀа.\nИз хьакхолларгьйолаш кӀалхагӀа доала корачу текст Iочуязде (нагахьа санна кхетаде хала дале [$1 новкъосталара оагӀонга] хьажа).\nЦа ховш укхаза нийсденнадале, шоай браузера '''Юха''' (назад) тоIаер тӀа пӀелг тоӀабе.",
        "noarticletext": "ХIанза укх оагӀон тӀа текст яц.\nШун аьттув ба [[Special:Search/{{PAGENAME}}|цу тайпара цӀи хьоаяр кораде]] кхыйола оагIонаш тIа, иштта\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} тара дола тептарий дIаяздаьраш], е\n'''[{{fullurl:{{FULLPAGENAME}}|action=edit}} изза мо цӀи йолаш оагӀув хьакхолла]'''</span>.",
        "noarticletext-nopermission": "ХIанз укх оагӀон тӀа текст яц.\nШун аьттув ба [[Special:Search/{{PAGENAME}}|цу тайпара цӀи белгалъяр хьалаха]] кхыйола оагIонаш тIа, иштта\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} тара дола тептарай дIаяздаьраш].</span> Ер оагӀув хьакхолла Хьа бокъо яц.",
+       "userpage-userdoesnotexist-view": "«$1» яха дагара йоазув дац.",
        "note": "'''Белгалдоахар:'''",
        "previewnote": "'''Теркам бе, ер хьалххе бIаргтохар мара бац.'''\nХьа хувцамаш хIанза а дIаяздаь дац!",
        "continue-editing": "Хувцар кхы дIахо де",
        "rcshowhidebots-show": "Хьахьокха",
        "rcshowhidebots-hide": "Къайладаккха",
        "rcshowhideliu": "$1 бовзийтарчара доакъашхой",
+       "rcshowhideliu-show": "Хьахьокха",
        "rcshowhideliu-hide": "Къайлабаха",
        "rcshowhideanons": "$1 цIияккханза доакъашхой",
        "rcshowhideanons-show": "Хьахьокха",
        "htmlform-selectorother-other": "Кхыдар",
        "logentry-delete-delete": "$1 {{GENDER:$2|дIаяккхай}} оагIув $3",
        "logentry-move-move": "$1 {{GENDER:$2|цӀи хийцай}} $3 → $4",
+       "logentry-move-move-noredirect": "$1 {{GENDER:$2|цӀи хийцай}} $3 → $4 дӀа-сахьожадар ца дуташ.",
        "logentry-newusers-create": "{{GENDER:$2|Доакъашхочо хьакхеллад}} дагара йоазув $1",
        "logentry-upload-upload": "$1 {{GENDER:$2|чуяьккхай}} $3",
        "rightsnone": "(яц)",
index 0184302..08a9911 100644 (file)
        "template-loop-category-desc": "La pagina contiene un template che richiama sé stesso, cioè un template in cui è incluso lo stesso template.",
        "parser-template-recursion-depth-warning": "È stato raggiunto il limite di ricorsione nel template ($1)",
        "language-converter-depth-warning": "Limite di profondità del convertitore di lingua superato ($1)",
-       "node-count-exceeded-category": "Pagine dove viene superato il numero di nodi",
+       "node-count-exceeded-category": "Pagine che superano il numero di nodi",
        "node-count-exceeded-category-desc": "La pagina supera il numero massimo di nodi.",
        "node-count-exceeded-warning": "Questa pagina ha superato il numero di nodi",
        "expansion-depth-exceeded-category": "Pagine nelle quali è superata la profondità di espansione",
        "revdelete-text-file": "Le versioni di file cancellati appariranno ancora nella cronologia del file, ma parti del loro contenuto sarà inaccessibile al pubblico.",
        "logdelete-text": "Gli eventi cancellati appariranno ancora nei registri, ma parti del loro contenuto sarà inaccessibile al pubblico.",
        "revdelete-text-others": "Altri amministratori saranno ancora in grado di accedere ai contenuti nascosti e potranno ripristinarli, se non sono state impostate restrizioni aggiuntive.",
-       "revdelete-confirm": "Per favore conferma che questo è quanto intendi fare, che sei consapevole delle conseguenze, e che stai facendo questo nel rispetto delle [[{{MediaWiki:Policy-url}}|linee guida]].",
+       "revdelete-confirm": "Per favore conferma che questo è quanto intendi fare, che sei consapevole delle conseguenze, e che stai facendo ciò nel rispetto delle [[{{MediaWiki:Policy-url}}|linee guida]].",
        "revdelete-suppress-text": "La rimozione dovrebbe essere utilizzata '''unicamente''' nei seguenti casi:\n* informazioni potenzialmente diffamatorie\n* dati personali inopportuni\n*: ''indirizzi, numeri di telefono, codici fiscali, ecc.''",
        "revdelete-legend": "Imposta le seguenti limitazioni sulle versioni cancellate:",
        "revdelete-hide-text": "Testo della versione",
        "yourlanguage": "Lingua dell'interfaccia:",
        "yourvariant": "Variante della lingua:",
        "prefs-help-variant": "La variante o grafia in cui preferisci che le pagine del wiki ti siano mostrate.",
-       "yournick": "Soprannome (nickname):",
+       "yournick": "Nuova firma:",
        "prefs-help-signature": "I commenti nelle pagine di discussione devono essere firmati con \"<nowiki>~~~~</nowiki>\" che verrà convertito nella propria firma seguita dalla data.",
        "badsig": "Errore nella firma non standard, verificare i tag HTML.",
        "badsiglength": "La firma scelta è troppo lunga, non deve superare $1 {{PLURAL:$1|carattere|caratteri}}.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "Il filtro \"Modifiche minori\" è in confitto con uno o più dei filtri \"Tipo di modifica\", perché certe modifiche non possono essere indicate come \"minori\". I filtri in conflitto sono indicati nell'area \"Filtri attivi\" qui sopra.",
        "rcfilters-hideminor-conflicts-typeofchange": "Alcuni tipi di modifiche non possono essere indicate come \"minori\", quindi questo filtro è in conflitto con i seguenti filtri \"Tipo di modifica\": $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Questo filtro \"Tipo di modifica\" è in conflitto con il filtro \"Modifiche minori\". Alcuni tipi di modifiche non possono essere indicati come \"minori\".",
-       "rcfilters-filtergroup-lastRevision": "Ultima versione",
-       "rcfilters-filter-lastrevision-label": "Ultima versione",
-       "rcfilters-filter-lastrevision-description": "Le ultime modifiche ad una pagina.",
-       "rcfilters-filter-previousrevision-label": "Versioni precedenti",
-       "rcfilters-filter-previousrevision-description": "Tutte le modifiche che non sono l'ultima modifica effettuata sulla voce.",
+       "rcfilters-filtergroup-lastRevision": "Ultime versioni",
+       "rcfilters-filter-lastrevision-label": "Versione attuale",
+       "rcfilters-filter-lastrevision-description": "Solo l'ultima modifica ad una pagina.",
+       "rcfilters-filter-previousrevision-label": "Non l'ultima versione",
+       "rcfilters-filter-previousrevision-description": "Tutte le modifiche che non sono la \"versione attuale\".",
        "rcfilters-filter-excluded": "Escluso",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:non</strong> $1",
        "rcfilters-exclude-button-off": "Escludi selezionato",
        "rcfilters-view-tags": "Modifiche etichettate",
        "rcnotefrom": "Di seguito {{PLURAL:$5|è elencata la modifica apportata|sono elencate le modifiche apportate}} a partire da <strong>$3, $4</strong> (mostrate fino a <strong>$1</strong>).",
        "rclistfromreset": "Reimposta la selezione della data",
-       "rclistfrom": "Mostra le modifiche apportate a partire da $3 $2",
+       "rclistfrom": "Mostra le nuove modifiche a partire daː $2, $3",
        "rcshowhideminor": "$1 le modifiche minori",
        "rcshowhideminor-show": "Mostra",
        "rcshowhideminor-hide": "Nascondi",
        "undeletepage": "Visualizza e recupera le pagine cancellate",
        "undeletepagetitle": "'''Quanto segue è composto da versioni cancellate di [[:$1|$1]]'''.",
        "viewdeletedpage": "Visualizza le pagine cancellate",
-       "undeletepagetext": "{{PLURAL:$1|La pagina indicata di seguito è stata cancellata, ma è ancora in archivio e pertanto può essere recuperata|Le pagine indicate di seguito sono state cancellate, ma sono ancora in archivio e pertanto possono essere recuperate}}. L'archivio può essere svuotato periodicamente.",
+       "undeletepagetext": "{{PLURAL:$1|La pagina indicata di seguito è stata cancellata, ma è ancora in archivio e pertanto può essere ripristinata|Le pagine indicate di seguito sono state cancellate, ma sono ancora in archivio e pertanto possono essere ripristinate}}. L'archivio può essere svuotato periodicamente.",
        "undelete-fieldset-title": "Ripristina versioni",
        "undeleteextrahelp": "Per recuperare l'intera cronologia della pagina, lasciare tutte le caselle deselezionate e fare clic su '''''{{int:undeletebtn}}'''''.\nPer effettuare un ripristino selettivo, selezionare le caselle corrispondenti alle versioni da ripristinare e fare clic su '''''{{int:undeletebtn}}'''''.",
        "undeleterevisions": "{{PLURAL:$1|Una versione cancellata|$1 versioni cancellate}}",
-       "undeletehistory": "Recuperando questa pagina, tutte le sue versioni saranno ripristinate nella relativa cronologia.\nSe dopo la cancellazione è stata creata una nuova pagina con lo stesso titolo, le versioni recuperate saranno inserite nella cronologia precedente.",
+       "undeletehistory": "Ripristinando questa pagina, tutte le sue versioni saranno recuperate nella relativa cronologia.\nSe dopo la cancellazione è stata creata una nuova pagina con lo stesso titolo, le versioni recuperate saranno inserite nella cronologia precedente.",
        "undeleterevdel": "Il ripristino non verrà effettuato se determina la cancellazione parziale della versione attuale della pagina o del file interessato. In tal caso, è necessario rimuovere il segno di spunta o l'oscuramento dalle versioni cancellate più recenti.",
        "undeletehistorynoadmin": "Questa pagina è stata cancellata.\nIl motivo della cancellazione è mostrato qui sotto, assieme ai dettagli dell'utente che ha modificato questa pagina prima della cancellazione.\nIl testo contenuto nelle versioni cancellate è disponibile solo agli amministratori.",
        "undelete-revision": "Versione cancellata della pagina $1, inserita il $4 alle $5 da $3:",
        "undeleteinvert": "Inverti selezione",
        "undeletecomment": "Motivo:",
        "cannotundelete": "Alcuni o tutti i ripristini non riusciti:\n$1",
-       "undeletedpage": "'''La pagina $1 è stata recuperata'''\n\nConsulta il [[Special:Log/delete|registro delle cancellazioni]] per vedere le cancellazioni e i recuperi più recenti.",
+       "undeletedpage": "'''La pagina $1 è stata ripristinata'''\n\nConsulta il [[Special:Log/delete|registro delle cancellazioni]] per vedere le cancellazioni e i ripristini più recenti.",
        "undelete-header": "Consulta il [[Special:Log/delete|registro delle cancellazioni]] per vedere le cancellazioni più recenti.",
        "undelete-search-title": "Ricerca nelle pagine cancellate",
        "undelete-search-box": "Ricerca le pagine cancellate",
        "exif-personinimage": "Persona raffigurata",
        "exif-originalimageheight": "Altezza dell'immagine prima che fosse ritagliata",
        "exif-originalimagewidth": "Larghezza dell'immagine prima che fosse ritagliata",
-       "exif-compression-1": "Nessuno",
+       "exif-compression-1": "Senza compressione",
        "exif-compression-2": "CCITT gruppo 3 monodimensionale - codifica run length di Huffman modificata",
        "exif-compression-3": "Codifica fax CCITT Group 3",
        "exif-compression-4": "Codifica fax CCITT gruppo 4",
        "fileduplicatesearch-noresults": "Nessun file di nome \"$1\" trovato.",
        "specialpages": "Pagine speciali",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Pagine speciali non riservate.\n* <span class=\"mw-specialpagerestricted\">Pagine speciali riservate ad alcune categorie di utenti.</span>",
        "specialpages-group-maintenance": "Resoconti di manutenzione",
        "specialpages-group-other": "Altre pagine speciali",
        "specialpages-group-login": "Accesso / creazione utenze",
        "feedback-back": "Indietro",
        "feedback-bugcheck": "Ottimo! Verifica che non sia già fra i [$1 bug conosciuti].",
        "feedback-bugnew": "Controllo effettuato. Segnala un nuovo bug",
-       "feedback-bugornote": "Se si è in grado di descrivere il problema tecnico riscontrato in maniera precisa, [$1 segnalate il bug]. In alternativa, si può usare il modulo semplificato sottostante. Il commento inserito sarà aggiunto alla pagina \"[$3 $2]\", insieme al proprio nome utente e al browser in uso.",
+       "feedback-bugornote": "Se sei in grado di descrivere il problema tecnico riscontrato in maniera precisa, [$1 segnala il bug]. In alternativa, si può usare il modulo semplificato sottostante. Il commento inserito sarà aggiunto alla pagina \"[$3 $2]\", insieme al proprio nome utente.",
        "feedback-cancel": "Annulla",
        "feedback-close": "Fatto",
        "feedback-external-bug-report-button": "Documenta un problema tecnico",
        "limitreport-templateargumentsize-value": "$1/$2 {{PLURAL:$2|byte}}",
        "limitreport-expansiondepth": "Massima profondità di espansione",
        "limitreport-expensivefunctioncount": "Numero funzioni parser dispendiose",
-       "expandtemplates": "Espansione dei template",
+       "expandtemplates": "Espandi i template",
        "expand_templates_intro": "Questa pagina speciale elabora un testo espandendo tutti i template presenti.\nCalcola inoltre il risultato delle funzioni supportate dal parser come\n<code><nowiki>{{</nowiki>#language:…}}</code> e delle variabili di sistema quali\n<code><nowiki>{{</nowiki>CURRENTDAY}}</code>,\nvale a dire praticamente tutto ciò che si trova tra doppie parentesi graffe.",
        "expand_templates_title": "Contesto (per {{FULLPAGENAME}} ecc.):",
        "expand_templates_input": "Testo da espandere:",
        "expand_templates_xml_output": "Output in formato XML",
        "expand_templates_html_output": "Risultato HTML",
        "expand_templates_ok": "OK",
-       "expand_templates_remove_comments": "Ignora i commenti",
+       "expand_templates_remove_comments": "Rimuovi i commenti",
        "expand_templates_remove_nowiki": "Elimina il tag <nowiki> nel risultato",
        "expand_templates_generate_xml": "Mostra albero sintattico XML",
        "expand_templates_generate_rawhtml": "Mostra HTML",
index af33996..023ca90 100644 (file)
        "searchbutton": "Golèk",
        "go": "Menyang",
        "searcharticle": "Menyang",
-       "history": "Sujarah kaca",
-       "history_short": "Sujarah",
-       "history_small": "sujarah",
+       "history": "Sajarah kaca",
+       "history_short": "Sajarah",
+       "history_small": "sajarah",
        "updatedmarker": "wis dianyari kawit tekaku mréné pungkasan",
        "printableversion": "Vèrsi cap-capan",
        "permalink": "Pranala permanèn",
        "showpreview": "Deleng pratuduh",
        "showdiff": "Tuduhaké owahan",
        "anoneditwarning": "<strong>Pènget:</strong> Panjenengan durung mlebu log. Alamat IP-né panjenengan bakal katon marang wong akèh manawa panjenengan mbesut. Manawa panjenengan <strong>[$1 mlebu log]</strong> utawa <strong>[$2 nggawé akun]</strong>, besutané panjenengan bakal dadi darbéné naragunané panjenengan lan uga ana kauntungan liya.",
-       "anonpreviewwarning": "<em>Panjenengan durung mlebu log. Yèn disimpen, alamat IP panjenengan bakal kacathet ing sujarah besutan kaca iki.</em>",
+       "anonpreviewwarning": "<em>Panjenengan durung mlebu log. Yèn disimpen, alamat IP panjenengan bakal kacathet ing sajarah besutan kaca iki.</em>",
        "missingsummary": "<strong>Pangéling-éling:</strong> Panjenengan ora ngisèni ringkesané besutan.\nManawa panjenengan mencèt \"$1\" manèh, besutané panjengan bakal kasimpen tanpa katerangan.",
        "selfredirect": "<strong>Pélik:</strong> Sampéyan ngalih kaca iki iya nyang kaca iki dhéwé.\nSampéyan mungkin salah wènèh tujuan kanggo alihan utawa salah mbesut kaca.\nYèn sampéyan ngeklik \"$1\" manèh, kaca alihan bakal digawé.",
        "missingcommenttext": "Mangga isi tanggepan ing ngisor iki.",
        "anontalkpagetext": "----\n<em>Iki kaca parembugané panganggo anonim sing durung gawé akun, utawa sing ora nganggo akuné.</em>\nMula, awak dhéwé kudu nganggo alamat IP awujud angka kanggo nglacak dhèwèké.\nAlamat IP mangkono bisa dianggo déning sawenèh panganggo.\nManawa panjenengan panganggo anonim lan rumasa yèn ana tanggepan sing ora ilok dieneraké marang panjenengan, mangga [[Special:CreateAccount|gawéa akun]] utawa [[Special:UserLogin|mlebua log]] kanggo ngéndhani salah pangira karo panganggo anonim liyané ing tembé buri.",
        "noarticletext": "Kala saiki kaca iki durung ana tulisané.\nSampéyan bisa [[Special:Search/{{PAGENAME}}|nggolèki sesirahing kaca iki]] sajeroning kaca liya,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} nggolèki log sing magepokan],\nutawa [{{fullurl:{{FULLPAGENAME}}|action=edit}} nggawé kaca iki]</span>.",
        "noarticletext-nopermission": "Saiki lagi ora ana tèks ing kaca iki. \nPanjenengan bisa [[Special:Search/{{PAGENAME}}|nggolèk sesirah kaca iki]] ing kaca liyané, \nutawa <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{urlencode:{{FULLPAGENAME}}}}}} nggolèk ing log sing gegayutan]</span>, nanging panjenengan ora kawogan nggawé kaca iki.",
-       "missing-revision": "Révisi #$1 saka kaca ajeneng \"{{FULLPAGENAME}}\" ora ana.\n\nIki biyasané kasababaké awit nututi pranala sujarah sing wis lawas saka sawijiné kaca sing wis dibusak.\nRerincèné bisa digolèki ing [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} log busak].",
+       "missing-revision": "Révisi #$1 saka kaca ajeneng \"{{FULLPAGENAME}}\" ora ana.\n\nIki biyasané kasababaké awit nututi pranala sajarah sing wis lawas saka sawijiné kaca sing wis dibusak.\nRerincèné bisa digolèki ing [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} log busak].",
        "userpage-userdoesnotexist": "Akun panganggo \"$1\" ora kadhaftar.\nMangga pesthèkaké dhisik yèn panjenengan péngin nggawé/mbesut kaca iki.",
        "userpage-userdoesnotexist-view": "Akun panganggo \"$1\" ora kadhaftar.",
        "blocked-notice-logextract": "Panganggo iki saiki lagi diblokir.\nLog pamblokiran pungkasan dituduhaké ing ngisor iki minangka bahan rujukan:",
        "histlast": "anyar dhéwé",
        "historysize": "($1 {{PLURAL:$1|bét|bét}})",
        "historyempty": "(suwung)",
-       "history-feed-title": "Sujarah owahan",
-       "history-feed-description": "Sujarah owahaning kaca iki ing wiki",
+       "history-feed-title": "Sajarah owahan",
+       "history-feed-description": "Sajarah owahaning kaca iki ing wiki",
        "history-feed-item-nocomment": "$1 ing $2",
        "history-feed-empty": "Kaca sing disuwun ora ditemokaké. Mbokmenawa wis dibusak saka wiki, utawa diwènèhi jeneng anyar. Coba [[Special:Search|golèka ing wiki]] kanggo kaca anyar sing rélevan.",
        "rev-deleted-comment": "(tingkesaning besutan dibusak)",
        "logdelete-failure": "'''Aturan pandhelikan ora bisa disèt:'''\n$1",
        "revdel-restore": "Ngowahi visiblitas (pangatonan)",
        "pagehist": "Babading kaca",
-       "deletedhist": "Sujarah kabusak",
+       "deletedhist": "Sajarah kabusak",
        "revdelete-hide-current": "Gagal ndhelikaké révisi tanggal $2, $1: iki arupa révisi paling anyar.\nRévisi iki ora bisa didhelikaké.",
        "revdelete-show-no-access": "Gagal nampilaké révisi tanggal $1, jam $2: révisi iki wis ditandhani \"kawates\".\nPanjenengan ora nduwèni aksès menyang révisi iki.",
        "revdelete-modify-no-access": "Gagal ngowahi révisi tanggal $1, jam $2: révisi iki wis ditandhani \"kawates\".\nPanjenengan ora nduwèni aksès menyang révisi iki.",
        "revdelete-offender": "Juru pangriptaning owahan:",
        "suppressionlog": "Log barang-barang sing didelikaké (''oversight'')",
        "suppressionlogtext": "Ngisor iki daptar apa-apa waé sing wis dibusak lan diblokir kalebu kontèn sing didhelikaké saka para pangurus.\nDelok [[Special:BlockList|daptar blokiran]] sing isiné daptar apa-apa waé sing lagi dilarang lan diblokir.",
-       "mergehistory": "Gabung sujarah kaca",
+       "mergehistory": "Gabung sajarah kaca",
        "mergehistory-header": "Ing kaca iki panjenengan bisa nggabung révisi-révisi sajarah saka sawijining kaca sumber menyang kaca anyar.\nPastèkna yèn owah-owahan iki bakal netepaké kasinambungan sajarah kaca.",
        "mergehistory-box": "Gabungna revisi-revisi saka rong kaca:",
        "mergehistory-from": "Kaca sumber:",
        "mergehistory-into": "Kaca paran:",
-       "mergehistory-list": "Sujarah besutan sing bisa digabung",
+       "mergehistory-list": "Sajarah besutan sing bisa digabung",
        "mergehistory-merge": "Révisi-révisi sing kapacak ing ngisor iki saka [[:$1]] bisa digabungaké menyang [[:$2]].\nGunakna tombol radio kanggo nggabungaké révisi-révisi sing digawé sadurungé wektu tartamtu. Gatèkna, menawa nganggo pranala navigasi bakal ngesèt ulang kolom iki.",
        "mergehistory-go": "Tuduhaké besutan sing bisa digabung",
        "mergehistory-submit": "Gabung owahan",
        "mergehistory-fail": "Ora bisa nggabung sajarah, coba dipriksa manèh kacané lan paramèter wektuné.",
        "mergehistory-fail-invalid-source": "Kaca sumber ora trep.",
        "mergehistory-fail-invalid-dest": "Kaca paran ora trep.",
-       "mergehistory-fail-no-change": "Panggabung sujarah ora nggabungaké rèvisi. Mangga priksanen kaca lan paramèter wektuné.",
+       "mergehistory-fail-no-change": "Panggabung sajarah ora nggabungaké rèvisi. Mangga priksanen kaca lan paramèter wektuné.",
        "mergehistory-fail-self-merge": "Kaca asal lan kaca paran padha.",
        "mergehistory-fail-timestamps-overlap": "Rèvisi asal tumpuk-undhung utawa njedhul sawisé révisi paran.",
-       "mergehistory-fail-toobig": "Ora bisa nggabungaké sujarah amarga {{PLURAL:$1|révisi}} sing arep dilih munjuli $1.",
+       "mergehistory-fail-toobig": "Ora bisa nggabungaké sajarah amarga {{PLURAL:$1|révisi}} sing arep dilih munjuli $1.",
        "mergehistory-no-source": "Kaca sumber $1 ora ana.",
        "mergehistory-no-destination": "Kaca paran $1 ora ana.",
        "mergehistory-invalid-source": "Kaca sumber kudu asesirah sing sah.",
        "mergelog": "Gabung log",
        "revertmerge": "Wurung gabung",
        "mergelogpagetext": "Ing ngisor iki kapacak daftar panggabungan sajarah kaca ing kaca liyané.",
-       "history-title": "Sujarah owahaning \"$1\"",
+       "history-title": "Sajarah owahaning \"$1\"",
        "difference-title": "Prabéda antara owahan \"$1\"",
        "difference-title-multipage": "Béda antarané kaca \"$1\" lan \"$2\"",
        "difference-multipage": "(Prabédhan antar kaca)",
        "right-autopatrol": "Gawé supaya suntingan-suntingan ditandhani minangka wis dipatroli",
        "right-patrolmarks": "Ndeleng tandha-tandha patroli owah-owahan anyar",
        "right-unwatchedpages": "Tuduhna daftar kaca-kaca sing ora diawasi",
-       "right-mergehistory": "Gabung sujarah kaca",
+       "right-mergehistory": "Gabung sajarah kaca",
        "right-userrights": "Besut kabèh hak panganggo",
        "right-userrights-interwiki": "Besut hak-haking panganggo asal wiki jaba",
        "right-siteadmin": "Kunci lan buka kunci basis data",
        "action-delete": "busak kaca iki",
        "action-deleterevision": "busak révisi",
        "action-deletelogentry": "busak isian log",
-       "action-deletedhistory": "deleng sujarah sing dibusak sawijiné kaca",
+       "action-deletedhistory": "deleng sajarah sing dibusak sawijiné kaca",
        "action-deletedtext": "deleng tèks révisi sing dibusak",
        "action-browsearchive": "nggolèki kaca-kaca sing wis dibusak",
        "action-undelete": "wurung busak kaca",
        "action-purge": "buwang kaca iki",
        "nchanges": "$1 {{PLURAL:$1|pangowahan|owah-owahan}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|saka keri dhewe mrene}}",
-       "enhancedrc-history": "sujarah",
+       "enhancedrc-history": "sajarah",
        "recentchanges": "Owahan pungkasan",
        "recentchanges-legend": "Pilihan owah-owahan pungkasan",
        "recentchanges-summary": "Runutna owah-owahan pungkasan ing wiki iki ing kaca iki.",
        "listfiles-latestversion-yes": "Iya",
        "listfiles-latestversion-no": "Ora",
        "file-anchor-link": "Barkas",
-       "filehist": "Sujarah barkas",
+       "filehist": "Sajarah barkas",
        "filehist-help": "Klik ing tanggal/wektuné saprelu ndeleng rupané barkasé nalika tanggal iku.",
        "filehist-deleteall": "busaken kabèh",
        "filehist-deleteone": "busaken iki",
        "deletereasonotherlist": "Alesan liya",
        "deletereason-dropdown": "*Alesan pambusakan\n** Spam\n** Vandalisme\n** Nglanggar hak cipta\n** Disuwun sing nulis\n** Pangalihan rusak",
        "delete-edit-reasonlist": "Besut alesané pambusak",
-       "delete-toobig": "Kaca iki darbé sujarah besutan sing dawa, punjul $1 {{PLURAL:$1|owahan}}.\nPambusak tumrap kaca sing kaya mangkono wis ora diidinaké nedya njagani murih ora ana karusakan ing {{SITENAME}}.",
-       "delete-warning-toobig": "Kaca iki duwé sujarah besut sing dawa, punjul $1 {{PLURAL:$1|révisi}}.\nMbusak kaca iki bisa ngrusak lakuné basis dhata ing {{SITENAME}};\nkudu diayahi kanthi ngati-ati.",
+       "delete-toobig": "Kaca iki darbé sajarah besutan sing dawa, punjul $1 {{PLURAL:$1|owahan}}.\nPambusak tumrap kaca sing kaya mangkono wis ora diidinaké nedya njagani murih ora ana karusakan ing {{SITENAME}}.",
+       "delete-warning-toobig": "Kaca iki duwé sajarah besut sing dawa, punjul $1 {{PLURAL:$1|révisi}}.\nMbusak kaca iki bisa ngrusak lakuné basis dhata ing {{SITENAME}};\nkudu diayahi kanthi ngati-ati.",
        "deleteprotected": "Panjenengan ora bisa mbusak kaca iki amarga direksa.",
        "deleting-backlinks-warning": "'''Awas:''' Kaca liyane mungkin ana sing nautake ing kaca sing arep sampeyan busak.",
        "rollback": "Pulihaké besutan",
        "lockedbyandtime": "(déning {{GENDER:$1|$1}} tanggal $2 wanci $3)",
        "move-page": "Ngalih $1",
        "move-page-legend": "Mindhah kaca",
-       "movepagetext": "Formulir ing ngisor iki bakal ngganti jeneng kaca lan ngalihaké kabèh sujarahé nyang jeneng anyar.\nJeneng lawas bakal dadi kaca alihan marang jeneng anyar.\nPanjenengan bisa ndandani kaca alihan sing otomatis nggayut nyang kaca asliné.\nYèn ora, pesthèkaké yèn panjenengan wis mriksa ana-orané kaca alihan [[Special:DoubleRedirects|dhobel]] utawa [[Special:BrokenRedirects|rusak]].\nPanjenengan kudu tanggon saperlu mesthèkaké yèn pranalané menyang kaca sing samesthiné.\n\nÉling-élingen yèn kacané <strong>ora</strong> bakal dilih yèn jeneng sing dituju wis ana kacané, kajaba isiné kaca alihan sing ora ana sujarah besutané.\nIki ateges panjenengan bisa ngganti jeneng kaca bali nyang asliné manawa ana salah, lan panjenengan ora bisa ngamblegi kaca sing wis ana.\n\n<strong>Cathetan:</strong>\nTumindak iki bisa dadi owahan sing ora kinira lan gedhé mungguh ing kaca sing misuwur;\nmangga pesthèkaké dhisik yèn panjenengan mudheng temahané sadurungé mbacutaké.",
-       "movepagetext-noredirectfixer": "Formulir ing ngisor iki bakal ngganti jeneng kaca lan ngalihaké kabèh sujarahé nyang jeneng anyar.\nJeneng lawas bakal dadi kaca alihan marang jeneng anyar.\nPanjenengan kudu yakin yèn wis mriksa ana-orané kaca alihan [[Special:DoubleRedirects|dhobel]] utawa [[Special:BrokenRedirects|rusak]].\nPanjenengan kudu tanggon saperlu mesthèkaké yèn pranalané menyang kaca sing samesthiné.\n\nÉling-élingen yèn kacané <strong>ora</strong> bakal dilih yèn jeneng sing dituju wis ana kacané, kajaba isiné kaca alihan sing ora ana sujarah besutané.\nIki ateges panjenengan bisa ngganti jeneng kaca bali nyang asliné manawa ana salah, lan panjenengan ora bisa ngamblegi kaca sing wis ana.\n\n<strong>Cathetan:</strong>\nTumindak iki bisa dadi owahan sing ora kinira lan gedhé mungguh ing kaca sing misuwur;\nmangga pesthèkaké dhisik yèn panjenengan mudheng temahané sadurungé mbacutaké.",
+       "movepagetext": "Formulir ing ngisor iki bakal ngganti jeneng kaca lan ngalihaké kabèh sajarahé nyang jeneng anyar.\nJeneng lawas bakal dadi kaca alihan marang jeneng anyar.\nPanjenengan bisa ndandani kaca alihan sing otomatis nggayut nyang kaca asliné.\nYèn ora, pesthèkaké yèn panjenengan wis mriksa ana-orané kaca alihan [[Special:DoubleRedirects|dhobel]] utawa [[Special:BrokenRedirects|rusak]].\nPanjenengan kudu tanggon saperlu mesthèkaké yèn pranalané menyang kaca sing samesthiné.\n\nÉling-élingen yèn kacané <strong>ora</strong> bakal dilih yèn jeneng sing dituju wis ana kacané, kajaba isiné kaca alihan sing ora ana sajarah besutané.\nIki ateges panjenengan bisa ngganti jeneng kaca bali nyang asliné manawa ana salah, lan panjenengan ora bisa ngamblegi kaca sing wis ana.\n\n<strong>Cathetan:</strong>\nTumindak iki bisa dadi owahan sing ora kinira lan gedhé mungguh ing kaca sing misuwur;\nmangga pesthèkaké dhisik yèn panjenengan mudheng temahané sadurungé mbacutaké.",
+       "movepagetext-noredirectfixer": "Formulir ing ngisor iki bakal ngganti jeneng kaca lan ngalihaké kabèh sajarahé nyang jeneng anyar.\nJeneng lawas bakal dadi kaca alihan marang jeneng anyar.\nPanjenengan kudu yakin yèn wis mriksa ana-orané kaca alihan [[Special:DoubleRedirects|dhobel]] utawa [[Special:BrokenRedirects|rusak]].\nPanjenengan kudu tanggon saperlu mesthèkaké yèn pranalané menyang kaca sing samesthiné.\n\nÉling-élingen yèn kacané <strong>ora</strong> bakal dilih yèn jeneng sing dituju wis ana kacané, kajaba isiné kaca alihan sing ora ana sajarah besutané.\nIki ateges panjenengan bisa ngganti jeneng kaca bali nyang asliné manawa ana salah, lan panjenengan ora bisa ngamblegi kaca sing wis ana.\n\n<strong>Cathetan:</strong>\nTumindak iki bisa dadi owahan sing ora kinira lan gedhé mungguh ing kaca sing misuwur;\nmangga pesthèkaké dhisik yèn panjenengan mudheng temahané sadurungé mbacutaké.",
        "movepagetalktext": "Menawa sampéyan nyénthang kothak iki, kaca parembugan sing magepokan bakal otomatis dilih nyang sesirah anyar, kajaba kaca parembugané sing dituju wis ana isiné.\n\nYèn mangkéné, sampéyan kudu ngalih utawa nggabung kaca-kaca iku kanthi manual.",
        "moveuserpage-warning": "<strong>Pènget:</strong> Panjenengan iki arep ngalih kaca panganggo. Mangga èlingana yèn mung kacané waé sing bakal dilih, déné panganggoné <em>ora</em> bakal ganti jeneng.",
        "movecategorypage-warning": "<strong>Pélik:</strong> Panjenengan arep ngalih kaca kategori. Tulung gatèkaké yèn mung kacané thok sing bakal dilih déné samubarang kaca sing ana ing kategori lawas <em>ora</em> bakal mèlu dilih nyang kaca kategori anyar.",
        "pageinfo-title": "Inpormasi kanggo \"$1\"",
        "pageinfo-not-current": "Maaf, tidak mungkin memberikan informasi ini ke revisi lama.",
        "pageinfo-header-basic": "Informasi dhasar",
-       "pageinfo-header-edits": "Sujarah besutan",
+       "pageinfo-header-edits": "Sajarah besutan",
        "pageinfo-header-restrictions": "Perlindungan halaman",
        "pageinfo-header-properties": "Properti kaca",
        "pageinfo-display-title": "Sesirah pajangan",
        "fileduplicatesearch-noresults": "Ora tinemu barkas kanthi jeneng \"$1\".",
        "specialpages": "Kaca mirunggan",
        "specialpages-note-top": "Katrangan",
-       "specialpages-note": "* Kaca mirunggan sedhengan.\n* <span class=\"mw-specialpagerestricted\">Kaca mirunggan winatesan.</span>",
        "specialpages-group-maintenance": "Lapuran pangopèn",
        "specialpages-group-other": "Kaca mirunggan liyané",
        "specialpages-group-login": "Mlebu log / nggawé akun",
index 91512db..18b1110 100644 (file)
        "rcfilters-legend-heading": "<strong>약어 목록:</strong>",
        "rcfilters-activefilters": "사용 중인 필터",
        "rcfilters-advancedfilters": "고급 필터",
+       "rcfilters-limit-shownum": "최근 $1개의 변경사항 표시",
        "rcfilters-days-show-days": "$1{{PLURAL:$1|일}}",
        "rcfilters-days-show-hours": "$1{{PLURAL:$1|시간}}",
        "rcfilters-quickfilters": "저장된 필터",
        "rcfilters-hideminor-conflicts-typeofchange-global": "특정한 유형의 변경사항을 \"사소한 편집\"으로 지정할 수 없기 때문에 \"사소한 편집\" 필터는 하나 이상의 변경사항 유형 필터와 충돌합니다. 충돌되는 필터들은 위의 사용 중인 필터 영역에 표시됩니다.",
        "rcfilters-hideminor-conflicts-typeofchange": "특정한 종류의 변경사항은 \"사소한 편집\"으로 지정할 수 없으므로 이 필터는 다음 유형의 변경사항 필터와 충돌합니다: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "이 유형의 변경사항 필터는 \"사소한 편집\" 필터와 충돌합니다. 특정한 종류의 변경사항은 \"사소한 편집\"으로 지정할 수 없습니다.",
-       "rcfilters-filtergroup-lastRevision": "마지막 판",
-       "rcfilters-filter-lastrevision-label": "마지막 판",
-       "rcfilters-filter-lastrevision-description": "문서의 최근 변경사항입니다.",
-       "rcfilters-filter-previousrevision-label": "ì\9d´ì \84 í\8c\90",
-       "rcfilters-filter-previousrevision-description": "문서에 대한 최근 변경사항이 아닌 모든 변경사항입니다.",
+       "rcfilters-filtergroup-lastRevision": "최신판",
+       "rcfilters-filter-lastrevision-label": "최신판",
+       "rcfilters-filter-lastrevision-description": "문서의 최근 변경사항입니다.",
+       "rcfilters-filter-previousrevision-label": "ìµ\9cì\8b í\8c\90ì\9d´ ì\95\84ë\8b\98",
+       "rcfilters-filter-previousrevision-description": "\"최신판\"이 아닌 모든 변경사항입니다.",
        "rcfilters-filter-excluded": "제외됨",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:아님</strong> $1",
        "rcfilters-view-tags": "태그된 편집",
        "delete-warning-toobig": "이 문서에는 {{PLURAL:$1|편집 역사}}가 $1개 있습니다.\n편집 역사가 긴 문서를 삭제하면 {{SITENAME}} 데이터베이스 동작에 큰 영향을 줄 수 있습니다.\n주의해 주세요.",
        "deleteprotected": "이 문서가 보호되어 있기 때문에 삭제할 수 없습니다.",
        "deleting-backlinks-warning": "<strong>경고:</strong> 삭제하려는 문서가 [[Special:WhatLinksHere/{{FULLPAGENAME}}|다른 문서]]에 링크되어 있거나 끼워져 있습니다.",
+       "deleting-subpages-warning": "<strong>경고:</strong> 삭제하려는 문서에 [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|하나의 하위 문서|$1개의 하위 문서|51=50개 이상의 하위 문서}}]]가 있습니다.",
        "rollback": "편집 되돌리기",
        "rollbacklink": "되돌리기",
        "rollbacklinkcount": "{{PLURAL:$1|편집}} $1회 되돌리기",
        "fileduplicatesearch-noresults": "\"$1\"이라는 이름을 가진 파일이 없습니다.",
        "specialpages": "특수 문서 목록",
        "specialpages-note-top": "범례",
-       "specialpages-note": "* 일반 특수 문서입니다.\n* <span class=\"mw-specialpagerestricted\">제한된 특수 문서입니다.</span>",
        "specialpages-group-maintenance": "관리용 목록",
        "specialpages-group-other": "다른 특수 문서",
        "specialpages-group-login": "로그인 / 계정 만들기",
index 3718d88..874f2b5 100644 (file)
        "rcfilters-legend-heading": "<strong>Lëscht vun Ofkierzungen:</strong>",
        "rcfilters-activefilters": "Aktiv Filteren",
        "rcfilters-advancedfilters": "Erweidert Filteren",
+       "rcfilters-limit-title": "Ännerunge fir ze weisen",
+       "rcfilters-limit-shownum": "Lescht $1 Ännerunge weisen",
+       "rcfilters-days-title": "Rezent Deeg",
+       "rcfilters-hours-title": "Rezent Stonnen",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|Dag|Deeg}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|Stonn|Stonnen}}",
        "rcfilters-quickfilters": "Gespäichert Filteren",
        "rcfilters-quickfilters-placeholder-title": "Nach keng Linke gespäichert",
        "rcfilters-quickfilters-placeholder-description": "Fir Är Filterastellungen z'änneren a méi spéit nees ze benotzen, klickt op d'Zeeche  fir Lieszeechen (bookmark) am Beräich vun den Aktive Filteren hei drënner.",
        "rcfilters-invalid-filter": "Net valabele Filter",
        "rcfilters-empty-filter": "Keen aktive Filter. All Kontributioune gi gewisen.",
        "rcfilters-filterlist-title": "Filteren",
-       "rcfilters-filterlist-whatsthis": "Wat ass dat?",
+       "rcfilters-filterlist-whatsthis": "Wéi geet dat?",
        "rcfilters-highlightbutton-title": "Resultater ervirhiewen",
        "rcfilters-highlightmenu-title": "Eng Faarf eraussichen",
        "rcfilters-filterlist-noresults": "Keng Filtere fonnt",
        "rcfilters-filter-editsbyself-description": "Är eegen Ännerungen.",
        "rcfilters-filter-editsbyother-label": "Ännerunge vun Aneren",
        "rcfilters-filter-editsbyother-description": "All Ännerunge ausser Ären eegenen.",
-       "rcfilters-filtergroup-userExpLevel": "Niveau vun der Erfahrung (just fir registréiert Benotzer)",
+       "rcfilters-filtergroup-userExpLevel": "Umeldung an Erfarung vu Benotzer",
        "rcfilters-filter-user-experience-level-registered-label": "Ugemellt",
        "rcfilters-filter-user-experience-level-unregistered-label": "Net-ugemellt",
        "rcfilters-filter-user-experience-level-unregistered-description": "Auteuren déi net ageloggt sinn.",
        "rcfilters-filter-logactions-label": "Protokolléiert Aktiounen",
        "rcfilters-filter-logactions-description": "Administrativ Aktiounen, Uleeë vu Benotzerkonten, Läsche vu Säiten, Eropgeluede Fichieren, ...",
        "rcfilters-hideminor-conflicts-typeofchange": "Verschidden Type vu Ännerunge kënnen net als \"kleng\" markéiert ginn, dofir ass dëse Filter a Konflikt mat dësem Typ vun Ännerungsfilteren: $1",
-       "rcfilters-filtergroup-lastRevision": "Lescht Versioun",
+       "rcfilters-filtergroup-lastRevision": "Lescht Versiounen",
        "rcfilters-filter-lastrevision-label": "Lescht Versioun",
-       "rcfilters-filter-lastrevision-description": "Déi lescht Ännerung op enger Säit",
-       "rcfilters-filter-previousrevision-label": "Méi fréi Versiounen",
+       "rcfilters-filter-lastrevision-description": "Nëmmen déi lescht Ännerung op enger Säit.",
+       "rcfilters-filter-previousrevision-label": "Net déi lescht Versioun",
        "rcfilters-filter-previousrevision-description": "All Ännerungen, déi net déi rezenst Ännerung vun enger Säit sinn.",
        "rcfilters-filter-excluded": "Ausgeschloss",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:net</strong> $1",
        "fileduplicatesearch-noresults": "Et gouf kee Fichier mam Numm \"$1\" fonnt.",
        "specialpages": "Spezialsäiten",
        "specialpages-note-top": "Erklärung",
-       "specialpages-note": "* Normal Spezialsäiten.\n* <span class=\"mw-specialpagerestricted\">Spezialsäite fir Benotzer mat méi Rechter.</span>",
        "specialpages-group-maintenance": "Maintenance-Rapporten",
        "specialpages-group-other": "Aner Spezialsäiten",
        "specialpages-group-login": "Aloggen / Benotzerkont uleeën",
index 1e38b71..41ca9d1 100644 (file)
        "preview": "Naokieke",
        "showpreview": "Betrach dees bewirking",
        "showdiff": "Toen verangeringe",
+       "blankarticle": "<strong>Waorsjoewing:</strong> de pagina die se wils aanmake is laeg.\nWens se oppernuuj op \"$1\" kliks, wuuertj de pagina aangemaak zónger welchen inhawd den ouch.",
        "anoneditwarning": "<strong>Waorsjoewing:</strong> Doe bös neet aangemeldj.\nDien IP-adres wuuertj opgeslage wen se verangeringe maaks op dees pagina. Wens doe <strong>[$1 dich aanmeljs]</strong> of <strong>[$2 'ne gebroeker aanmaaks]</strong> versjiene dien bewirkinge ónger diene gebroekersnaam, naeve anges veurdeiler.",
        "anonpreviewwarning": "''Doe bös neet aangemeldj.''\n''Door dien bewèrking op te slaon wört dien IP-adres opgeslagen in de paginagesjiedenis.''",
        "missingsummary": "'''Herinnering:''' doe höbs gein samevatting opgegaeve veur dien bewirking. Es te weer op ''Pagina opslaon'' kliks weurt de bewirking zonger samevatting opgesjlage.",
+       "selfredirect": "<strong>Waorsjoewing:</strong> Doe höbs 'ne redirek gemaak nao dees pagina.\nMeugelik höbs se 'n verkieërdje bestumming veure redirek gebroek of bewirks se de verkieërdje pagina.\nDoor nans op \"$1\" te klikke wuuertj de redirek tonna gemaak.",
        "missingcommenttext": "Plaats dien opmèrking hiej onger, a.u.b.",
        "missingcommentheader": "'''Let op:''' Doe höbs gén ongerwerp/kop veur deze opmèrking opgegaeve. Esse oppernuuj op \"$1\" kliks, wörd dien verangering zonger ongerwerp/kop opgeslage.",
        "summary-preview": "Veurvertoeaning van de bewirkingssamevatting:",
        "post-expand-template-argument-warning": "Waarsjoewing: dees pagina bevat winnigstes eine sjabloonparameter mit 'n te groete transclusiegruutde.\nDees parameters zeen eweggelaote.",
        "post-expand-template-argument-category": "Pagina's die missende sjabloonillemènte bevatte",
        "parser-template-loop-warning": "D'r is 'ne krinkloup in sjablone geconstateerd: [[$1]]",
+       "template-loop-category": "Pagina's mit sjeblone die zichzelf insloete",
        "parser-template-recursion-depth-warning": "De recursiedeepte veur sjablone is euversjrede ($1)",
        "language-converter-depth-warning": "De deepdjelimiet veure spraokómzètter is euversjreje ($1)",
        "node-count-exceeded-category": "Pagina's wo 't maximaal aantal nodes te väöl is",
-       "node-count-exceeded-warning": "Oppe paasj is 't maximaal aantal nodes te väöl",
+       "node-count-exceeded-warning": "Oppe paasj is 't maximaal aantal nodes behaoltj",
        "expansion-depth-exceeded-category": "Pagina's wo de expansiedeepdje te väöl is",
+       "expansion-depth-exceeded-category-desc": "De pagina geit euver de maximaal oetbreijingsdeepdje.",
        "expansion-depth-exceeded-warning": "De paasj haet te väöl sjablone",
        "parser-unstrip-loop-warning": "Unstriplus gevónje",
        "parser-unstrip-recursion-limit": "Unstriprecursielimiet te väöl ($1)",
+       "converter-manual-rule-error": "'n Fout is óntdèk gewaore in 'ne handjmaesig tougeveudje spraokómzèttingsregel",
        "undo-success": "Hiej onger stuit de teks wo in de verangering ongedaon gemaak is. Controleer veur 't opslaon of 't resultaot gewins is.",
        "undo-failure": "De verangering kòs neet ongedaon gemaak waere waeges angere striedige verangeringe.",
        "undo-norev": "De bewerking kon neet ongedaan gemaak waere, omdat die neet besteet of is verwijderd.",
+       "undo-nochange": "De bewirking liek al óngedaon te zeen gemaak.",
        "undo-summary": "Versie $1 van [[Special:Contributions/$2|$2]] ([[User talk:$2|euverlèk]]) óngedaon gemaak.",
+       "undo-summary-username-hidden": "Drej versie $1 door 'ne verborge gebroeker trögk",
        "cantcreateaccount-text": "'t Aanmake van gebroekers van dit IP-adres ('''$1''') is geblokkeerd door [[User:$3|$3]].\n\nDe door $3 opgegaeve reje is ''$2''",
        "viewpagelogs": "Logbeuk veur dees pagina tuine",
        "nohistory": "Dees pagina is nog neet bewirk.",
        "page_last": "lèste",
        "histlegend": "Verklaoring aafkortinge: (wijz) = versjil mit actueile versie, (vörrige) = versjil mit vörrige versie, K = kleine verangering",
        "history-fieldset-title": "Zeuk nao versies",
-       "history-show-deleted": "Inkel eweggesjaf",
+       "history-show-deleted": "Inkel eweggesjafdje versie",
        "histfirst": "aadste",
        "histlast": "nuujste",
        "historysize": "({{PLURAL:$1|1 byte|$1 bytes}})",
        "history-feed-description": "Bewerkingseuverzich veur dees pagina op de wiki",
        "history-feed-item-nocomment": "$1 op $2",
        "history-feed-empty": "De gevraogde pagina besjteit neet.\nWellich is ze gewis of verplaats.\n[[Special:Search|Doorzeuk de wiki]] veur relevante pagina's.",
+       "history-edit-tags": "Bewirk labels van oetgekaoze versies",
        "rev-deleted-comment": "(bewirkingssamevatting eweggesjaf)",
        "rev-deleted-user": "(gebroeker weggehaold)",
-       "rev-deleted-event": "(actie weggehaold)",
+       "rev-deleted-event": "(logbookregel weggehaold)",
        "rev-deleted-user-contribs": "[gebroeker of IP gewösj - bewèrking verbórge in biedraag]",
        "rev-deleted-text-permission": "Dees bewerking is '''gewusj'''.\nDao kónne details aanwezig zeen in 't [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} wusjlogbook].",
+       "rev-suppressed-text-permission": "Dees paginaversie is <strong>óngerdrók</strong>.\nAchtergrönj zeen te vinjen in 't [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} logbook ven óngerdrökdje versies].",
        "rev-deleted-text-unhide": "Dees versie van de pagina is '''eweggesjaf'''.\nDetails zien meugelik te vinde in 't [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} wislogbook].\nEs beheerder kins se [$1 dees versie bekieke] es se wils.",
        "rev-suppressed-text-unhide": "Dees paginaversie is '''óngerdrök'''.\nAchtergrönj zeen meugelik te vinje in 't [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} logbook ven óngerdrökdje versies].\nEs behierder kèns toe [$1 de versjille bekieken] es se wils.",
        "rev-deleted-text-view": "Dees bewèrking is '''gewösj'''.\nEs beheerder kèns te deze zeen;\ndao kónne details aanwezig zeen in 't [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} wusjlogbook].",
        "revdelete-confirm": "Bevestig des se dit wils doon, des se de consequenties begrieps en des se dit deis in euvereinstömming mit 't geljendj [[{{MediaWiki:Policy-url}}|beleid]].",
        "revdelete-suppress-text": "Versies verbèrge deentj '''allein''' gebroek te waere in de volgende gevalle:\n* Ongepaste perseunlike informatie\n*: ''woonadres, telefoonnummers, Burger Service Nummers, enzovoors.''",
        "revdelete-legend": "Stel zichbaarheidsbeperkinge in",
-       "revdelete-hide-text": "Verberg de bewerkte teks",
+       "revdelete-hide-text": "Versieteks",
        "revdelete-hide-image": "Verberg bestandjsinhoud",
-       "revdelete-hide-name": "Actie en doel verberge",
-       "revdelete-hide-comment": "De bewerkingssamevatting verberge",
+       "revdelete-hide-name": "Verstaek hanjeling en doel",
+       "revdelete-hide-comment": "Bewirkingssamevatting",
        "revdelete-hide-user": "Verberg gebroekersnaam/IP van de gebroeker",
        "revdelete-hide-restricted": "Pas deze beperkinge toe op zowaal beheerders es angere",
        "revdelete-radio-same": "(anger neet)",
        "mergehistory-empty": "Gein inkele versies kinne samegevoeg waere.",
        "mergehistory-done": "$3 {{PLURAL:$3|versie|versies}} van $1 zeen succesvol samegevoeg nao [[:$2]].",
        "mergehistory-fail": "Kan gein gesjiedenis samevoege, lèvver opnuuj de pagina- en tiedparamaeters te controlere.",
+       "mergehistory-fail-bad-timestamp": "Tiedstempel is óngeljig.",
+       "mergehistory-fail-invalid-source": "Brónpagina is óngeljig.",
+       "mergehistory-fail-invalid-dest": "Doelpagina is óngeljig.",
        "mergehistory-no-source": "Bronpagina $1 besteit neet.",
        "mergehistory-no-destination": "Bestömmingspagina $1 besteit neet.",
        "mergehistory-invalid-source": "De bronpagina mot 'ne geldige titel zeen.",
        "grouppage-bot": "{{ns:project}}:Bots",
        "grouppage-sysop": "{{ns:project}}:Beheerders",
        "grouppage-bureaucrat": "{{ns:project}}:Bureaucrate",
-       "grouppage-suppress": "{{ns:project}}:Euverzich",
+       "grouppage-suppress": "{{ns:project}}:Toezich",
        "right-read": "Pagina's bekieke",
        "right-edit": "Pagina's bewerke",
        "right-createpage": "Pagina's aanmake",
        "right-editinterface": "De gebroekersinterface bewerke",
        "right-editusercss": "De CSS-bestande van angere gebroekers bewerke",
        "right-edituserjs": "De JS-bestande van angere gebroekers bewerke",
+       "right-editmyoptions": "Bewirk dien eige veurkäöre",
        "right-rollback": "Snel de letste bewerking(e) van 'n gebroeker van 'n pagina terugdraaie",
        "right-markbotedits": "Teruggedraaide bewerkinge markere es botbewerkinge",
        "right-noratelimit": "Heet gein ti'jdsafhankelijke beperkinge",
        "right-siteadmin": "De database blokkere en weer vriegaeve",
        "right-override-export-depth": "Export paazjes midin geslinkdje paazjes mit 'n deepdje ven 5",
        "right-sendemail": "Versjik e-mail aan anger gebroekers",
+       "grant-generic": "Rechtegroep \"$1\"",
+       "grant-group-page-interaction": "Wirk mit pagina's",
+       "grant-group-file-interaction": "Wirk mit media",
+       "grant-group-watchlist-interaction": "Wirk mit diene volglies",
+       "grant-group-email": "Sjik e-mail",
+       "grant-group-high-volume": "Veur aktiviteite mit hoeag voluum oet",
+       "grant-group-customization": "Aanpassinge en veurkäöre",
+       "grant-group-administration": "Veur behieërdershanjelinge oet",
+       "grant-group-private-information": "Betrach privaatgegaeves euver dich",
+       "grant-group-other": "Divers hanjelinge",
+       "grant-blockusers": "Blokkeer en deblokkeer gebroekers",
+       "grant-createaccount": "Maak gebroekers aan",
+       "grant-createeditmovepage": "Maak, bewirk en verplaats pagina's",
+       "grant-delete": "Wösj pagina's, bewirkinge en logbookregele",
+       "grant-basic": "Basisrechte",
        "newuserlogpage": "Logbook nuuj gebroekers",
        "newuserlogpagetext": "Hiej ónger saton de nuuj ingesjreve gebroekers.",
        "rightslog": "Gebroekersrechtelogbook",
        "action-userrights-interwiki": "gebroekersrechte van gebroekers van anger wiki's te bewerke",
        "action-siteadmin": "de database aaf te sloete of aope te stelle",
        "action-sendemail": "Sjik e-mails",
+       "action-purge": "sjoean dees pagina op",
        "nchanges": "$1 {{PLURAL:$1|bewerking|bewerkinge}}",
+       "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|saer die litste bezeuk}}",
        "enhancedrc-history": "historie",
        "recentchanges": "Lètste verangeringe",
        "recentchanges-legend": "Opties veur recènte verangeringe",
        "recentchanges-label-plusminus": "Dees paginagruuedje is verangerdj mit dit aantaal aan bytes",
        "recentchanges-legend-heading": "<strong>Legenda:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (zuuch ouch [[Special:NewPages|de nuuj pagina's]])",
+       "recentchanges-submit": "Toean",
+       "rcfilters-legend-heading": "<strong>Lies mit aafkórtinge:</strong>",
+       "rcfilters-activefilters": "Aktief filtjers",
+       "rcfilters-advancedfilters": "Geavenceerdje filtjers",
+       "rcfilters-limit-title": "Te toeane verangeringe",
+       "rcfilters-limit-shownum": "Toean de litste $1 verangeringe",
+       "rcfilters-days-title": "Recènte daag",
+       "rcfilters-hours-title": "Recènte oere",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|daag}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|oer}}",
+       "rcfilters-quickfilters": "Opgeslage filtjers",
+       "rcfilters-quickfilters-placeholder-title": "Nag gein opgeslage links",
+       "rcfilters-savedqueries-rename": "Herneum",
+       "rcfilters-savedqueries-setdefault": "Stèl in es standerd",
+       "rcfilters-savedqueries-unsetdefault": "Sjaf eweg es standerd",
+       "rcfilters-savedqueries-remove": "Sjaf eweg",
+       "rcfilters-savedqueries-new-name-label": "Naam",
+       "rcfilters-savedqueries-new-name-placeholder": "Besjrief 't doel van de filtjer",
+       "rcfilters-savedqueries-apply-label": "Maak filtjer aan",
+       "rcfilters-savedqueries-cancel-label": "Braek aaf",
+       "rcfilters-savedqueries-add-new-title": "Slaon hujige filtjerinstèllinge op",
+       "rcfilters-restore-default-filters": "Zèt standerd filtjers trögk",
+       "rcfilters-clear-all-filters": "Sjaf alle filtjers eweg",
+       "rcfilters-search-placeholder": "Filtjer recènte verangeringe (blajer of begin mit intikke)",
+       "rcfilters-invalid-filter": "Óngeljige filtjer",
+       "rcfilters-empty-filter": "Gein aktief filtjers. Alle biedrage waere waergaeve.",
+       "rcfilters-filterlist-title": "Filtjers",
+       "rcfilters-filterlist-whatsthis": "Wie wirk dit?",
+       "rcfilters-filterlist-feedbacklink": "Gaef trögksjaking op de nuuj (bèta)filtjers",
+       "rcfilters-highlightbutton-title": "Markeer rizzeltaote",
+       "rcfilters-highlightmenu-title": "Kees 'n kluuer",
+       "rcfilters-highlightmenu-help": "Kees 'n kluuer veur dees eigesjappe oet te lichte",
+       "rcfilters-filterlist-noresults": "Gein filtjers gevónje",
+       "rcfilters-filter-bots-label": "Bot",
+       "rcfilters-tag-prefix-namespace-inverted": "<strong>:neet</strong> $1",
        "rcnotefrom": "{{PLURAL:$5|Verangering|Verangeringe}} saer <strong>$3 óm $4</strong> (maximaal <strong>$1</strong> {{PLURAL:$1|verangering|verangeringe}}).",
        "rclistfrom": "Tuin de verangeringe vanaaf $3 $2",
        "rcshowhideminor": "$1 klein bewèrkinge",
        "rcshowhideanons-show": "Toean",
        "rcshowhideanons-hide": "Versjtaek",
        "rcshowhidepatr": "$1 gecontroleerde bewerkinge",
+       "rcshowhidepatr-show": "Toean",
+       "rcshowhidepatr-hide": "Versjtaek",
        "rcshowhidemine": "$1 mien bewirkinge",
        "rcshowhidemine-show": "Toean",
        "rcshowhidemine-hide": "Versjtaek",
+       "rcshowhidecategorization": "$1 paginacategorisatie",
+       "rcshowhidecategorization-show": "Toean",
+       "rcshowhidecategorization-hide": "Versjtaek",
        "rclinks": "Bekiek de $1 litste verangeringe van de aafgeloupe $2 daag.",
        "diff": "vers",
        "hist": "hist",
        "newpageletter": "N",
        "boteditletter": "b",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|keer|keer}} op 'ne volglies]",
-       "rc_categories": "Beperk tot allein categorieë (sjeij mit 'n \"|\")",
-       "rc_categories_any": "Iddere",
+       "rc_categories": "Bepirk tot categorieë (sjeij mit 'n \"|\")",
+       "rc_categories_any": "Idder vanne gekaozene",
        "rc-change-size-new": "$1 {{PLURAL:$1|byte|bytes}} nao verangering",
        "newsectionsummary": "/* $1 */ nuje subkop",
-       "rc-enhanced-expand": "Details weergaeve (JavaScript verplich)",
+       "rc-enhanced-expand": "Toean details",
        "rc-enhanced-hide": "Details verberge",
        "rc-old-title": "oearsprónkelik aangemaak es \"$1\"",
        "recentchangeslinked": "Volg links",
        "recentchangeslinked-summary": "Dees speciaal pagina tuint de lètste bewirkinge op pagina's die gelink waere vanaaf deze pagina. Pagina's die op [[Special:Watchlist|dien volglies]] staon waere '''vet''' weergegaeve.",
        "recentchangeslinked-page": "Paginanaam:",
        "recentchangeslinked-to": "Verangeringe weergaeve nao de gelinkde pagina's",
+       "recentchanges-page-added-to-category": "[[:$1]] aan categorie tougeveug gewaore",
+       "recentchanges-page-added-to-category-bundled": "[[:$1]] is tougeveug gewaore aan de categorie, [[Special:WhatLinksHere/$1|dees pagina is opgenómme gewaore in anger pagina's]]",
+       "recentchanges-page-removed-from-category": "[[:$1]] is oet de categorie eweggehaoldj gewaore",
+       "recentchanges-page-removed-from-category-bundled": "[[:$1]] is oet de categorie eweggehaoldj gewaore, [[Special:WhatLinksHere/$1|dees pagina is opgenómme gewaore in anger pagina's]]",
+       "autochange-username": "Automatische verangering van MediaWiki",
        "upload": "Upload",
        "uploadbtn": "bestandj uploade",
        "reuploaddesc": "Truuk nao 't uploadformeleer.",
        "unwatchthispage": "Neet mië volge",
        "notanarticle": "Is gein artikel",
        "notvisiblerev": "Bewèrking is verwiederd",
-       "watchlist-details": "D'r {{PLURAL:$1|sjteit ein pagina|sjtaon $1 pagina's}} op dien volglies mit oetzunjering van de euverlèkpagina's.",
+       "watchlist-details": "D'r {{PLURAL:$1|sjteit ein pagina|sjtaon $1 pagina's}} op dien volglies mit de euverlèkpagina's neet mitgetèldj.",
        "wlheader-enotif": "Doe wörs per e-mail gewaarsjuwd",
        "wlheader-showupdated": "Pazjena's die verangerd zeen saers doe ze veur 't lètste bekeeks sjtaon '''vet'''",
        "wlnote": "Hieónger {{PLURAL:$1|steit de lètste verangering|staon de lètste <strong>$1</strong> verangeringe}} van {{PLURAL:$2|'t lètste oer|de lètste <strong>$2</strong> oer}} óp $3 óm $4.",
        "pageinfo-few-watchers": "Minder es  {{PLURAL:$1|eine volger|$1 volgers}}",
        "pageinfo-redirects-name": "Aantaal redireks nao dees pagina",
        "pageinfo-subpages-name": "Subpagina's van dees pagina",
+       "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|redirek|redireks}}; $3 {{PLURAL:$3|neet-redirek|neet-redireks}})",
        "pageinfo-firstuser": "Aanmaker",
        "pageinfo-firsttime": "Datum vannen aanmaak",
        "pageinfo-lastuser": "Litste bewirker",
        "pageinfo-recent-edits": "Recènte bewirkinge (binne de aafgeloupe $1)",
        "pageinfo-recent-authors": "Recènte sjrievers",
        "pageinfo-magic-words": "{{PLURAL:$1|Magisch waord|Magische wäörd}} ($1)",
+       "pageinfo-hidden-categories": "Verstaoke {{PLURAL:$1|categorie|categorieje}} ($1)",
        "pageinfo-templates": "{{PLURAL:$1|Gebroek sjebloon|Gebroekde sjeblone}} ($1)",
        "pageinfo-toolboxlink": "Pazjena-infermasie",
        "pageinfo-contentpage": "Getèldj es pagina mit inhawd",
        "fileduplicatesearch-result-n": "'t Bestandj \"$1\" haet {{PLURAL:$2|1 identieke döbbelversie|$2 identiek döbbelversies}}.",
        "fileduplicatesearch-noresults": "d'r Is gei bestandj mitte naam \"$1\" gevónje.",
        "specialpages": "Speciaal pagina's",
-       "specialpages-note": "* Normaal speciaal pagina's\n* <strong class=\"mw-specialpagerestricted\">Beperk toegankelike speciaal pagina's</strong>\n* <span class=\"mw-specialpagecached\">Speciaal pagina's mit allein gegaeves oete cache (meugelik verajerd)</span>",
        "specialpages-group-maintenance": "Óngerhajingsrapporter",
        "specialpages-group-other": "Euverige speciaal pazjena's",
        "specialpages-group-login": "Aanmelje / registrere",
        "htmlform-reset": "Maak verangeringe óngedaon",
        "htmlform-selectorother-other": "Anges",
        "logentry-delete-delete": "$1 {{GENDER:$1|haet}} de pagina $3 gewösj",
-       "logentry-delete-restore": "$1 haet de pagina $3 trögkgezat",
+       "logentry-delete-restore": "$1 {{GENDER:$2|haet}} de pagina $3 ($4) trögkgezatte",
        "logentry-delete-event": "$1 haet de zichbaarheid van {{PLURAL:$5|'ne logbookregel|$5 logbookregels}} van $3 gewiezig: $4",
        "logentry-delete-revision": "$1 {{GENDER:$2|haet}} de zichbaarheid van {{PLURAL:$5|'n versie|$5 versies}} van de pagina $3 verangerdj: $4",
        "logentry-delete-event-legacy": "$1 haet de zichbaarheid van logbookregels van $3 gewiezig",
index 9ffd045..5a81bab 100644 (file)
        "rcfilters-hideminor-conflicts-typeofchange-global": "Филтерот „Ситни уредувања“ е спротиставен на еден или повеќе од филтрите за видови измена, бидејќи извеси видови не можат да се означат како ситни. Спротиставените филтри се означени во делот Неактивни филтри погоре.",
        "rcfilters-hideminor-conflicts-typeofchange": "Извезни видови промени не можат да се означат како „ситни“, па затоа овој филтер е во спротиставеност со следниве филтри за видови промени: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Овој филтер за видови промени е во спротиставеност со филтерот „Ситни уредувања“. Извсни видови промени не можат да се означат како „ситни“.",
-       "rcfilters-filtergroup-lastRevision": "Ð\9fоÑ\81ледна Ð¿Ñ\80еÑ\80абоÑ\82ка",
+       "rcfilters-filtergroup-lastRevision": "Ð\9fоÑ\81ледни Ð¿Ñ\80еÑ\80абоÑ\82ки",
        "rcfilters-filter-lastrevision-label": "Последна преработка",
-       "rcfilters-filter-lastrevision-description": "Ð\9dаÑ\98нови Ð¿Ñ\80еÑ\80абоÑ\82ки Ð½Ð° страница.",
-       "rcfilters-filter-previousrevision-label": "Ð\9fÑ\80еÑ\82Ñ\85одни Ð¿Ñ\80еÑ\80абоÑ\82ки",
-       "rcfilters-filter-previousrevision-description": "Сите промени кои не се најнови преработки на страницата.",
+       "rcfilters-filter-lastrevision-description": "Само Ð½Ð°Ñ\98нови Ð¿Ñ\80еÑ\80абоÑ\82ки Ð²Ð¾ страница.",
+       "rcfilters-filter-previousrevision-label": "Ð\9dе Ð¿Ð¾Ñ\81леднаÑ\82а Ð¿Ñ\80еÑ\80абоÑ\82ка",
+       "rcfilters-filter-previousrevision-description": "Сите промени кои не се „последна преработка“.",
        "rcfilters-filter-excluded": "Исклучени",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:не</strong> $1",
        "rcfilters-exclude-button-off": "Изземи избрано",
        "delete-warning-toobig": "Оваа страница има долга историја на уредување, преку $1 {{PLURAL:$1|преработка|преработки}}.\nБришењето може да предизвика проблеми при работењето на базата на податоци на {{SITENAME}};\nпродолжете доколку сте сигруни дека треба тоа да го сторите.",
        "deleteprotected": "Не можете да ја избришете страницава бидејќи е заштитена.",
        "deleting-backlinks-warning": "<strong>Предупредување:</strong>  До страницата што сакате да ја избришете водат [[Special:WhatLinksHere/{{FULLPAGENAME}}|други страници]] или пак се превметнуваат во неа.",
+       "deleting-subpages-warning": "<strong>Предупредување:</strong> Страницата што сакате да ја избришете има [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|потстраница|$1 потстраници|51=преку 50 потстраници}}]].",
        "rollback": "Отповикај промени",
        "rollbacklink": "отповикај",
        "rollbacklinkcount": "отповикај $1 {{PLURAL:$1|уредување|уредувања}}",
        "fileduplicatesearch-noresults": "Не пронајдов податотека со име „$1“.",
        "specialpages": "Службени страници",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Нормални службени страници.\n* <span class=\"mw-specialpagerestricted\">Ограничени службени страници.</span>",
+       "specialpages-note-restricted": "* Нормални службени страници.\n* <span class=\"mw-specialpagerestricted\">Ограничени службени страници.</span>",
        "specialpages-group-maintenance": "Извештаи за одржување",
        "specialpages-group-other": "Други службени страници",
        "specialpages-group-login": "Најава / направи сметка",
index 508c79e..3145b9a 100644 (file)
@@ -23,7 +23,8 @@
                        "Matma Rex",
                        "Nemo bis",
                        "Mbrt",
-                       "Muhdnurhidayat"
+                       "Muhdnurhidayat",
+                       "Jeluang Terluang"
                ]
        },
        "tog-underline": "Garis bawah pautan:",
        "anontalk": "Perbincangan",
        "navigation": "Pandu arah",
        "and": "&#32;dan",
-       "qbfind": "Cari",
-       "qbbrowse": "Semak imbas",
-       "qbedit": "Sunting",
-       "qbpageoptions": "Laman ini",
-       "qbmyoptions": "Laman-laman saya",
        "faq": "Soalan Lazim",
-       "faqpage": "Project:Soalan Lazim",
        "actions": "Tindakan",
        "namespaces": "Ruang nama",
        "variants": "Kelainan",
        "tagline": "Daripada {{SITENAME}}.",
        "help": "Bantuan",
        "search": "Cari",
-       "search-ignored-headings": " #<!-- jangan usik baris ini --> <pre>\n# Tajuk yang akan diabaikan oleh pencarian.\n# Suntingannya diperlakukan sebaik sahaja laman yang bertajuk ini diindekskan.\n# Anda boleh memaksakan pengindeksan semula laman dengan melakukan suntingan nol (null edit).\n# Sintaks adalah seperti berikut:\n#   * Semuanya dari aksara \"#\" ke hujung baris dikira komen.\n#   * Setiap baris tak kosong adalah tajuk yang setepatnya untuk diabaikan.\nRujukan\nPautan luar\nLihat juga\n #</pre> <!-- jangan usik baris ini -->",
+       "search-ignored-headings": " #<!-- jangan usik baris ini --> <pre>\n# Tajuk yang akan diabaikan oleh pencarian.\n# Suntingannya diperlakukan sebaik sahaja laman yang bertajuk ini diindekskan.\n# Anda boleh memaksakan pengindeksan semula laman dengan melakukan suntingan nol (null edit).\n# Sintaks adalah seperti berikut:\n#   * Semuanya dari aksara \"#\" ke hujung baris dikira komen.\n#   * Setiap baris tak kosong ialah tajuk yang setepatnya untuk diabaikan.\nRujukan\nPautan luar\nLihat juga\n #</pre> <!-- jangan usik baris ini -->",
        "searchbutton": "Cari",
        "go": "Pergi",
        "searcharticle": "Pergi",
        "edit-local": "Sunting huraian tempatan",
        "create": "Cipta",
        "create-local": "Tambahkan huraian tempatan",
-       "editthispage": "Sunting laman ini",
-       "create-this-page": "Cipta laman ini",
        "delete": "Hapus",
-       "deletethispage": "Hapuskan laman ini",
-       "undeletethispage": "Nyahhapuskan laman ini",
        "undelete_short": "Nyahhapus {{PLURAL:$1|satu suntingan|$1 suntingan}}",
        "viewdeleted_short": "Lihat {{PLURAL:$1|satu|$1}} suntingan dihapuskan",
        "protect": "Lindung",
        "protect_change": "ubah",
-       "protectthispage": "Lindungi laman ini",
        "unprotect": "Ubah perlindungan",
-       "unprotectthispage": "Ubah tahap perlindungan laman ini",
        "newpage": "Laman baru",
-       "talkpage": "Bincangkan laman ini",
        "talkpagelinktext": "Perbincangan",
        "specialpage": "Laman khas",
        "personaltools": "Alatan peribadi",
-       "articlepage": "Lihat laman kandungan",
        "talk": "Perbincangan",
        "views": "Rupa",
        "toolbox": "Peralatan",
        "tool-link-userrights": "Tukar kumpulan {{GENDER:$1|pengguna}}",
        "tool-link-userrights-readonly": "Lihat kumpulan {{GENDER:$1|pengguna}}",
        "tool-link-emailuser": "Email {{GENDER:$1|pengguna}} ini",
-       "userpage": "Lihat laman pengguna",
-       "projectpage": "Lihat laman projek",
        "imagepage": "Lihat laman fail",
        "mediawikipage": "Lihat laman pesanan",
        "templatepage": "Lihat laman templat",
        "userlogin-resetpassword-link": "Lupa kata laluan anda?",
        "userlogin-helplink2": "Bantuan untuk log masuk",
        "userlogin-loggedin": "Anda sudah log masuk sebagai {{GENDER:$1|$1}}. Gunakan borang di bawah untuk log masuk sebagai pengguna lain.",
-       "userlogin-reauth": "Anda mesti log masuk sekali lagi untuk mengesahkan bahawa anda adalah {{GENDER:$1|$1}}.",
+       "userlogin-reauth": "Anda mesti log masuk sekali lagi untuk mengesahkan bahawa anda ialah {{GENDER:$1|$1}}.",
        "userlogin-createanother": "Buka satu lagi akaun",
        "createacct-emailrequired": "Alamat e-mel",
        "createacct-emailoptional": "Alamat e-mel (pilihan)",
        "noname": "Nama pengguna tidak sah.",
        "loginsuccesstitle": "Berjaya melog masuk",
        "loginsuccess": "'''Anda telah log masuk ke dalam {{SITENAME}} sebagai \"$1\".'''",
-       "nosuchuser": "Tiada pengguna yang menggunakan nama \"$1\".\nNama pengguna adalah kes sensitif.\nSemak ejaan anda, atau sila [[Special:CreateAccount|membuka akaun baru]].",
+       "nosuchuser": "Tiada pengguna yang menggunakan nama \"$1\".\nNama pengguna adalah sesitif huruf.\nSemak ejaan anda, atau sila [[Special:CreateAccount|membuka akaun baru]].",
        "nosuchusershort": "Pengguna \"$1\" tidak wujud. Sila semak ejaan anda.",
        "nouserspecified": "Sila nyatakan nama pengguna.",
        "login-userblocked": "Pengguna ini disekat. Log masuk tidak dibenarkan.",
        "botpasswords-updated-body": "Kata laluan bot untuk nama bot \"$1\" bagi pengguna \"$2\" telah dikemaskini.",
        "botpasswords-deleted-title": "Kata laluan bot telah dipadam",
        "botpasswords-deleted-body": "Kata laluan bot untuk nama bot \"$1\" bagi pengguna \"$2\" telah dipadam.",
-       "botpasswords-newpassword": "Kata laluan baru untuk log masuk dengan <strong>$1</strong> adalah <strong>$2</strong>. <em>Sila catatkan ini untuk rujukan masa depan.</em> <br> (Untuk bots lama yang memerlukan nama log masuk untuk menjadi sama dengan nama pengguna akhirnya, anda juga boleh menggunakan <strong>$3</strong> sebagai nama pengguna dan <strong>$4</strong> sebagai kata laluan.)",
+       "botpasswords-newpassword": "Kata laluan baru untuk log masuk dengan <strong>$1</strong> adalah <strong>$2</strong>. <em>Sila catatkan ini untuk rujukan masa depan.</em> <br> (Untuk bot-bot lama yang memerlukan nama log masuk agar sama dengan nama pengguna akhirnya, anda juga boleh menggunakan <strong>$3</strong> sebagai nama pengguna dan <strong>$4</strong> sebagai kata laluan.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider tidak tersedia.",
        "botpasswords-restriction-failed": "Bot sekatan kata laluan menghalang log masuk ini.",
        "botpasswords-invalid-name": "Nama pengguna yang dinyatakan tidak mengandungi pemisah kata laluan bot (\"$1\").",
        "hr_tip": "Garis melintang (gunakan dengan hemat)",
        "summary": "Ringkasan:",
        "subject": "Perkara:",
-       "minoredit": "Ini adalah suntingan kecil",
+       "minoredit": "Ini ialah suntingan kecil",
        "watchthis": "Pantau laman ini",
        "savearticle": "Paparkan Laman",
        "publishpage": "Terbitkan",
        "preview": "Pralihat",
        "showpreview": "Paparkan pralihat",
        "showdiff": "Lihat perubahan",
-       "blankarticle": "<strong>Amaran:</strong> Laman yang anda sedang menciptakan adalah kosong.\nJika akan menklik \"$1\" sekali lagi, laman ini akan diciptakan tanpa sebarang kandungan.",
+       "blankarticle": "<strong>Amaran:</strong> Laman yang sedang anda ciptakan adalah kosong.\nJika anda menklik \"$1\" sekali lagi, laman ini akan diciptakan tanpa sebarang kandungan.",
        "anoneditwarning": "<strong>Amaran:</strong> Anda tidak log masuk. Alamat IP anda akan disiarkan kepada umum jika anda membuat sebarang suntingan. Jika anda <strong>[$1 log masuk]</strong> atau <strong>[$2 membuka akaun]</strong>, suntingan anda akan diatribusikan kepada nama pengguna anda di samping manfaat-manfaat lain.",
        "anonpreviewwarning": "''Anda belum log masuk. Jika anda menyimpan laman ini, alamat IP anda akan direkodkan dalam sejarah penyuntingan laman ini.''",
        "missingsummary": "'''Peringatan:''' Anda tidak menyatakan ringkasan suntingan. Klik '''Simpan''' sekali lagi untuk menyimpan suntingan ini tanpa ringkasan.",
        "accmailtext": "Kata laluan janaan rawak untuk [[User talk:$1|$1]] telah dikirim kepada $2. Anda boleh menukarnya di halaman ''[[Special:ChangePassword|tukar kata laluan]]'' sebaik sahaja log masuk.",
        "newarticle": "(Baru)",
        "newarticletext": "Anda telah mengikuti pautan ke laman yang belum wujud.\nUntuk mencipta laman ini, sila taip dalam kotak di bawah\n(lihat [$1 laman bantuan] untuk maklumat lanjut).\nJika anda tiba di sini secara tak sengaja, hanya klik butang '''back''' pada pelayar anda.",
-       "anontalkpagetext": "----''Ini ialah laman perbincangan bagi pengguna tanpa nama yang belum membuka akaun atau tidak log masuk.\nOleh itu kami terpaksa menggunakan alamat IP untuk mengenal pasti pengguna tersebut. Alamat IP ini boleh dikongsi oleh ramai pengguna.\nSekiranya anda adalah seorang pengguna tanpa nama dan berasa bahawa komen yang tidak kena mengena telah ditujukan kepada anda, sila [[Special:CreateAccount|buka akaun baru]] atau [[Special:UserLogin|log masuk]] untuk mengelakkan sebarang kekeliruan dengan pengguna tanpa nama yang lain.''",
+       "anontalkpagetext": "----''Ini ialah laman perbincangan bagi pengguna tanpa nama yang belum membuka akaun atau tidak log masuk.\nOleh itu, kami terpaksa menggunakan alamat IP untuk mengenal pasti pengguna tersebut. Alamat IP ini boleh dikongsi oleh ramai pengguna.\nSekiranya anda ialah seorang pengguna tanpa nama dan berasa bahawa komen yang tidak kena-mengena telah ditujukan kepada anda, sila [[Special:CreateAccount|buka akaun baru]] atau [[Special:UserLogin|log masuk]] untuk mengelakkan sebarang kekeliruan dengan pengguna tanpa nama yang lain.''",
        "noarticletext": "Laman ini buat masa sekarang tidak berteks. Anda boleh [[Special:Search/{{PAGENAME}}|cari tajuk bagi laman ini]] dalam laman-laman lain, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} cari log-log yang berkaitan], atau [{{fullurl:{{FULLPAGENAME}}|action=edit}} sunting laman ini]</span>.",
        "noarticletext-nopermission": "Tiada teks dalam laman ini ketika ini.\nAnda boleh [[Special:Search/{{PAGENAME}}|mencari tajuk laman ini]] dalam laman lain,\natau <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} mencari log yang berkaitan]</span>.",
        "missing-revision": "Semakan #$1 pada halaman \"{{FULLPAGENAME}}\" tidak wujud.\n\nHal ini biasanya disebabkan oleh pautan sejarah yang lapuk ke halaman yang sudah dihapuskan.\nButirannya boleh didapati di [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} log penghapusan].",
        "nonunicodebrowser": "'''AMARAN: Pelayar anda tidak mematuhi Unicode. Aksara-aksara bukan ASCII akan dipaparkan dalam kotak sunting sebagai kod perenambelasan.'''",
        "editingold": "'''AMARAN: Anda sedang\nmenyunting sebuah semakan yang sudah ketinggalan zaman.\nJika anda menyimpannya, sebarang perubahan yang dibuat selepas tarikh semakan ini akan hilang.'''",
        "yourdiff": "Perbezaan",
-       "copyrightwarning": "Sila ambil perhatian bahawa semua sumbangan kepada {{SITENAME}} akan dikeluarkan di bawah $2 (lihat $1 untuk butiran lanjut). Jika anda tidak mahu tulisan anda disunting sewenang-wenangnya oleh orang lain dan diedarkan secara bebas, maka jangan kirim di sini.<br />\nAnda juga berjanji bahawa ini adalah hasil kerja tangan anda sendiri, atau disalin daripada domain awam atau mana-mana sumber bebas lain.\n'''JANGAN KIRIM KARYA HAK CIPTA ORANG LAIN TANPA KEBENARAN!'''",
-       "copyrightwarning2": "Sila ambil perhatian bahawa semua sumbangan terhadap {{SITENAME}} boleh disunting, diubah, atau dipadam oleh penyumbang lain. Jika anda tidak mahu tulisan anda disunting sewenang-wenangnya, maka jangan kirim di sini.<br />\nAnda juga berjanji bahawa ini adalah hasil kerja tangan anda sendiri, atau\ndisalin daripada domain awam atau mana-mana sumber bebas lain (lihat $1 untuk butiran lanjut).\n'''JANGAN KIRIM KARYA HAK CIPTA ORANG LAIN TANPA KEBENARAN!'''",
+       "copyrightwarning": "Sila ambil perhatian bahawa semua sumbangan kepada {{SITENAME}} akan dikeluarkan di bawah $2 (lihat $1 untuk butiran lanjut). Jika anda tidak mahu tulisan anda disunting sewenang-wenangnya oleh orang lain dan diedarkan secara bebas, maka jangan kirim di sini.<br />\nAnda juga berjanji bahawa ini ialah hasil kerja tangan anda sendiri, atau disalin daripada domain awam atau mana-mana sumber bebas lain.\n'''JANGAN KIRIM KARYA HAK CIPTA ORANG LAIN TANPA KEBENARAN!'''",
+       "copyrightwarning2": "Sila ambil perhatian bahawa semua sumbangan terhadap {{SITENAME}} boleh disunting, diubah, atau dipadam oleh penyumbang lain. Jika anda tidak mahu tulisan anda disunting sewenang-wenangnya, maka jangan kirim di sini.<br />\nAnda juga berjanji bahawa ini ialah hasil kerja tangan anda sendiri, atau\ndisalin daripada domain awam atau mana-mana sumber bebas lain (lihat $1 untuk butiran lanjut).\n'''JANGAN KIRIM KARYA HAK CIPTA ORANG LAIN TANPA KEBENARAN!'''",
        "editpage-cannot-use-custom-model": "Model kandungan laman ini tidak boleh diubah.",
        "longpageerror": "'''Ralat: Teks yang anda serahkan itu panjangnya {{PLURAL:$1|1|$1}} kilobait, iaitu lebih panjang daripada had maksimum {{PLURAL:$2|1|$2}} kilobait.'''\nOleh itu, ia tidak boleh disimpan.",
        "readonlywarning": "'''Amaran: Pangkalan data ini dikunci untuk tujuan penyelenggaraan , maka anda tidak akan dapat menyimpan suntingan anda buat sekarang.'''\nAnda boleh menyalin tampal teks anda pada fail teks dan menyimpannya untuk lain kali.\n\nPenyelia yang menguncinya memberikan penjelasan ini: $1",
        "revdel-restore": "Tukar kebolehnampakan",
        "pagehist": "Sejarah laman",
        "deletedhist": "Sejarah yang dihapuskan",
-       "revdelete-hide-current": "Ralat menyembunyikan item bertarikh $2, $1: ini adalah versi semasa.\nIa tidak dapat disembunyikan.",
+       "revdelete-hide-current": "Ralat menyembunyikan item bertarikh $2, $1: Ini ialah versi semasa.\nIa tidak dapat disembunyikan.",
        "revdelete-show-no-access": "Ralat menunjukkan item bertarikh $2, $1: item ini telah ditanda \"larangan\".\nAnda tidak memiliki capaian padanya.",
        "revdelete-modify-no-access": "Ralat menyunting item bertarikh $2, $1: item ini telah ditanda \"larangan\".\nAnda tidak memiliki capaian padanya.",
        "revdelete-modify-missing": "Ralat menyunting item ID $1: ia tiada dalam pangkalan data!",
        "revdelete-edit-reasonlist": "Ubah sebab-sebab hapus",
        "revdelete-offender": "Pengarang semakan:",
        "suppressionlog": "Log penahanan",
-       "suppressionlogtext": "Berikut adalah senarai penghapusan dan sekatan yang melibatkan kandungan yang telah disembunyikan daripada penyelia.\nLihat [[Special:BlockList|senarai sekatan]] untuk senarai larangan dan sekatan semasa.",
+       "suppressionlogtext": "Berikut ialah senarai penghapusan dan sekatan yang melibatkan kandungan yang telah disembunyikan daripada penyelia.\nLihat [[Special:BlockList|senarai sekatan]] untuk senarai larangan dan sekatan semasa.",
        "mergehistory": "Gabungkan sejarah laman",
        "mergehistory-header": "Anda boleh menggabungkan semua semakan dalam sejarah bagi sesebuah laman sumber ke dalam laman lain.\nSila pastikan bahawa perubahan ini akan mengekalkan kesinambungan sejarah laman.\n\n'''Setidak-tidaknya semakan semasa bagi laman sumber akan ditinggalkan.'''",
        "mergehistory-box": "Gabungkan semakan bagi dua laman:",
        "prefs-help-gender": "Pilihan: Digunakan oleh perisian ini untuk merujuk jantina anda dengan betul. Maklumat ini akan didedahkan kepada awam.",
        "email": "E-mel",
        "prefs-help-realname": "Nama sebenar adalah tidak wajib.\n\nJika dinyatakan, ia akan digunakan untuk mengiktiraf karya anda.",
-       "prefs-help-email": "Alamat e-mail adalah tidak wajib, tapi diperlukan untuk set semula kata laluan jika anda terlupa kata laluan anda.",
+       "prefs-help-email": "Alamat e-mel adalah tidak wajib, tapi diperlukan untuk set semula kata laluan jika anda terlupa kata laluan anda.",
        "prefs-help-email-others": "Anda juga boleh memilih untuk membolehkan pengguna lain menghubungi anda melalui e-mel melalui sebuah pautan pada laman pengguna atau perbincangan anda.\nAlamat e-mel anda tidak didedahkan apabila pengguna lain menghubungi anda.",
        "prefs-help-email-required": "Alamat e-mel adalah wajib.",
        "prefs-info": "Maklumat asas",
        "saveusergroups": "Simpan Kumpulan Pengguna",
        "userrights-groupsmember": "Ahli bagi:",
        "userrights-groupsmember-auto": "Ahli automatik bagi:",
-       "userrights-groups-help": "Anda boleh mengubah keahlian kumpulan bagi pengguna ini:\n* Petak yang bertanda bererti pengguna tersebut adalah ahli kumpulan itu.\n* Petak yang tidak bertanda bererti bahawa pengguna tersebut bukan ahli kumpulan itu.\n* Tanda bintang (*) menandakan bahawa anda tidak boleh melucutkan keahlian pengguna tersebut setelah anda melantiknya, dan begitulah sebaliknya.",
+       "userrights-groups-help": "Anda boleh mengubah keahlian kumpulan bagi pengguna ini:\n* Petak yang bertanda bererti pengguna tersebut ialah ahli kumpulan itu.\n* Petak yang tidak bertanda bererti bahawa pengguna tersebut bukan ahli kumpulan itu.\n* Tanda bintang (*) menandakan bahawa anda tidak boleh melucutkan keahlian pengguna tersebut setelah anda melantiknya, dan begitulah sebaliknya.",
        "userrights-reason": "Sebab:",
        "userrights-no-interwiki": "Anda tidak mempunyai keizinan untuk mengubah hak-hak pengguna di wiki lain.",
        "userrights-nodatabase": "Pangkalan data $1 tiada atau bukan tempatan.",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (lihat juga [[Special:NewPages|senarai laman baru]])",
        "rcfilters-filter-pageedits-description": "Suntingan kandungan wiki, perbincangan, huraian kategori…",
        "rcfilters-filter-logactions-description": "Tindakan pentadbiran, pembuatan akaun, penghapusan halaman, muat naik…",
-       "rcnotefrom": "Yang berikut adalah {{PLURAL:$5|suntingan|suntingan-suntingan}} sejak <strong>$3, $4</strong> (selebihi <strong>$1</strong> dipaparkan).",
+       "rcnotefrom": "Yang berikut ialah {{PLURAL:$5|suntingan|suntingan-suntingan}} sejak <strong>$3, $4</strong> (selebihi <strong>$1</strong> dipaparkan).",
        "rclistfrom": "Paparkan perubahan sejak $3 $2",
        "rcshowhideminor": "$1 suntingan kecil",
        "rcshowhideminor-show": "Paparkan",
        "filetype-mime-mismatch": "Sambungan fail \".$1\" tidak padan dengan jenis MIME fail ($2).",
        "filetype-badmime": "Memuat naik fail jenis MIME \"$1\" adalah tidak dibenarkan.",
        "filetype-bad-ie-mime": "Fail ini tidak boleh dimuat naik kerana Internet Explorer mengesannya sebagai fail jenis \"$1\" yang tidak dibenarkan dan berbahaya.",
-       "filetype-unwanted-type": "'''\".$1\"''' adalah jenis fail yang tidak dikehendaki. {{PLURAL:$3|Jenis|Jenis-jenis}} fail yang diutamakan ialah $2.",
-       "filetype-banned-type": "'''\".$1\"''' adalah {{PLURAL:$4|jenis|jenis-jenis}} fail yang dilarang. {{PLURAL:$3|Jenis|Jenis-jenis}} fail yang dibenarkan ialah $2.",
+       "filetype-unwanted-type": "'''\".$1\"''' ialah jenis fail yang tidak dikehendaki. {{PLURAL:$3|Jenis|Jenis-jenis}} fail yang diutamakan ialah $2.",
+       "filetype-banned-type": "<strong>\".$1\"</strong> ialah {{PLURAL:$4|jenis|jenis-jenis}} fail yang dilarang. {{PLURAL:$3|Jenis|Jenis-jenis}} fail yang dibenarkan ialah $2.",
        "filetype-missing": "Fail ini tidak mempunyai sambungan (contohnya \".jpg\").",
        "empty-file": "Fail yang anda serahkan adalah kosong.",
        "file-too-large": "Fail yang anda serahkan adalah terlalu besar.",
        "fileexists": "Sebuah fail dengan nama ini sudah wujud. Sila semak <strong>[[:$1]]</strong> sekiranya {{GENDER:|anda}} tidak pasti jika anda mahu menukarnya.\n[[$1|thumb]]",
        "filepageexists": "Laman penerangan untuk fail ini telah pun dicipta di <strong>[[:$1]]</strong>, tetapi tiada fail dengan nama ini wujud.\nRingkasan yang anda masukkan tidak akan muncul di laman penerangan tersebut. Untuk memastikannya muncul, anda perlu menyuntingnya secara manual.\n[[$1|thumb]]",
        "fileexists-extension": "Sebuah fail dengan nama yang serupa sudah wujud: [[$2|thumb]]\n* Nama fail yang hendak dimuat naik: <strong>[[:$1]]</strong>\n* Nama fail yang sudah sedia ada: <strong>[[:$2]]</strong>\nAdakah anda mungkin mahu menggunakan nama yang lebih tersendiri?",
-       "fileexists-thumbnail-yes": "Fail ini kelihatan seperti sebuah imej yang telah dikecilkan ''(gambar kenit)''. [[$1|thumb]]\nSila semak fail <strong>[[:$1]]</strong>.\nJika fail yang disemak itu adalah sama dengan yang saiz asal, maka anda tidak perlu memuat naik gambar kenit tambahan.",
-       "file-thumbnail-no": "Nama fail ini bermula dengan <strong>$1</strong>.\nBarangkali ia adalah sebuah imej yang telah dikecilkan ''(gambar kenit)''.\nJika anda memiliki imej ini dalam leraian penuh, sila muat naik fail tersebut. Jika tidak, sila tukar nama fail ini.",
+       "fileexists-thumbnail-yes": "Fail ini kelihatan seperti sebuah imej yang telah dikecilkan <em>(gambar kenit)</em>.\n[[$1|thumb]]\nSila semak fail <strong>[[:$1]]</strong>.\nJika fail yang disemak itu adalah sama dengan yang saiz asal, maka anda tidak perlu memuat naik gambar kenit tambahan.",
+       "file-thumbnail-no": "Nama fail ini bermula dengan <strong>$1</strong>.\nBarangkali ia ialah sebuah imej yang telah dikecilkan <em>(gambar kenit)</em>.\nJika anda memiliki imej ini dalam leraian penuh, sila muat naik fail tersebut. Jika tidak, sila tukar nama fail ini.",
        "fileexists-forbidden": "Sebuah fail dengan nama ini telah pun wujud, dan tidak boleh ditulis ganti. Jika anda masih mahu memuat naik fail ini, sila berundur dan muat naik fail ini dengan nama lain. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Sebuah fail dengan nama ini telah pun wujud dalam gedung fail kongsi. Jika anda masih mahu memuat naik fail ini, sila kembali ke borang muat naik dan gunakan nama lain. [[File:$1|thumb|center|$1]]",
-       "file-exists-duplicate": "Fail ini adalah salinan bagi {{PLURAL:$1|fail|fail-fail}} berikut:",
+       "file-exists-duplicate": "Fail ini ialah salinan bagi {{PLURAL:$1|fail|fail-fail}} berikut:",
        "file-deleted-duplicate": "Sebuah fail yang serupa dengan fail ini ([[:$1]]) telah pun dihapuskan sebelum ini. Anda seharusnya memeriksa sejarah penghapusan fail itu terlebih dahulu sebelum memuat naiknya sekali lagi.",
        "file-deleted-duplicate-notitle": "Satu fail yang seiras dengan fail ini telah dihapuskan dahulu, maka judulnya telah disekat. Anda harus meminta sesiapa yang boleh melihat data fail yang disekat untuk meneliti situasinya sebelum cuba memuat naiknya semula.",
        "uploadwarning": "Amaran muat naik",
        "lockmanager-fail-svr-release": "Selak-selak tidak dapat dikeluarkan di pelayan $1.",
        "zip-file-open-error": "Wujud ralat ketika membuka fail untuk pemeriksaan ZIP.",
        "zip-wrong-format": "Fail yang dinyatakan bukan fail ZIP.",
-       "zip-bad": "Fail ini adalah fail ZIP rosak atau tidak dapat dibaca.\nIa tidak dapat diperiksa dengan betul demi keselamatan.",
-       "zip-unsupported": "Fail ini adalah fail ZIP yang menggunakan ciri-ciri ZIP tidak disokong oleh MediaWiki. \nIa tidak dapat diperiksa dengan betul demi keselamatan.",
+       "zip-bad": "Fail ini ialah fail ZIP rosak atau tidak dapat dibaca.\nIa tidak dapat diperiksa dengan betul demi keselamatan.",
+       "zip-unsupported": "Fail ini ialah fail ZIP yang menggunakan ciri-ciri ZIP tidak disokong oleh MediaWiki. \nIa tidak dapat diperiksa dengan betul demi keselamatan.",
        "uploadstash": "Stor muat naik",
        "uploadstash-summary": "Laman ini menyediakan capaian kepada fail-fail yang dimuat naik (atau sedang dimuat naik) tapi belum diterbitkan ke dalam wiki. Fail-fail ini tidak dapat dilihat oleh sesiapa melainkan pengguna yang memuatnaiknya.",
        "uploadstash-clear": "Bersihkan fail-fail sorokan",
        "nolinkstoimage": "Tiada laman yang mengandungi pautan ke fail ini.",
        "morelinkstoimage": "Lihat [[Special:WhatLinksHere/$1|semua pautan]] ke fail ini.",
        "linkstoimage-redirect": "$1 (lencongan fail) $2",
-       "duplicatesoffile": "{{PLURAL:$1|Fail|$1 buah fail}} berikut adalah salinan bagi fail ini ([[Special:FileDuplicateSearch/$2|butiran lanjut]]):",
+       "duplicatesoffile": "{{PLURAL:$1|Fail|$1 buah fail}} berikut ialah salinan bagi fail ini ([[Special:FileDuplicateSearch/$2|butiran lanjut]]):",
        "sharedupload": "Fail ini daripada $1 dan boleh digunakan oleh projek lain.",
        "sharedupload-desc-there": "Fail ini dari $1 dan mungkin digunakan oleh projek lain.\nSila lihat [$2 laman penerangan fail] untuk maklumat lanjut.",
        "sharedupload-desc-here": "Fail ini dari $1 dan mungkin digunakan oleh projek lain.\nPenerangan pada [$2 laman penerangan failnya] di sana ditunjukkan di bawah.",
        "activeusers-from": "Tunjukkan pengguna bermula pada:",
        "activeusers-noresult": "Tiada pengguna dijumpai.",
        "listgrouprights": "Hak kumpulan pengguna",
-       "listgrouprights-summary": "Berikut adalah senarai kumpulan pengguna yang ditubuhkan di wiki ini, dengan hak-hak mereka masing-masing.\nMungkin terdapat [[{{MediaWiki:Listgrouprights-helppage}}|maklumat tambahan]] mengenai setiap hak.",
+       "listgrouprights-summary": "Berikut ialah senarai kumpulan pengguna yang ditubuhkan di wiki ini, dengan hak-hak mereka masing-masing.\nMungkin terdapat [[{{MediaWiki:Listgrouprights-helppage}}|maklumat tambahan]] mengenai setiap hak.",
        "listgrouprights-key": "Petunjuk:\n* <span class=\"listgrouprights-granted\">Hak ditunaikan</span>\n* <span class=\"listgrouprights-revoked\">Hak dibatalkan</span>",
        "listgrouprights-group": "Kumpulan",
        "listgrouprights-rights": "Hak",
        "trackingcategories-name": "Nama pesanan",
        "trackingcategories-desc": "Kriteria kemasukan kategori",
        "noindex-category-desc": "Laman tidak didaftar oleh robot kerana ia mempunyai kata ajaib <code><nowiki>__NOINDEX__</nowiki></code> padanya dan terdapat dalam ruang nama di mana tanda tersebut adalah dibenarkan.",
-       "index-category-desc": "Laman mempunyai <code><nowiki>__INDEX__</nowiki></code> padanya (dan terdapat dalam ruang nama di mana tanda tersebut dibenarkan), dan oleh itu adalah didaftar oleh robot di mana ia biasanya tidak akan.",
+       "index-category-desc": "Laman mempunyai <code><nowiki>__INDEX__</nowiki></code> padanya (dan terdapat dalam ruang nama di mana tanda tersebut dibenarkan), dan oleh itu ia didaftar oleh robot bilamana ia biasanya tidak akan didaftar.",
        "post-expand-template-inclusion-category-desc": "Saiz laman melebihi <code>$wgMaxArticleSize</code> setelah semua templat telah dikembangkan; oleh itu beberapa templat tidak dikembangkan.",
        "post-expand-template-argument-category-desc": "Laman menjadi lebih besar daripada <code>$wgMaxArticleSize</code> setelah mengembangkan sebuah hujah templat (sebarang dalam tiga kurungan, seperti <code>{{{Foo}}}</code>.)",
        "expensive-parserfunction-category-desc": "Laman menggunakan terlalu banyak fungsi parser yang mahal (seperti <code>#ifexist</code>). Lihat [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgExpensiveParserFunctionLimit Manual:$wgExpensiveParserFunctionLimit].",
        "rollbacklinkcount": "mengundurkan $1 {{PLURAL:$1|suntingan}}",
        "rollbacklinkcount-morethan": "mengundurkan lebih daripada $1 {{PLURAL:$1|suntingan}}",
        "rollbackfailed": "Pengunduran gagal",
-       "cantrollback": "Suntingan tersebut tidak dapat dibalikkan: penyumbang terakhir adalah satu-satunya pengarang bagi rencana ini.",
+       "cantrollback": "Suntingan tersebut tidak dapat dibalikkan: penyumbang terakhir ialah satu-satunya pengarang bagi rencana ini.",
        "alreadyrolled": "Suntingan terakhir bagi [[:$1]] oleh [[Pengguna:$2|$2]] ([[Perbincangan pengguna:$2|bincang]]{{int:pipe-separator}}[[Khas:Sumbangan/$2|{{int:contribslink}}]]) tidak dapat dibalikkan; seorang pengguna lain sudahpun menyunting atau membalikkan laman itu.\n\nSuntingan terakhir kepada laman ini telah dibuat oleh [[Pengguna:$3|$3]] ([[Perbincangan pengguna:$3|bincang]]{{int:pipe-separator}}[[Khas:Sumbangan/$3|{{int:contribslink}}]]).",
        "editcomment": "Ringkasan sutingan: <em>$1</em>.",
        "revertpage": "Membalikkan suntingan oleh [[Special:Contributions/$2|$2]] ([[User talk:$2|Perbincangan]]) kepada versi terakhir oleh [[User:$1|$1]]",
        "protect_expiry_old": "Waktu tamat telah berlalu.",
        "protect-unchain-permissions": "Aktifkan pilihan perlindungan selanjutnya",
        "protect-text": "Anda boleh melihat dan menukar peringkat perlindungan bagi laman '''$1'''.",
-       "protect-locked-blocked": "Anda telah disekat, justeru tidak boleh menukar peringkat perlindungan.\nIni adalah tetapan semasa bagi laman '''$1''':",
-       "protect-locked-dblock": "Anda tidak boleh menukar peringkat perlindungan kerana pangkalan data sedang dikunci.\nIni adalah tetapan semasa bagi laman '''$1''':",
-       "protect-locked-access": "Anda tidak mempunyai keizinan untuk menukar peringkat perlindungan.\nIni adalah tetapan semasa bagi laman '''$1''':",
+       "protect-locked-blocked": "Anda telah disekat, justeru tidak boleh menukar peringkat perlindungan.\nIni ialah tetapan semasa bagi laman '''$1''':",
+       "protect-locked-dblock": "Anda tidak boleh menukar peringkat perlindungan kerana pangkalan data sedang dikunci.\nIni ialah tetapan semasa bagi laman '''$1''':",
+       "protect-locked-access": "Anda tidak mempunyai keizinan untuk menukar peringkat perlindungan.\nIni ialah tetapan semasa bagi laman '''$1''':",
        "protect-cascadeon": "Laman ini dilindungi kerana ia terkandung dalam {{PLURAL:$1|laman|laman-laman}} berikut, yang dilindungi secara melata. Penukaran peringkat perlindungan laman ini tidak akan menjejaskan perlindungan melata tersebut.",
        "protect-default": "Benarkan semua pengguna",
        "protect-fallback": "Benarkan pengguna yang berizin \"$1\" sahaja",
        "change-blocklink": "ubah sekatan",
        "contribslink": "sumb.",
        "emaillink": "hantar e-mel",
-       "autoblocker": "Disekat secara automatik kerana alamat IP anda baru digunakan oleh \"[[User:$1|$1]]\". Sebab yang diberi adalah: \"$2\"",
+       "autoblocker": "Disekat secara automatik kerana alamat IP anda baru digunakan oleh \"[[User:$1|$1]]\". Sebab yang diberi ialah: \"$2\"",
        "blocklogpage": "Log sekatan",
        "blocklog-showlog": "Pengguna ini pernah disekat sebelum ini. Log sekatan disediakan di bawah sebagai rujukan:",
        "blocklog-showsuppresslog": "Pengguna ini pernah disekat dan tersembunyi sebelum ini.\nLog sekatan disediakan di bawah sebagai rujukan:",
        "blocklogentry": "menyekat [[$1]] sehingga $2 $3",
        "reblock-logentry": "menukar tetapan sekatan [[$1]] yang tamat pada $2 $3",
-       "blocklogtext": "Ini adalah log bagi tindakan menyekat dan menyahsekat pengguna.\nAlamat-alamat IP yang disekat secara automatik tidak disenaraikan di sini.\nSila lihat juga [[Special:BlockList|senarai sekatan]] untuk senarai larangan dan sekatan yang sedang berkuatkuasa.",
+       "blocklogtext": "Ini ialah log bagi tindakan menyekat dan menyahsekat pengguna.\nAlamat-alamat IP yang disekat secara automatik tidak disenaraikan di sini.\nSila lihat juga [[Special:BlockList|senarai sekatan]] untuk senarai larangan dan sekatan yang sedang berkuat kuasa.",
        "unblocklogentry": "menyahsekat $1",
        "block-log-flags-anononly": "pengguna tanpa nama sahaja",
        "block-log-flags-nocreate": "pembukaan akaun dimatikan",
        "proxyblockreason": "Alamat IP anda telah disekat kerana ia merupakan proksi terbuka.\nSila hubungi penyedia perkhidmatan Internet anda atau pihak sokongan teknikal dan beritahu mereka mengenai masalah keselamatan yang berat ini.",
        "sorbsreason": "Alamat IP anda telah disenaraikan sebagai proksi terbuka dalam DNSBL yang digunakan oleh {{SITENAME}}.",
        "sorbs_create_account_reason": "Alamat IP anda telah disenaraikan sebagai proksi terbuka dalam DNSBL yang digunakan oleh {{SITENAME}}. Oleh itu, anda tidak dibenarkan membuka akaun baru.",
-       "xffblockreason": "Alamat IP yang terdapat dalam pengepala X-Forwarded-For, sama ada milik anda ataupun pelayan proksi yang anda gunakan, telah disekat. Sebab asal sekatan adalah: $1",
+       "xffblockreason": "Alamat IP yang terdapat dalam pengepala X-Forwarded-For, sama ada milik anda ataupun pelayan proksi yang anda gunakan, telah disekat. Sebab asal sekatan ialah: $1",
        "cant-see-hidden-user": "Pengguna yang anda cuba sekat telahpun disekat dan tersorok.\nMemandangkan anda tidak mempunyai hak untuk menyorokkan pengguna, anda tidak boleh melihat atau menyunting sekatan pengguna tersebut.",
        "ipbblocked": "Anda tidak boleh menyekat atau menyahsekat pengguna lain kerana anda sendiri telah disekat",
        "ipbnounblockself": "Anda tidak dibenarkan menyahsekat diri sendiri",
        "tooltip-ca-nstab-main": "Lihat laman kandungan",
        "tooltip-ca-nstab-user": "Lihat laman pengguna",
        "tooltip-ca-nstab-media": "Lihat laman media",
-       "tooltip-ca-nstab-special": "Ini adalah laman khas yang tidak boleh disunting.",
+       "tooltip-ca-nstab-special": "Ini ialah laman khas yang tidak boleh disunting.",
        "tooltip-ca-nstab-project": "Lihat laman projek",
        "tooltip-ca-nstab-image": "Lihat laman imej",
        "tooltip-ca-nstab-mediawiki": "Lihat pesanan sistem",
        "saturday-at": "Sabtu $1",
        "sunday-at": "Ahad $1",
        "yesterday-at": "Semalam $1",
-       "bad_image_list": "Berikut adalah format yang digunakan:\n\nHanya item senarai (baris yang dimulakan dengan *) diambil kira. Pautan pertama pada sesebuah baris mestilah merupakan pautan ke sebuah imej rosak.\nSebarang pautan berikutnya pada baris yang sama dikira sebagai pengecualian (rencana yang dibenarkan disertakan imej).",
+       "bad_image_list": "Berikut ialah format yang digunakan:\n\nHanya item senarai (baris yang dimulakan dengan *) diambil kira. Pautan pertama pada sesebuah baris mestilah merupakan pautan ke sebuah imej rosak.\nSebarang pautan berikutnya pada baris yang sama dikira sebagai pengecualian (rencana yang dibenarkan disertakan imej).",
        "metadata": "Metadata",
        "metadata-help": "Fail ini mengandungi maklumat tambahan daripada kamera digital atau pengimbas yang digunakan untuk menghasilkannya. Jika fail ini telah diubah suai daripada rupa asalnya, beberapa butiran dalam maklumat ini mungkin sudah tidak relevan.",
        "metadata-expand": "Tunjukkan butiran penuh",
        "version-poweredby-others": "penyumbang-penyumbang lain",
        "version-poweredby-translators": "para penterjemah translatewiki.net",
        "version-credits-summary": "Kami ingin mengucapkan sekalung budi kepada mereka yang berikut atas sumbangan mereka keada [[Special:Version|MediaWiki]].",
-       "version-license-info": "MediaWiki adalah perisian bebas; anda boleh mengedarkannya semula dan/atau mengubah suainya di bawah terma-terma Lesen Awam GNU sebagai mana yang telah diterbitkan oleh Yayasan Perisian Bebas, sama ada versi 2 bagi Lesen tersebut, atau (berdasarkan pilihan anda) mana-mana versi selepasnya.\n\nMediaWiki diedarkan dengan harapan bahawa ia berguna, tetapi TANPA SEBARANG WARANTI; hatta waranti yang tersirat bagi KEBOLEHDAGANGAN mahupun KESESUAIAN UNTUK TUJUAN TERTENTU. Sila lihat Lesen Awam GNU untuk butiran lanjut.\n\nAnda patut telah menerima [{{SERVER}}{{SCRIPTPATH}}/COPYING sebuah salinan bagi Lesen Awam GNU] bersama-sama dengan atur cara ini; jika tidak, tulis ke Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA atau [//www.gnu.org/licenses/old-licenses/gpl-2.0.html baca dalam talian].",
+       "version-license-info": "MediaWiki ialah perisian bebas; anda boleh mengedarkannya semula dan/atau mengubah suainya di bawah terma-terma Lesen Awam GNU sebagai mana yang telah diterbitkan oleh Yayasan Perisian Bebas, sama ada versi 2 bagi Lesen tersebut, atau (berdasarkan pilihan anda) mana-mana versi selepasnya.\n\nMediaWiki diedarkan dengan harapan bahawa ia berguna, tetapi TANPA SEBARANG WARANTI; hatta waranti yang tersirat bagi KEBOLEHDAGANGAN mahupun KESESUAIAN UNTUK TUJUAN TERTENTU. Sila lihat Lesen Awam GNU untuk butiran lanjut.\n\nAnda patut telah menerima [{{SERVER}}{{SCRIPTPATH}}/COPYING sebuah salinan bagi Lesen Awam GNU] bersama-sama dengan atur cara ini; jika tidak, tulis kepada Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA atau [//www.gnu.org/licenses/old-licenses/gpl-2.0.html baca dalam talian].",
        "version-software": "Perisian yang dipasang",
        "version-software-product": "Produk",
        "version-software-version": "Versi",
        "fileduplicatesearch-noresults": "Tidak ada gambar-gambar dengan nama \"$1\" dijumpai.",
        "specialpages": "Laman khas",
        "specialpages-note-top": "Petunjuk",
-       "specialpages-note": "* Laman khas biasa.\n* <span class=\"mw-specialpagerestricted\">Laman khas terhad.</span>",
        "specialpages-group-maintenance": "Laporan penyenggaraan",
        "specialpages-group-other": "Laman khas lain",
        "specialpages-group-login": "Log masuk / buka akaun",
        "right-pagelang": "Mengubah bahasa laman",
        "action-pagelang": "mengubah bahasa laman",
        "log-name-pagelang": "Log perubahan bahasa",
-       "log-description-pagelang": "Ini adalah log untuk perubahan-perubahan bahasa laman.",
+       "log-description-pagelang": "Ini ialah log untuk perubahan-perubahan bahasa laman.",
        "logentry-pagelang-pagelang": "$1 telah {{GENDER:$2|mengubahkan}} bahasa untuk laman $3 dari $4 ke $5.",
        "default-skin-not-found": "Maaf, tidak terdapat rupa asali wiki anda yang tertakrif dalam <code dir=\"ltr\">$wgDefaultSkin</code> sebagai <code>$1</code>.\n\nNampaknya pemasangan anda merangkumi {{PLURAL:$4|rupa|rupa-rupa}} yang berikut. Rujuk [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Skin configuration] untuk cara-cara membolehkan {{PLURAL:$4|rupa tersebut|rupa-rupa tersebut serta memilih rupa asali}}.\n\n$2\n\n; Jika anda baru memasang MediaWiki:\n: Mungkin anda memasangnya dari git, atau terus dari kod sumber dengan menggunakan suatu kaedah lain. Perkara ini dijangka. Cuba pasang beberapa rupa dari [https://www.mediawiki.org/wiki/Category:All_skins direktori rupa mediawiki.org], dengan:\n:* Memuat turun [https://www.mediawiki.org/wiki/Download pemasang tarball] yang datang dengan beberapa rupa dan sambungan. Anda boleh menyalin-tampal direktori <code>skins/</code> daripadanya.\n:* Memuatkan satu persatu tarball rupa dari [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Menggunakan Git untuk memuat turun rupa].\n: Tindakan ini seharusnya tidak mengganggu repositori git anda jika anda seorang pembangun MediaWiki.\n\n; Jika anda baru menaik taraf MediaWiki:\n: MediaWiki 1.24 ke atas tidak lagi membolehkan  secara automatik rupa-rupa yang terpasang dari luaran (rujuk [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Manual: Skin autodiscovery]). Anda boleh menampalkan {{PLURAL:$5|baris|baris-baris}} berikut kepada <code>LocalSettings.php</code> untuk membolehkan {{PLURAL:$5|rupa|semua rupa}} yang terpasang:\n\n<pre dir=\"ltr\">$3</pre>\n\n; Jika anda baru mengubahsuai <code>LocalSettings.php</code>:\n: Semak nama-nama rupa untuk kesilapan ejaan.",
        "default-skin-not-found-no-skins": "Maaf, tidak terdapat rupa asali wiki anda yang tertakrif dalam <code dir=\"ltr\">$wgDefaultSkin</code> sebagai <code>$1</code>.\n\nTiadanya rupa yang terpasang.\n\n; Jika anda baru memasang atau menaik taraf MediaWiki:\n: Mungkin anda memasangnya dari git, atau terus dari kod sumber dengan menggunakan suatu kaedah lain. Perkara ini dijangka. MediaWiki 1.24 ke atas tidak menyertakan sebarang rupa dalam repositori utama.  Cuba pasang beberapa rupa dari [https://www.mediawiki.org/wiki/Category:All_skins direktori rupa mediawiki.org], dengan:\n:* Memuatkan tarball rupa satu persatu dari [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Menggunakan Git untuk memuat turun rupa].\n: Tindakan ini seharusnya tidak mengganggu repositori git anda jika anda seorang pembangun MediaWiki. Rujuk [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Skin configuration] untuk cara-cara membolehkan penggunaan rupa-rupa serta memilih rupa asali.",
index 116b216..911fc80 100644 (file)
@@ -41,7 +41,7 @@
        "tog-enotifminoredits": "Famme na masciata mail pure quanno se fanno cagnamiente piccerille 'e paggene e files",
        "tog-enotifrevealaddr": "Fa' vedé 'o ndirizzo mail ncopp'e mmasciate 'e notifica",
        "tog-shownumberswatching": "Fa' vedé 'o nummero d'utente che teneno 'a paggena cuntrullata",
-       "tog-oldsig": "Firma 'e mmo:",
+       "tog-oldsig": "'A firma vosta (mo' mo'):",
        "tog-fancysig": "Piglia 'a firma comme fosse nu wikitesto (senza fà link automatico)",
        "tog-uselivepreview": "Abbìa 'o \"Live preview\"",
        "tog-forceeditsummary": "Chiere a mme quanno se sta azzeccanno nu campo oggetto abbacante",
@@ -58,7 +58,7 @@
        "tog-showhiddencats": "Fa' vedé 'e categurie annascunnute",
        "tog-norollbackdiff": "Nun fà vedé 'o cunfronto nfra verziune quanno se fà nu rollback ('o torna arreto)",
        "tog-useeditwarning": "Famme sapé quanno lasso na paggena 'e mudifeca senza sarvà 'e cagnamiente",
-       "tog-prefershttps": "Usa sempe na connessione sicura quanno s'accummincia sessione",
+       "tog-prefershttps": "Usa sempe na connessione sicura pe' tramente ca s'accummincia sessione",
        "underline-always": "Sèmpe",
        "underline-never": "Màje",
        "underline-default": "Tiene sempe le mpostazzione d' 'o navigatóre",
        "newwindow": "(s'arape n'ata fenèsta)",
        "cancel": "Scancèlla",
        "moredotdotdot": "Cchiù...",
-       "morenotlisted": "Chisto elenco nun è cumpreto.",
+       "morenotlisted": "Chisto elenco putesse nun essere cumpleto sano sano.",
        "mypage": "Paggena",
        "mytalk": "'E chiàcchieriate mmie",
        "anontalk": "Chiacchierate",
        "navigation": "Navigazzione",
        "and": "&#32;e",
-       "qbfind": "Truòva",
-       "qbbrowse": "Sfoglia",
-       "qbedit": "Càgna",
-       "qbpageoptions": "Chesta paggena",
-       "qbmyoptions": "'E ppaggene mie",
        "faq": "FAQ",
-       "faqpage": "Project:Domanne frequente",
        "actions": "Azione",
        "namespaces": "Namespace",
        "variants": "Variante",
        "searcharticle": "Vàje",
        "history": "Verziune 'e primma",
        "history_short": "Cronologgia",
+       "history_small": "cronologgia",
        "updatedmarker": "cagnamiénte 'e ll'urdema visita d' 'a mia",
        "printableversion": "Verzione pe' stampa",
        "permalink": "Jonta permanente",
        "edit-local": "Càgna descrizione lucale",
        "create": "Crèa",
        "create-local": "Azzecca descrizione lucale",
-       "editthispage": "Càgna chesta paggena",
-       "create-this-page": "Crèa sta paggena",
        "delete": "Scancèlla",
-       "deletethispage": "Scancèlla chésta paggena",
-       "undeletethispage": "Arrepiglia chista paggena",
        "undelete_short": "Arremedia {{PLURAL:$1|na verziona|$1 vverziune}}",
        "viewdeleted_short": "Vide {{PLURAL:$1|nu cagnamiénto scancellato|$1 cagnamiénte scancellate}}",
        "protect": "Prutegge",
        "protect_change": "càgna",
-       "protectthispage": "Ferma chesta paggena",
        "unprotect": "Càgna prutezzione",
-       "unprotectthispage": "Càgna prutezzione 'e chesta paggena",
        "newpage": "Paggena nòva",
-       "talkpage": "Paggena 'e chiàcchiera",
        "talkpagelinktext": "Chiàcchiera",
        "specialpage": "Paggena speciàle",
        "personaltools": "Strumiente perzonale",
-       "articlepage": "Vere a paggena e contenuto",
        "talk": "Chiàcchiera",
        "views": "Visite",
        "toolbox": "Strumiente",
-       "userpage": "Vere a paggena utente",
-       "projectpage": "Vere a paggena 'e servizio",
+       "tool-link-userrights": "Càgna gruppe {{GENDER:$1|utente}}",
+       "tool-link-userrights-readonly": "Vire gruppe {{GENDER:$1|utente}}",
+       "tool-link-emailuser": "Manna na masciata email a st'{{GENDER:$1|utente}}",
        "imagepage": "Vere a paggena d' 'o file",
        "mediawikipage": "Vere 'a mmasciata",
        "templatepage": "Vere 'o template",
        "redirectedfrom": "(Redirect 'a $1)",
        "redirectpagesub": "Paggena 'e redirect",
        "redirectto": "Reindirizza a:",
-       "lastmodifiedat": "Urdemo cagnamiénto pe' a paggena: $2, $1.",
+       "lastmodifiedat": "Sta paggena fuje, n'urdema vota, cagnàta 'o $1, 'e $2.",
        "viewcount": "Chesta paggena è stata liggiùta {{PLURAL:$1|una vòta|$1 vòte}}.",
        "protectedpage": "Paggena prutetta",
        "jumpto": "Vaje a:",
        "createacct-another-username-ph": "'Nserisce 'o nomme utente",
        "yourpassword": "Password:",
        "userlogin-yourpassword": "Password",
-       "userlogin-yourpassword-ph": "'Nserisce 'a toja password",
+       "userlogin-yourpassword-ph": "Nzertàte 'a password vuosta",
        "createacct-yourpassword-ph": "'Nserisce 'na password",
        "yourpasswordagain": "Ripete 'a password:",
        "createacct-yourpasswordagain": "Cunferma password",
        "eauthentsent": "Na mmasciata 'e conferma t'è stata mannata a l'indirizzo e-mail nzignàto.\nApprimm' 'e te mannà n'atu mail, hè 'a stà 'a ffà 'e struzione dint'a l'e-mail, pe' cunfermà ca 'o cunto fosse d' 'o tujo overo.",
        "throttled-mailpassword": "S'è mannata na mail pe te' riabbià 'a password 'a meno 'e {{PLURAL:$1|n'ora|$1 ore}}.\nPe' ce sparagnà abbuse, 'a funzione 'e riabbiamento d' 'a password se può usa sulamente na vota ogne {{PLURAL:$1|ora|$1 ore}}.",
        "mailerror": "Errore pe' tramente ca se mannava na mmasciata: $1",
-       "acct_creation_throttle_hit": "{{PLURAL:$1|1 registrazzione è già stata effettuata|$1 registrazzione song già state effettuate}} 'e qualcuno cu 'o tujo stisso innerezzo IP dint'ô urdemo juorno: è 'o massimo cunsentito 'n chisto periodo 'e tiempo.\nPerciò, 'e utente ca ausano chisto innerezzo IP nun possono registrarse ppe 'o mumiento.",
+       "acct_creation_throttle_hit": "'E vvisite a sta wiki ausanno l'IP tuoja se so' mise a crià {{PLURAL:$1|1 registrazzione|$1 registrazzione}} int'a ll'urdeme juorne ($2), chesto fosse 'o massimo premmesso pe' stu periodo 'e tiempo.\nPerciò, 'e utente ca ausano chisto innerezzo IP nun se ponno riggistrà ancora mò mò.",
        "emailauthenticated": "'O ndirizzo email è stato cunfermato 'o $2 a 'e $3.",
        "emailnotauthenticated": "'O ndirizzo 'e posta elettronica nun è stat'ancora cunfermato.\nNun se mannarranno mmasciate e-mail p' ' funzione ccà abbascio.",
        "noemailprefs": "Avite 'a specificà nu ndirizzo e-mail pe ll'attivà sti funzione.",
        "botpasswords-label-delete": "Scancèlla",
        "botpasswords-label-resetpassword": "Riabbìa 'a password",
        "botpasswords-label-grants": "Assegnaziune apprecabbele:",
-       "botpasswords-help-grants": "Ogne assegnazione dà acciesso a 'e deritte utente elencate ca n'utenza avesse già. Vedite 'a [[Special:ListGrants|tabbella 'e ll'assegnaziune]] pe' ne mòvere cchiù nfurmaziune.",
+       "botpasswords-help-grants": "L'assegnazione premmettessero ausà deritte utente elencate ca n'utenza avesse già. Premmettenno st'assegnazione ccà nun è ca ve facesse trasì int'a sti deritte, pecché 'o cunto vuosto nun 'e tenisse pe' n'atu mezzo. Vedite 'a [[Special:ListGrants|tabbella 'e ll'assegnaziune]] pe' ne mòvere cchiù nfurmaziune.",
        "botpasswords-label-grants-column": "Assegnaziune date",
        "botpasswords-bad-appid": "'O nomme bot \"$1\" nun è bbuono.",
        "botpasswords-insert-failed": "Nun se pò azzeccà 'o nomme bot \"$1\". Fosse stato già azzeccato?",
        "botpasswords-updated-body": "'A password bot \"$1\" 'a ll'utente \"$2\" fuje agghiurnata.",
        "botpasswords-deleted-title": "Password bot scancellata",
        "botpasswords-deleted-body": "'A password bot \"$1\" 'a ll'utente \"$2\" è stata scancellata.",
-       "botpasswords-newpassword": "'A password nòva pe' puté trasì cu <strong>$1</strong> è <strong>$2</strong>. <em>Pe' piacere signatevello chesto pe' ve ffà conzurtaziune future.</em>",
+       "botpasswords-newpassword": "'A password nòva pe' puté trasì cu <strong>$1</strong> è <strong>$2</strong>. <em>Pe' piacere signatevello chesto pe' ve ffà conzurtaziune future.</em> <br>('E bott viecchie addò servisse nu nomme utente comm'a chell' 'e l'utente, putite ancora ausà <strong>$3</strong> comm' 'o nomm' 'utente e <strong>$4</strong> comm' 'a password.)",
        "botpasswords-no-provider": "BotPasswordsSessionProvider nun è disponibbele.",
        "botpasswords-restriction-failed": "'E restriziune 'e password bot nun ve permettessero st'acciesso.",
        "botpasswords-invalid-name": "'O nomme utente nnecato nun cuntenesse nu spartetóre 'e bot password (\"$1\").",
        "blockedtitle": "Utente bloccato.",
        "blockedtext": "<strong>'O nomme utente o ll'IP vuosto è stato bloccato.</strong>\n\n'O blocco è stato mpustato 'a $1. 'O mutivo d' 'o blocco è chesto: ''$2''\n\n* Abbiàta d' 'o blocco: $8\n* Ammaturità d' 'o blocco: $6\n* Tiempo 'e blocco: $7\n\nPutite cuntattà $1 o n'atu [[{{MediaWiki:Grouppage-sysop}}|ammenistratore]] pe' discutere 'o blocco.\n\nVedite c' 'a funzione 'Scrivete a ll'utente' nun è attiva si nun s'è riggistrato 'o ndirizzo e-mail buono dint' 'e [[Special:Preferences|preferenze]] o pùre si ll'uso 'e tale funzione è stato bloccato.\n\n'O ndirizzo IP attuale è $3, 'o nummero ID d' 'o blocco è #$5.\nPe' piacere avite 'e specificà tutte sti dettaglie ccà ncoppa quanno facite cocche dumanna.",
        "autoblockedtext": "Ll'IP vuosto è stato bloccato pecché 'o steva piglianno n'atu utente, ch'è stato bloccato pe' $1.\n\n'O mutivo d' 'o blocco è chesto:\n\n:''$2''\n\n* Abbiàta d' 'o blocco: $8\n* Ammaturità d' 'o blocco: $6\n* Tiempo 'e blocco: $7\n\nPutite cuntattà $1 o n'atu [[{{MediaWiki:Grouppage-sysop}}|ammenistratore]] pe' discutere 'o blocco.\n\nVedite c' 'a funzione 'Scrivete a ll'utente' nun è attiva si nun s'è riggistrato 'o ndirizzo e-mail buono dint' 'e [[Special:Preferences|preferenze]] o pùre si ll'uso 'e tale funzione è stato bloccato.\n\n'O ndirizzo IP attuale è $3, 'o nummero ID d' 'o blocco è #$5.\nPe' piacere avite 'e specificà tutte sti dettaglie ccà ncoppa quanno facite cocche dumanna.",
+       "systemblockedtext": "'O nomme utente d' 'o vuosto o ll'IP address fosse stata automaticamente bluccata 'a MediaWiki.\n'O mutivo fosse chesto:\n\n:<em>$2</em>\n\n* Inizio d' 'o blocco: $8\n* Ammatura 'o blocco: $6\n* Intervall' 'e blocco: $7\n\n'O indirizzo IP 'e mò fosse $3.\nPe' piacere, facite specifice tuttuquante 'e ddettaglie ccà quanno iate a ghienchere na richiesta 'e chiarimiente.",
        "blockednoreason": "nisciuna ragione è stata indicata",
        "whitelistedittext": "Pe' cagnà 'e ppaggene è necessario $1.",
        "confirmedittext": "Pe puté cagnà paggene avite 'a cunfermà l'indirizzo e-mail.\nPe' piacere abbiate e ffà 'a validazione d' 'o ndirizzo e-mail pe' bbìa d' 'e [[Special:Preferences|preferenze d'utente]].",
        "readonlywarning": "<strong>Attenziò</strong>: 'o database è bloccato pe se ffà 'a manutenzione. P' 'o mumento nun se ponno sarvà 'e cagnamiente fatte.\nPe' nun 'e sperdere, copia sti cuntenute dint'a nu file 'e testo e sarvatillo pe' tramente c'aspiette 'o sblocco d' 'o database.\n\nL'ammenistratore 'e sistema ca mpustaje 'o blocco ave scritto sta spiegazione: $1.",
        "protectedpagewarning": "'''Attenziò: sta paggena è stata bloccata 'n modo tale ca sulamente l'utente ch' 'e privilegge d'ammenistratore 'a ponno cagnà.'''\nL'urdemo elemento d' 'o riggistro è scritto ccà abbascio pe' n'avé riferimento:",
        "semiprotectedpagewarning": "'''Nota:''' Sta paggena è stata bloccata 'n modo ca sulamente l'utente riggistrate 'a ponno cagnà.\nL'urdemo elemento d' 'o riggistro è scritto ccà abbascio pe n'avé nfurmazione:",
-       "cascadeprotectedwarning": "'''Attenziò:''' Sta paggena è stata bloccata 'n modo ca sulamente l'utente ch' 'e privilegge d'ammenistratore 'a ponno cagnà. Chesto succiere pecché 'a paggena è appennuta dint'a {{PLURAL:$1|la paggena innecata ccà abbascio, ch'è stata prutetta|'e paggene innecate ccà abbascio, che so' state prutette}} sciglienno 'a prutezione \"ricurziva\":",
+       "cascadeprotectedwarning": "<strong>Attenziò:</strong> Sta paggena è stata bloccata 'n modo ca sulamente l'utente ch' 'e [[Special:ListGroupRights|privilegge specifiche]] 'a ponno cagnà. Chesto succiere pecché 'a paggena è appennuta dint'a {{PLURAL:$1|la paggena innecata ccà abbascio, ch'è stata prutetta|'e paggene innecate ccà abbascio, che so' state prutette}} sciglienno 'a prutezione \"ricurziva\":",
        "titleprotectedwarning": "'''Attenziò: sta paggena è stata bloccata 'n modo ca fossero necessarie [[Special:ListGroupRights|deritte specifici]] p' 'a crià.'''\nL'urdemo elemento d' 'o riggistro è riportato ccà abbascio pe nfurmazione:",
        "templatesused": "{{PLURAL:$1|Template|Templates}} ausate 'a chesta paggena:",
        "templatesusedpreview": "{{PLURAL:$1|Template|Templates}} ausate dint'a st'anteprimma:",
        "invalid-content-data": "Date cuntenute nun buone",
        "content-not-allowed-here": "'O cuntenuto \"$1\" nun è permesso dint'a paggena [[$2]]",
        "editwarning-warning": "Ascenno 'e sta paggena putisse ffà sperdere 'e cagnamiente fatte.\nSi sì trasuto, allora può stutà st'avviso dint'a sezziona \"{{int:prefs-editing}}\" d' 'e preferenze.",
+       "editpage-invalidcontentmodel-title": "Mudell' 'e cuntenute nun suppurtato",
+       "editpage-invalidcontentmodel-text": "'O mudell' 'e cuntenute \"$1\" nun è suppurtato.",
        "editpage-notsupportedcontentformat-title": "Furmato d' 'o cuntenuto nun suppurtato",
        "editpage-notsupportedcontentformat-text": "'O furmato d' 'o cuntenuto $1 nun è suppurtato d' 'o mudello 'e cuntenuto $2.",
        "content-model-wikitext": "wikitesto",
        "post-expand-template-argument-warning": "'''Attenziò:''' sta paggena cuntene uno o cchiù argumente 'e template troppo gruosse pe' 'a spannere. Sti argumente se lassarranno fore.",
        "post-expand-template-argument-category": "Paggene ca cunteneno argumente nun cunziderate",
        "parser-template-loop-warning": "È stato scummigliato n'aniello d' 'o template: [[$1]]",
+       "template-loop-category": "Paggene ca chiammassero a esse stisse",
+       "template-loop-category-desc": "Sta paggena tenesse nu template ca chiammasse a essa stissa, cioè nu template addò sta mmescat' 'o template ca 'o chiammasse.",
        "parser-template-recursion-depth-warning": "È arrivato 'o lemmeto 'e ricurzione d' 'o template ($1)",
        "language-converter-depth-warning": "'O fùto d' 'o lemmeto d' 'o scagnatòre 'e lengua è appassato ($1)",
        "node-count-exceeded-category": "Paggene addò 'o nummero 'e núrece è stato appassato",
        "page_first": "primma",
        "page_last": "úrdema",
        "histlegend": "Confronto nfra verziune: sciglite 'e casciulelle c'attoccassero a 'e verziune che vulite cunfruntà e spremmite Invio o pure 'o buttóne ccà abbascio.\n\nLiggenda: '''({{int:cur}})''' = differenze c' 'a verzione 'e mmò, '''({{int:last}})''' = differenze c' 'a verzione 'e primma, '''{{int:minoreditletter}}''' = cagnamiento minore",
-       "history-fieldset-title": "Naviga dint' 'a cronologgia",
-       "history-show-deleted": "Solo chille canciellate",
+       "history-fieldset-title": "Circa pe' verziune",
+       "history-show-deleted": "Sulo 'e verziune scancellate",
        "histfirst": "primma",
        "histlast": "urdema",
        "historysize": "({{PLURAL:$1|1 byte|$1 byte}})",
        "searchprofile-advanced-tooltip": "Circa dint'e namespace perzonalizzate",
        "search-result-size": "$1 ({{PLURAL:$2|'na parola|$2 parole}})",
        "search-result-category-size": "{{PLURAL:$1|1 utente|$1 utente}} ({{PLURAL:$2|1 sottocategurìa|$2 sottocategurìe}}, {{PLURAL:$3|1 file|$3 files}})",
-       "search-redirect": "(redirect $1)",
+       "search-redirect": "(redirect 'a $1)",
        "search-section": "(sezzione $1)",
        "search-category": "(categurìa $1)",
        "search-file-match": "(currispunnenza dint' 'e cuntenute d' 'o file)",
        "search-suggest": "Prova chisto: $1",
        "search-rewritten": "Mmustann' 'e risultate pe' $1. Circa mmece pe' $2.",
-       "search-interwiki-caption": "Prugiette frate",
+       "search-interwiki-caption": "Risultate 'a prugiette frate",
        "search-interwiki-default": "Risultate 'a $1:",
        "search-interwiki-more": "(cchiù)",
+       "search-interwiki-more-results": "cchiù risultate",
        "search-relatedarticle": "Azzeccato",
        "searchrelated": "azzeccato",
        "searchall": "Tutte",
        "search-external": "Ricerca 'a fore",
        "searchdisabled": "'A ricerca dint'a {{SITENAME}} nun è attiva; pe' tramente se putesse ausà nu mutore 'e cerca sterno comm'a Google. (Avite 'e sapé però, ca sti cuntenute d' 'o {{SITENAME}} dint' 'e mutore, può darse ca nun stanno agghiurnate.)",
        "search-error": "È succiesso n'errore pe' tramente ca se faceva 'a ricerca: $1",
+       "search-warning": "È succiesso n'avvertimento pe' tramente ca se vaceva 'a ricerca: $1",
        "preferences": "Preferenze d''e mmeje",
        "mypreferences": "Preferenze d''e mmeje",
        "prefs-edits": "Cagnamiente affettuate:",
        "prefs-help-recentchangescount": "Chesto ntenne ll'urdeme cagnamiente, 'e cronologgie 'e paggena, e riggistre.",
        "prefs-help-watchlist-token2": "Chest'è 'a chiave segreta pe se ffà 'o feed web 'e l'elenco 'e cuntrolo d' 'o vuosto.\nSi coccheruno 'a cunoscesse, allora putesse vedé l'elenco 'e cuntrollo, picciò nun 'a spartite. [[Special:ResetTokens|Cliccate ccà se tenite necessità d' 'a rimpizzà]].",
        "savedprefs": "'E preferenze songo state sarvate.",
-       "savedrights": "'E dritte 'e l'utente {{GENDER:$1|$1}} sto state sarvate.",
+       "savedrights": "'E dritte 'e gruppe 'utente {{GENDER:$1|$1}} sto state sarvate.",
        "timezonelegend": "Fuso orario:",
        "localtime": "Ora lucale:",
        "timezoneuseserverdefault": "Aúsa ora predefinita d' 'o wiki ($1)",
        "youremail": "E-mail:",
        "username": "{{GENDER:$1|Nomme utente}}:",
        "prefs-memberingroups": "{{GENDER:$2|Membro}} {{PLURAL:$1|d' 'o gruppo|d' 'e gruppe}}:",
+       "group-membership-link-with-expiry": "$1 (nzin' 'a $2)",
        "prefs-registration": "Data 'e riggistrazione:",
        "yourrealname": "Nomme vero",
        "yourlanguage": "Lengua:",
        "prefs-help-prefershttps": "Sta preferenza averrà affetto 'a 'o prossimo acciesso vuosto.",
        "prefswarning-warning": "Avite fatto cagnamiente a 'e preferenze d' 'e vuoste ca nun so' stat'ancora sarvate.\nSi ascite 'a sta paggena senza clickà \"$1\" 'e preferenze d' 'e vuoste nun sarranno agghiurnate.",
        "prefs-tabs-navigation-hint": "Suggerimento: se ponno ausà 'e buttòne 'e freccia a manca e a dritta pe' ve muovere nfra 'e schede dint'a l'elenco d' 'e schede.",
-       "userrights": "Gestione d' 'e permesse 'e l'utente",
-       "userrights-lookup-user": "Gestione 'e gruppe d'utenza",
+       "userrights": "Deritte utente",
+       "userrights-lookup-user": "Sciglie n'utente",
        "userrights-user-editname": "Nzertàte nu nomme utente:",
-       "editusergroup": "Cagnate 'e gruppe d'{{GENDER:$1|utenze}}",
+       "editusergroup": "Càrreca gruppe 'utente",
        "editinguser": "Cagnamiento d' 'e deritte d'{{GENDER:$1|utente}} '''[[User:$1|$1]]''' $2",
-       "userrights-editusergroup": "Cagnate 'e gruppe d'utenze",
+       "viewinguserrights": "Verenn' 'e deritte '{{GENDER:$1|utente}} <strong>[[User:$1|$1]]</strong> $2",
+       "userrights-editusergroup": "Cagna 'e gruppe '{{GENDER:$1|utente}}",
+       "userrights-viewusergroup": "Vire gruppe {{GENDER:$1|utente}}",
        "saveusergroups": "Sarvate 'e gruppe d'{{GENDER:$1|utenza}}",
        "userrights-groupsmember": "Ffà parte {{PLURAL:$1|d' 'o gruppo|d' 'e gruppe}}:",
        "userrights-groupsmember-auto": "Membro mplicito 'e:",
-       "userrights-groups-help": "Putite cagnà 'e gruppe assegnate a l'utente:\n* Na cascia 'e spunta scigliuta significasse ca appartenenza 'e l'utente a 'o gruppo\n* Na cascia 'e spunta nun scigliuta significasse 'a nun appartenenza a 'o gruppo.\n* 'O simmolo * significasse ca nun se può scancellà l'appartenenza a 'o gruppo aropp'a ll'avé miso (o viceversa).",
+       "userrights-groups-help": "Putite cagnà 'e gruppe assegnate a l'utente:\n* Na cascia 'e spunta scigliuta significasse ca appartenenza 'e l'utente a 'o gruppo\n* Na cascia 'e spunta nun scigliuta significasse 'a nun appartenenza a 'o gruppo.\n* 'O simmolo * significasse ca nun se può scancellà l'appartenenza a 'o gruppo aropp'a ll'avé miso (o viceversa).\n* 'O # significasse ca vuje putite surtanto tirà arreto nu tiempo 'e ammatura 'e stu gruppo utente; nun 'o putite fà annanze.",
        "userrights-reason": "Mutivo:",
        "userrights-no-interwiki": "Nun tenite permesse pe' cagnà 'e deritte 'e l'utente ncopp'a l'ati wiki.",
        "userrights-nodatabase": "'O database $1 nun esiste o nun è nu database lucale.",
        "userrights-changeable-col": "Gruppe ca putite cagnà",
        "userrights-unchangeable-col": "Gruppe ca nun putite cagnà",
+       "userrights-expiry-current": "Ammatura 'o $1",
+       "userrights-expiry-none": "Nun ammaturasse",
+       "userrights-expiry": "Ammatura:",
+       "userrights-expiry-existing": "'O tiempo d'ammaturamiento esistente: $3, $2",
+       "userrights-expiry-othertime": "N'ata durata:",
+       "userrights-expiry-options": "1 juorne:1 day,1 semmana:1 week,1 mese:1 month,3 mise:3 mise,6 mesi:6 months,1 anno:1 year",
+       "userrights-invalid-expiry": "'O tiempo pe' quanno ammatura 'o gruppo \"$1\" nun è buono.",
+       "userrights-expiry-in-past": "'O tiempo pe' quanno ammatura 'o gruppo \"$1\" fosse int' 'o passato.",
+       "userrights-cannot-shorten-expiry": "Nun putite turnà arreto 'o tiempo ammatura int' 'o gruppo \"$1\". Surtanto ll'utente cu nu permesso pe' puté azzeccà o luvà stu gruppo ponno ffà annanze 'e tiempe ammaturamiento.",
        "userrights-conflict": "Conflitto 'e cagnamiento 'e deritte utente! Cuntrullate e cunfermate 'e cagnamiente vuoste.",
        "group": "Gruppo:",
        "group-user": "Utente",
        "grant-basic": "Deritte 'e base",
        "grant-viewdeleted": "Vide 'e file e paggene scancellate",
        "grant-viewmywatchlist": "Vide l'elenco 'e cuntrullate",
+       "grant-viewrestrictedlogs": "Vide 'e valure private d' 'o riggistro",
        "newuserlogpage": "Riggistro 'e nuove utente",
        "newuserlogpagetext": "Chest'è nu riggistro 'e criazione d'utenze.",
        "rightslog": "Deritte 'e ll'utente",
        "action-upload_by_url": "carreca stu file 'a n'indirizzo URL",
        "action-writeapi": "usa l'API 'n scrittura",
        "action-delete": "scancèlla chista paggena",
-       "action-deleterevision": "scancellà sta verziona",
+       "action-deleterevision": "scancellà 'e verziune",
        "action-deletedhistory": "vide 'a cronologgia scancellata 'e sta paggena",
        "action-browsearchive": "ascìa dint' 'e paggene scancellate",
        "action-undelete": "arripiglia chista paggena",
        "fileduplicatesearch-noresults": "Nisciuno file chiamato \"$1\" è stato accucchiato.",
        "specialpages": "Paggene speciale",
        "specialpages-note-top": "Liggenda",
-       "specialpages-note": "* Paggene speciale normale.\n* <span class=\"mw-specialpagerestricted\">Paggene speciale ch' 'e restriziune.</span>",
        "specialpages-group-maintenance": "Report 'e manutenzione",
        "specialpages-group-other": "Ati paggene speciale",
        "specialpages-group-login": "Trasite o criate n'acciesso nuovo",
index edaebe7..2763df2 100644 (file)
        "fileduplicatesearch-noresults": "Ingen ved navn «$1» funnet.",
        "specialpages": "Spesialsider",
        "specialpages-note-top": "Tegnforklaring",
-       "specialpages-note": "* Normale spesialsider.\n* <span class=\"mw-specialpagerestricted\">Spesialsider med begrenset tilgang.</span>",
        "specialpages-group-maintenance": "Vedlikeholdsrapporter",
        "specialpages-group-other": "Andre spesialsider",
        "specialpages-group-login": "Innlogging / opprette bruker",
index de9207a..99041d4 100644 (file)
        "right-autocreateaccount": "Automatisch aanmelden met een extern gebruikersaccount",
        "right-minoredit": "Bewerkingen als klein markeren",
        "right-move": "Pagina's hernoemen",
-       "right-move-subpages": "Pagina's inclusief subpagina's verplaatsen",
+       "right-move-subpages": "Pagina's inclusief deelpagina's verplaatsen",
        "right-move-rootuserpages": "Gebruikerspagina's van het hoogste niveau hernoemen",
        "right-move-categorypages": "Categoriepagina's hernoemen",
        "right-movefile": "Bestanden hernoemen",
        "action-history": "de geschiedenis van deze pagina te bekijken",
        "action-minoredit": "deze bewerking als klein te markeren",
        "action-move": "deze pagina te hernoemen",
-       "action-move-subpages": "deze pagina en bijbehorende subpagina's te hernoemen",
+       "action-move-subpages": "deze pagina en bijbehorende deelpagina's te hernoemen",
        "action-move-rootuserpages": "gebruikerspagina's van het hoogste niveau te hernoemen",
        "action-move-categorypages": "categoriepagina's te hernoemen",
        "action-movefile": "dit bestand te hernoemen",
        "rcfilters-legend-heading": "<strong>Lijst met afkortingen:</strong>",
        "rcfilters-activefilters": "Actieve filters",
        "rcfilters-advancedfilters": "Geavanceerde filters",
+       "rcfilters-limit-title": "Wijzigingen om te tonen",
+       "rcfilters-limit-shownum": "Toon laatste $1 wijzigingen",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|dag|dagen}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|uur|uren}}",
        "rcfilters-quickfilters": "Opgeslagen filters",
        "rcfilters-quickfilters-placeholder-title": "Nog geen koppelingen opgeslagen",
        "rcfilters-quickfilters-placeholder-description": "Om uw filterinstellingen op te slaan en later te kunnen hergebruiken, klik op het bladwijzer pictogram in het Actieve Filter gebied beneden.",
        "rcfilters-savedqueries-unsetdefault": "Als standaard verwijderen",
        "rcfilters-savedqueries-remove": "Verwijderen",
        "rcfilters-savedqueries-new-name-label": "Naam",
+       "rcfilters-savedqueries-new-name-placeholder": "Beschrijf het doel van het filter",
        "rcfilters-savedqueries-apply-label": "Filter aanmaken",
        "rcfilters-savedqueries-cancel-label": "Annuleren",
        "rcfilters-savedqueries-add-new-title": "Huidige filter instellingen opslaan",
        "rcfilters-invalid-filter": "Ongeldig filter",
        "rcfilters-empty-filter": "Geen actieve filters. Alle bijdragen worden weergeven.",
        "rcfilters-filterlist-title": "Filters",
-       "rcfilters-filterlist-whatsthis": "Wat is dit?",
+       "rcfilters-filterlist-whatsthis": "Hoe werkt dit?",
        "rcfilters-filterlist-feedbacklink": "Geef terugkoppeling op de nieuwe (beta)filters",
        "rcfilters-highlightbutton-title": "Resultaten markeren",
        "rcfilters-highlightmenu-title": "Kies een kleur",
        "rcfilters-filter-editsbyself-description": "Uw eigen bijdragen.",
        "rcfilters-filter-editsbyother-label": "Wijzigingen door anderen",
        "rcfilters-filter-editsbyother-description": "Alle wijzigingen behalve die door u gemaakt zijn.",
-       "rcfilters-filtergroup-userExpLevel": "Ervaringsniveau (alleen voor geregistreerde gebruikers)",
+       "rcfilters-filtergroup-userExpLevel": "Gebruikersregistratie en ervaring",
        "rcfilters-filter-user-experience-level-registered-label": "Geregistreerd",
-       "rcfilters-filter-user-experience-level-registered-description": "Ingelogde gebruikers.",
+       "rcfilters-filter-user-experience-level-registered-description": "Aangemelde bewerkers.",
        "rcfilters-filter-user-experience-level-unregistered-label": "Niet-geregistreerd",
-       "rcfilters-filter-user-experience-level-unregistered-description": "Gebruikers die niet zijn ingelogd.",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Bewerkers die niet zijn aangemeld.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Nieuwkomers",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Minder dan 10 bewerkingen en 4 dagen van activiteit.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Geregistreerde bewerkers met minder dan 10 bewerkingen en 4 dagen van activiteit.",
        "rcfilters-filter-user-experience-level-learner-label": "Leerlingen",
-       "rcfilters-filter-user-experience-level-learner-description": "Meer ervaring dan \"nieuwkomers\", maar minder dan \"ervaren gebruikers\".",
+       "rcfilters-filter-user-experience-level-learner-description": "Geregistreerde bewerkers met meer ervaring dan \"nieuwkomers\", maar minder dan \"ervaren gebruikers\".",
        "rcfilters-filter-user-experience-level-experienced-label": "Ervaren gebruikers",
-       "rcfilters-filter-user-experience-level-experienced-description": "Meer dan 30 dagen van activiteit en 500 bewerkingen.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Geregistreerde bewerkers met meer dan 500 bewerkingen en 30 dagen van activiteit.",
        "rcfilters-filtergroup-automated": "Automatische bijdragen",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "De wijzigingen van geautomatiseerde hulpmiddelen.",
        "rcfilters-filter-logactions-label": "Geregistreerde acties",
        "rcfilters-filter-logactions-description": "Administratieve handelingen, account creaties, pagina verwijderingen, uploads…",
        "rcfilters-hideminor-conflicts-typeofchange": "Bepaalde soorten wijzigingen kunnen niet worden aangemerkt als \"klein\", dus dit filter is in conflict met de volgende soorten wijzigingenfilters: $1",
-       "rcfilters-filtergroup-lastRevision": "Laatste versie",
+       "rcfilters-filtergroup-lastRevision": "Laatste versies",
        "rcfilters-filter-lastrevision-label": "Laatste versie",
-       "rcfilters-filter-lastrevision-description": "De meest recente wijziging aan de pagina.",
-       "rcfilters-filter-previousrevision-label": "Eerdere versies",
-       "rcfilters-filter-previousrevision-description": "Alle wijzigingen die niet de meest recente wijziging op de pagina zijn.",
+       "rcfilters-filter-lastrevision-description": "Alleen de meest recente wijziging aan de pagina.",
+       "rcfilters-filter-previousrevision-label": "Niet de laatste versie",
+       "rcfilters-filter-previousrevision-description": "Alle wijzigingen die niet de \"laatste versie\" zijn.",
        "rcfilters-filter-excluded": "Uitgesloten",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:niet</strong> $1",
+       "rcfilters-exclude-button-off": "Geselecteerde uitsluiten",
        "rcfilters-view-tags": "Gelabelde bewerkingen",
        "rcfilters-view-namespaces-tooltip": "Filter resultaten op naamruimte",
        "rcfilters-view-tags-tooltip": "Filter resultaten door middel van bewerkingslabels",
        "delete-warning-toobig": "Deze pagina heeft een lange bewerkingsgeschiedenis, meer dan $1 {{PLURAL:$1|versie|versies}}.\nHet verwijderen van deze pagina kan de werking van de database van {{SITENAME}} verstoren.\nWees voorzichtig.",
        "deleteprotected": "U kunt deze pagina niet verwijderen omdat hij is beveiligd.",
        "deleting-backlinks-warning": "<strong>Waarschuwing:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|andere pagina's]] gebruiken of verwijzen naar de pagina die u wilt verwijderen.",
+       "deleting-subpages-warning": "<strong>Waarschuwing:</strong>De pagina die u wilt verwijderen heeft [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|een deelpagina|$1 deelpagina's|51=meer dan 50 deelpagina's}}]].",
        "rollback": "Wijzigingen ongedaan maken",
        "rollbacklink": "terugdraaien",
        "rollbacklinkcount": "{{PLURAL:$1|één bewerking|$1 bewerkingen}} terugdraaien",
        "articleexists": "De pagina bestaat al of de paginanaam is ongeldig.\nKies een andere paginanaam.",
        "cantmove-titleprotected": "U kunt geen pagina naar deze naam hernoemen, omdat deze naam beveiligd is tegen het aanmaken ervan.",
        "movetalk": "Bijbehorende overlegpagina hernoemen",
-       "move-subpages": "Subpagina's hernoemen (maximaal $1)",
-       "move-talk-subpages": "Subpagina's van overlegpagina's hernoemen (maximaal $1)",
+       "move-subpages": "Deelpagina's hernoemen (maximaal $1)",
+       "move-talk-subpages": "Deelpagina's van overlegpagina's hernoemen (maximaal $1)",
        "movepage-page-exists": "De pagina $1 bestaat al en kan niet automatisch verwijderd worden.",
        "movepage-page-moved": "De pagina $1 is hernoemd naar $2.",
        "movepage-page-unmoved": "De pagina $1 kon niet hernoemd worden naar $2.",
        "movepage-max-pages": "Het maximale aantal automatisch te hernoemen pagina's is bereikt ({{PLURAL:$1|$1|$1}}).\nDe overige pagina's worden niet automatisch hernoemd.",
        "movelogpage": "Hernoemingslogboek",
        "movelogpagetext": "Hieronder staan hernoemde pagina's.",
-       "movesubpage": "{{PLURAL:$1|Subpagina|Subpagina's}}",
-       "movesubpagetext": "De {{PLURAL:$1|subpagina|$1 subpagina's}} van deze pagina {{PLURAL:$1|wordt|worden}} hieronder weergegeven.",
+       "movesubpage": "{{PLURAL:$1|Deelpagina|Deelpagina's}}",
+       "movesubpagetext": "De {{PLURAL:$1|deelpagina|$1 deelpagina's}} van deze pagina {{PLURAL:$1|wordt|worden}} hieronder weergegeven.",
        "movesubpagetalktext": "De bijbehorende overlegpagina heeft $1 {{PLURAL:$1|deelpagina|deelpagina's}} hierbeneden getoond.",
-       "movenosubpage": "Deze pagina heeft geen subpagina's.",
+       "movenosubpage": "Deze pagina heeft geen deelpagina's.",
        "movereason": "Reden:",
        "revertmove": "terugdraaien",
        "delete_and_move_text": "Onder de naam \"[[:$1]]\" bestaat al een pagina.\nWilt u deze verwijderen om plaats te maken voor de te hernoemen pagina?",
        "import-interwiki-submit": "Importeren",
        "import-mapping-default": "Importeren naar standaardplaatsen",
        "import-mapping-namespace": "Importeren naar een naamruimte:",
-       "import-mapping-subpage": "Importeren als subpagina's van de volgende pagina:",
+       "import-mapping-subpage": "Importeren als deelpagina's van de volgende pagina:",
        "import-upload-filename": "Bestandsnaam:",
        "import-comment": "Opmerking:",
        "importtext": "Gebruik de [[Special:Export|exportfunctie]] in de wiki waar de informatie vandaan komt.\nSla de uitvoer op uw eigen computer op, en voeg die daarna hier toe.",
        "import-error-bad-location": "Versie $2 met behulp van model $3 kan niet worden opgeslagen als \"$1\" op deze wiki, aangezien dat model niet ondersteund wordt op die pagina.",
        "import-options-wrong": "Verkeerde {{PLURAL:$2|optie|opties}}: <nowiki>$1</nowiki>",
        "import-rootpage-invalid": "De opgegeven basispagina is ongeldig.",
-       "import-rootpage-nosubpage": "In de naamruimte \"$1\" van de basispagina is het aanmaken van subpagina's niet mogelijk.",
+       "import-rootpage-nosubpage": "In de naamruimte \"$1\" van de basispagina is het aanmaken van deelpagina's niet mogelijk.",
        "importlogpage": "Importlogboek",
        "importlogpagetext": "Administratieve import van pagina's met geschiedenis van andere wiki's.",
        "import-logentry-upload-detail": "$1 {{PLURAL:$1|versie|versies}} geïmporteerd",
        "pageinfo-few-watchers": "Minder dan  {{PLURAL:$1|één volger|$1 volgers}}",
        "pageinfo-few-visiting-watchers": "Er kan wel of niet een volger zijn die de laatste bewerkingen hier bezoekt",
        "pageinfo-redirects-name": "Aantal doorverwijzingen naar deze pagina",
-       "pageinfo-subpages-name": "Subpagina's van deze pagina",
+       "pageinfo-subpages-name": "Aantal deelpagina's van deze pagina",
        "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|doorverwijzing|doorverwijzingen}}; $3 {{PLURAL:$3|niet-doorverwijzing|niet-doorverwijzingen}})",
        "pageinfo-firstuser": "Gebruiker die de pagina heeft aangemaakt",
        "pageinfo-firsttime": "Datum waarop de pagina is aangemaakt",
        "fileduplicatesearch-noresults": "Er is geen bestand met de naam \"$1\" gevonden.",
        "specialpages": "Speciale pagina's",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Normale speciale pagina's\n* <span class=\"mw-specialpagerestricted\">Beperkt toegankelijke speciale pagina's</span>",
+       "specialpages-note-restricted": "* Normale speciale pagina's.\n* <span class=\"mw-specialpagerestricted\">Beperkt toegankelijke speciale pagina's.</span>",
        "specialpages-group-maintenance": "Onderhoudsrapporten",
        "specialpages-group-other": "Overige speciale pagina's",
        "specialpages-group-login": "Aanmelden / registreren",
index c06af23..c6c66f0 100644 (file)
        "preview": "Førehandsvising",
        "showpreview": "Førehandsvis",
        "showdiff": "Sjå skilnader",
+       "blankarticle": "<strong>Åtvaring:</strong> Sida du er i ferd med å oppretta er tom.\nKlikkar du på «$1» ein gong til vil sida opprettast utan innhald.",
        "anoneditwarning": "'''Åtvaring:''' Du er ikkje innlogga.\nIP-adressa di vil verta lagra i den offentlege endringshistorikken til sida. Om du <strong>[$1 loggar inn]</strong> eller <strong>[$2 lagar ein konto]</strong>, vil endringane dine knytast til brukarnamnet ditt, saman med andre fordelar.",
        "anonpreviewwarning": "''Du er ikkje innlogga. Lagrar du vil IP-adressa di verta ført opp i endringshistorikken til denne sida.''",
        "missingsummary": "'''Påminning:''' Du har ikkje skrive noko endringssamandrag. Dersom du trykkjer «Lagre» ein gong til, vert endringa di lagra utan.",
        "post-expand-template-inclusion-category": "Sider som inneheld for store malar",
        "post-expand-template-argument-warning": "Åtvaring: Sida inneheld ein eller fleire malparameterar som vert for lange når dei utvidast.\nDesse parameterane har vorte utelatne.",
        "post-expand-template-argument-category": "Sider med utelatne malparameterar",
-       "parser-template-loop-warning": "Malløkka oppdaga: [[$1]]",
+       "parser-template-loop-warning": "Mallykkje oppdaga: [[$1]]",
        "parser-template-recursion-depth-warning": "Malen er inkludert for mange gonger ($1)",
        "language-converter-depth-warning": "Språkomformaren si djubdegrense vart overstege ($1)",
        "node-count-exceeded-category": "Sider der talet på knutepunkt er overskride",
        "fileduplicatesearch-noresults": "Fann inga fil med namnet «$1».",
        "specialpages": "Spesialsider",
        "specialpages-note-top": "Tyding",
-       "specialpages-note": "* Vanlege spesialsider.\n* <span class=\"mw-specialpagerestricted\">Spesialsider med avgrensa tilgang.</span>",
        "specialpages-group-maintenance": "Vedlikehaldsrapportar",
        "specialpages-group-other": "Andre spesialsider",
        "specialpages-group-login": "Logga inn / oppretta brukarkonto",
index 9d1a356..61a40b3 100644 (file)
        "rcfilters-legend-heading": "<strong>Wykaz skrótów:</strong>",
        "rcfilters-activefilters": "Aktywne filtry",
        "rcfilters-advancedfilters": "Zaawansowane filtry",
+       "rcfilters-limit-title": "Zmian do pokazania",
+       "rcfilters-limit-shownum": "Pokaż ostatnie $1 zmian",
+       "rcfilters-days-title": "Ostatnich dni",
+       "rcfilters-hours-title": "Ostatnich godzin",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|dzień|dni}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|godzina|godziny|godzin}}",
        "rcfilters-quickfilters": "Zapisane filtry",
        "rcfilters-quickfilters-placeholder-title": "Nie masz jeszcze zapisanych linków",
        "rcfilters-quickfilters-placeholder-description": "Aby zapisać ustawienia filtrów i używać ich później, kliknij ikonkę zakładki w polu aktywnych filtrów znajdującym się niżej.",
        "rcfilters-invalid-filter": "Nieprawidłowy filtr",
        "rcfilters-empty-filter": "Brak aktywnych filtrów. Wyświetlane są wszystkie zmiany.",
        "rcfilters-filterlist-title": "Filtry",
-       "rcfilters-filterlist-whatsthis": "Co to jest?",
+       "rcfilters-filterlist-whatsthis": "Jak działają?",
        "rcfilters-filterlist-feedbacklink": "Podziel się swoją opinią na temat tych nowych (beta) filtrów",
        "rcfilters-highlightbutton-title": "Podświetl wyniki",
        "rcfilters-highlightmenu-title": "Wybierz kolor",
        "rcfilters-filter-editsbyself-description": "Czynności dokonane przez Ciebie.",
        "rcfilters-filter-editsbyother-label": "Zmiany dokonane przez innych",
        "rcfilters-filter-editsbyother-description": "Wszystkie zmiany oprócz Twoich.",
-       "rcfilters-filtergroup-userExpLevel": "Poziom doświadczenia (tylko o zarejestrowanych użytkownikach)",
+       "rcfilters-filtergroup-userExpLevel": "Zarejestrowanie użytkownika i doświadczenie",
        "rcfilters-filter-user-experience-level-registered-label": "Zarejestrowani",
        "rcfilters-filter-user-experience-level-registered-description": "Zalogowani edytorzy.",
        "rcfilters-filter-user-experience-level-unregistered-label": "Niezarejestrowani",
        "rcfilters-filter-user-experience-level-unregistered-description": "Niezalogowani",
        "rcfilters-filter-user-experience-level-newcomer-label": "Początkujący",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Mniej niż 10 edycji i 4 dni aktywności.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Zarejestrowani edytorzy z mniej niż 10 edycji i 4 dni aktywności.",
        "rcfilters-filter-user-experience-level-learner-label": "Uczący się",
-       "rcfilters-filter-user-experience-level-learner-description": "Większe doświadczenie niż „Nowicjusze”, ale mniejsze niż „Doświadczeni użytkownicy”.",
+       "rcfilters-filter-user-experience-level-learner-description": "Zarejestrowani edytujący, których doświadczenie plasuje się między „Nowicjuszami”, a „Doświadczonymi użytkownikami”.",
        "rcfilters-filter-user-experience-level-experienced-label": "Doświadczeni użytkownicy",
-       "rcfilters-filter-user-experience-level-experienced-description": "Ponad 30 dni aktywności i 500 edycji.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Zarejestrowani edytujący z ponad 500 edycji i 30 dni aktywności.",
        "rcfilters-filtergroup-automated": "Zmiany zautomatyzowane",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Zmiany wykonane z użyciem zautomatyzowanych narzędzi.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "Filtr „Drobne zmiany” koliduje z jednym lub wieloma filtrami Rodzaju zmian, ponieważ niektóre rodzaje zmian nie mogą być uznawane za  „drobne”. Kolidujące filtry zostały powyżej odpowiednio zaznaczone na pasku aktywnych filtrów.",
        "rcfilters-hideminor-conflicts-typeofchange": "Niektóre rodzaje zmian nie mogą być uznawane za „drobne”, dlatego ten filtr koliduje z następującymi filtrami Rodzaju zmian: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Ten filtr Rodzaju zmian koliduje z filtrem „Drobne zmiany”. Nie wszystkie zmiany mogą być uznawane za „drobne”.",
-       "rcfilters-filtergroup-lastRevision": "Ostatnia wersja",
-       "rcfilters-filter-lastrevision-label": "Ostatnie wersje",
+       "rcfilters-filtergroup-lastRevision": "Ostatnie wersje",
+       "rcfilters-filter-lastrevision-label": "Najnowsza wersja",
        "rcfilters-filter-lastrevision-description": "Tylko najnowsze zmiany dla każdej ze stron.",
-       "rcfilters-filter-previousrevision-label": "Wcześniejsze wersje",
-       "rcfilters-filter-previousrevision-description": "Wszystkie edycje, które nie są najnowszą zmianą strony.",
+       "rcfilters-filter-previousrevision-label": "Wersje inne niż najnowsza",
+       "rcfilters-filter-previousrevision-description": "Wszystkie edycje, które nie są najnowszą wersją strony.",
        "rcfilters-filter-excluded": "Wykluczono",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:nie z</strong> $1",
+       "rcfilters-exclude-button-off": "Wyklucz zaznaczone",
+       "rcfilters-exclude-button-on": "Zaznaczone są wykluczone",
        "rcfilters-view-tags": "Edycje ze znacznikami zmian",
        "rcfilters-view-namespaces-tooltip": "Przefiltruj wyniki według przestrzeni nazw",
        "rcfilters-view-tags-tooltip": "Przefiltruj wyniki według znaczników zmian",
        "delete-warning-toobig": "Ta strona ma bardzo długą historię edycji – ponad $1 {{PLURAL:$1|zmianę|zmiany|zmian}}.<br />\nBądź ostrożny, ponieważ usunięcie jej może spowodować zakłócenia w pracy {{GRAMMAR:D.lp|{{SITENAME}}}}.",
        "deleteprotected": "Nie możesz usunąć tej strony, ponieważ została zabezpieczona.",
        "deleting-backlinks-warning": "<strong>Uwaga:</strong> Do strony, którą masz zamiar usunąć, odwołują się [[Special:WhatLinksHere/{{FULLPAGENAME}}|inne strony]].",
+       "deleting-subpages-warning": "<strong>Ostrzeżenie:</strong> Strona którą chcesz usunąć ma [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|jedną podstronę|$1 podstrony|$1 podstron|51=ponad 50 podstron}}]].",
        "rollback": "Cofnij edycję",
        "rollbacklink": "cofnij",
        "rollbacklinkcount": "cofnij $1 {{PLURAL:$1|edycję|edycje|edycji}}",
        "fileduplicatesearch-noresults": "Brak pliku o nazwie „$1”.",
        "specialpages": "Strony specjalne",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Normalne strony specjalne.\n* <span class=\"mw-specialpagerestricted\">Zastrzeżone strony specjalne.</span>",
+       "specialpages-note-restricted": "* Normalne strony specjalne.\n* <span class=\"mw-specialpagerestricted\">Zastrzeżone strony specjalne.</span>",
        "specialpages-group-maintenance": "Raporty konserwacyjne",
        "specialpages-group-other": "Inne strony specjalne",
        "specialpages-group-login": "Logowanie / rejestracja",
index 81b6f72..eda97ca 100644 (file)
@@ -14,7 +14,8 @@
                        "Matma Rex",
                        "Saanvel",
                        "Satdeep gill",
-                       "Abbas dhothar"
+                       "Abbas dhothar",
+                       "Saraiki"
                ]
        },
        "tog-underline": "جوڑ تھلے لین:",
        "category-file-count-limited": "اس گٹھ چ اے {{PLURAL:$1|فائل اے|$1 فائلاں نیں}}۔",
        "listingcontinuesabbrev": "جاری",
        "index-category": "انڈیکسڈ صفے",
-       "noindex-category": "نان انڈیکسڈ صفے",
+       "noindex-category": "نان انڈیکسڈ ورقے",
        "broken-file-category": "ٹٹے ہوۓ جوڑاں آلے صفحے",
        "about": "بارے چ",
        "article": "آرٹیکل والا صفہ",
        "anontalk": "گل",
        "navigation": "کھوج",
        "and": "&#32;تے",
-       "qbfind": "کھوج",
-       "qbbrowse": "لبو",
-       "qbedit": "لکھو",
-       "qbpageoptions": "اے صفہ",
-       "qbmyoptions": "میرے صفے",
        "faq": "FAQ",
-       "faqpage": "Project:FAQ",
        "actions": "کم",
        "namespaces": "ناں تھانواں:",
        "variants": "قسماں",
        "view": "وکھالہ",
        "view-foreign": "$1 تے ویکھو",
        "edit": "لکھو",
+       "edit-local": "مقامی تفصیل درج کرو",
        "create": "بناؤ",
        "create-local": "آپنی لکھت رلاؤ",
-       "editthispage": "اس صفحہ تے لکھو",
-       "create-this-page": "اے صفحہ بناؤ",
        "delete": "مٹاؤ",
-       "deletethispage": "اے صفحہ مٹاؤ",
-       "undeletethispage": "اس صفحے نوں واپس لیاؤ",
        "undelete_short": "مٹانا واپس {{PLURAL:$1|اکتبدیلی|$1 تبدیلی}}",
        "viewdeleted_short": "ویکھو {{PLURAL:$1|اک مٹائی گئی تبدیلی|$1 مٹائیاں گئیاں تبدیلیاں}}",
        "protect": "بچاؤ",
        "protect_change": "تبدیل کرو",
-       "protectthispage": "اے صفحہ بچاؤ",
        "unprotect": "اینا بچاؤ",
-       "unprotectthispage": "اے صفحہ اینا بچاؤ",
        "newpage": "نواں صفہ",
-       "talkpage": "اس صفحے دے بارے چ گل بات کرو",
        "talkpagelinktext": "گل بات",
        "specialpage": "خاص صفحہ",
        "personaltools": "ذاتی اوزار",
-       "articlepage": "مضمون آلا صفحہ",
        "talk": "گل بات",
        "views": "وکھالے",
        "toolbox": "سَند",
-       "userpage": "ورتن آلے دا صفہ ویکھو",
-       "projectpage": "ویونت والا صفہ ویکھو",
        "imagepage": "فائل آلا صفہ ویکھو",
        "mediawikipage": "سنیعا آلا صفحہ ویکھو",
        "templatepage": "سچے آلا صفحہ ویکھو",
        "fileduplicatesearch-result-n": "فائل ''$1'' چ {{PLURAL:$2|1 رلدی نقل|$2 رلدیاں نقلں}} نیں۔",
        "fileduplicatesearch-noresults": "\"$1\" ناں دی کوئی فائل نئیں لبی۔",
        "specialpages": "خاص صفے",
-       "specialpages-note": "* نارمل خاص صفے.\n* <span class=\"mw-specialpagerestricted\">روکے گۓ خاص صفے.</span>\n* <span class=\"mw-specialpagecached\">کاشے خاص صفے (پرانے ہوگۓ ہون).</span>",
        "specialpages-group-maintenance": "مرمت رپورٹ",
        "specialpages-group-other": "ہور خاص صفے",
        "specialpages-group-login": "لاگان / کھاتہ کھولو",
index c95d6f8..2eaaf0b 100644 (file)
        "rcfilters-legend-heading": "<strong>Lista de abreviaturas:</strong>",
        "rcfilters-activefilters": "Filtros ativos",
        "rcfilters-advancedfilters": "Filtros avançados",
+       "rcfilters-limit-title": "Mudanças para mostrar",
+       "rcfilters-limit-shownum": "Mostrar as últimas $1 modificações",
+       "rcfilters-days-title": "Dias recentes",
+       "rcfilters-hours-title": "Horas recentes",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|dia|dias}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|hora|horas}}",
        "rcfilters-quickfilters": "Filtros salvos",
        "rcfilters-quickfilters-placeholder-title": "Ainda não foi gravado nenhum link",
        "rcfilters-quickfilters-placeholder-description": "Para gravar as suas configurações dos filtros e reutilizá-las mais tarde, clique o ícone do marcador de página, na área Filtro Ativo abaixo.",
        "rcfilters-invalid-filter": "Filtro inválido",
        "rcfilters-empty-filter": "Nenhum filtro ativo. Todas as contribuições são mostradas.",
        "rcfilters-filterlist-title": "Filtros",
-       "rcfilters-filterlist-whatsthis": "O que é isso?",
+       "rcfilters-filterlist-whatsthis": "Como funcionam estes?",
        "rcfilters-filterlist-feedbacklink": "Forneça feedback sobre os novos filtros (beta)",
        "rcfilters-highlightbutton-title": "Realçar os resultados",
        "rcfilters-highlightmenu-title": "Selecione uma cor",
        "rcfilters-filter-editsbyself-description": "Suas proprias contribuições.",
        "rcfilters-filter-editsbyother-label": "Mudanças de outros",
        "rcfilters-filter-editsbyother-description": "Todas as mudanças, exceto a sua.",
-       "rcfilters-filtergroup-userExpLevel": "Nível de experiência (apenas para usuário registados)",
+       "rcfilters-filtergroup-userExpLevel": "Registro e experiência do usuário",
        "rcfilters-filter-user-experience-level-registered-label": "Registrado",
-       "rcfilters-filter-user-experience-level-registered-description": "Editores conectados.",
-       "rcfilters-filter-user-experience-level-unregistered-label": "Não registrado",
-       "rcfilters-filter-user-experience-level-unregistered-description": "Editores que não estão conectados.",
+       "rcfilters-filter-user-experience-level-registered-description": "Editores registrados.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Não registados",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Editores que não estão autenticados.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Recém-chegados",
        "rcfilters-filter-user-experience-level-newcomer-description": "Menos de 10 edições e 4 dias de atividade.",
        "rcfilters-filter-user-experience-level-learner-label": "Aprendizes",
        "rcfilters-hideminor-conflicts-typeofchange-global": "O filtro \"Edições menores\" conflita com um ou mais filtros de Tipo de Alteração, porque certos tipos de alteração não podem ser designadas como \"menores\". Os filtros em conflito estão marcados na área Filtros Ativos, acima.",
        "rcfilters-hideminor-conflicts-typeofchange": "Determinados tipos de alteração não podem ser designados como \"menor\", portanto, este filtro entra em conflito com os seguintes filtros de Tipo de Alteração: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Este filtro de Tipo de Alteração entra em conflito com o filtro \"Edições menores\". Certos tipos de mudança não podem ser designadas como \"menores\".",
-       "rcfilters-filtergroup-lastRevision": "Última revisão",
-       "rcfilters-filter-lastrevision-label": "Última revisão",
-       "rcfilters-filter-lastrevision-description": "A alteração mais recente para uma página.",
-       "rcfilters-filter-previousrevision-label": "Revisões anteriores",
-       "rcfilters-filter-previousrevision-description": "Todas as alterações que não são a alteração mais recente para uma página.",
+       "rcfilters-filtergroup-lastRevision": "Últimas revisões",
+       "rcfilters-filter-lastrevision-label": "Revisão atual",
+       "rcfilters-filter-lastrevision-description": "Somente a mudança mais recente para uma página.",
+       "rcfilters-filter-previousrevision-label": "Não é a última revisão",
+       "rcfilters-filter-previousrevision-description": "Todas as mudanças que não são as \"ultimas revisões\".",
        "rcfilters-filter-excluded": "Excluído",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:não</strong> $1",
+       "rcfilters-exclude-button-off": "Excluir selecionado",
+       "rcfilters-exclude-button-on": "Excluindo selecionados",
        "rcfilters-view-tags": "Edições marcadas",
        "rcfilters-view-namespaces-tooltip": "Filtrar resultados por namespace",
        "rcfilters-view-tags-tooltip": "Filtre os resultados usando edit tags",
        "delete-warning-toobig": "Esta página possui um longo histórico de edições, com mais de $1 {{PLURAL:$1|edição|edições}}.\nEliminá-la poderá causar problemas na base de dados de {{SITENAME}};\nprossiga com cuidado.",
        "deleteprotected": "Não é possível eliminar esta página porque foi protegida.",
        "deleting-backlinks-warning": "'''Cuidado:''' [[Special:WhatLinksHere/{{FULLPAGENAME}}|outras páginas]] ligam ou redirecionam para a página que você está prestes a eliminar.",
+       "deleting-subpages-warning": "<strong>Aviso:</strong> A página que você está prestes a excluir tem [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|uma subpágina|$1 subpáginas|51=mais de 50 subpáginas}}]].",
        "rollback": "Reverter edições",
        "rollbacklink": "reverter",
        "rollbacklinkcount": "reverter $1 {{PLURAL:$1|edição|edições}}",
        "fileduplicatesearch-noresults": "Não foi encontrado nenhum arquivo com o nome \"$1\".",
        "specialpages": "Páginas especiais",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Páginas especiais normais.\n* <span class=\"mw-specialpagerestricted\">Páginas especiais restritas.</span>",
+       "specialpages-note-restricted": "* Páginas especiais normais.\n* <span class=\"mw-specialpagerestricted\">Páginas especiais restritas.</span>",
        "specialpages-group-maintenance": "Relatórios de manutenção",
        "specialpages-group-other": "Outras páginas especiais",
        "specialpages-group-login": "Entrar / Criar conta",
index e771309..23f56c0 100644 (file)
        "rcfilters-hideminor-conflicts-typeofchange-global": "O filtro \"Edições menores\" entra em conflito com um ou mais filtros de Tipo de Modificação, porque certos tipos de modificações não podem ser classificados como \"menores\". Os filtros em conflito estão marcados na área Filtros Ativos, acima.",
        "rcfilters-hideminor-conflicts-typeofchange": "Certos tipos de modificações não podem ser classificados como \"menores\", portanto este filtro entra em conflito com os seguintes filtros de Tipo de Modificação: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Este filtro de Tipo de Modificação entra em conflito com o filtro \"Edições menores\". Certos tipos de modificações não podem ser classificados como \"menores\".",
-       "rcfilters-filtergroup-lastRevision": "Última revisão",
+       "rcfilters-filtergroup-lastRevision": "Últimas revisões",
        "rcfilters-filter-lastrevision-label": "Última revisão",
-       "rcfilters-filter-lastrevision-description": "A modificação mais recente de uma página.",
-       "rcfilters-filter-previousrevision-label": "Revisões anteriores",
-       "rcfilters-filter-previousrevision-description": "Todas as modificações que não sejam a modificação mais recente de uma página.",
+       "rcfilters-filter-lastrevision-description": "Só a modificação mais recente de uma página.",
+       "rcfilters-filter-previousrevision-label": "Revisões menos a mais recente",
+       "rcfilters-filter-previousrevision-description": "Todas as modificações que não são a \"última revisão\".",
        "rcfilters-filter-excluded": "Excluído",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:não</strong> $1",
        "rcfilters-exclude-button-off": "Excluir os selecionados",
        "fileduplicatesearch-noresults": "Não foi encontrado nenhum ficheiro com o nome \"$1\".",
        "specialpages": "Páginas especiais",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Páginas especiais normais.\n* <span class=\"mw-specialpagerestricted\">Páginas especiais restritas.</span>",
+       "specialpages-note-restricted": "* Páginas especiais normais.\n* <span class=\"mw-specialpagerestricted\">Páginas especiais restritas.</span>",
        "specialpages-group-maintenance": "Relatórios de manutenção",
        "specialpages-group-other": "Outras páginas especiais",
        "specialpages-group-login": "Entrar / criar conta",
index 8937aec..eae1eac 100644 (file)
        "specialpages": "{{doc-special|SpecialPages|unlisted=1}}\nDisplay name of link to [[Special:SpecialPages]] shown on all pages in the toolbox.\n\nSee also:\n* {{msg-mw|Specialpages}}\n* {{msg-mw|Accesskey-t-specialpages}}\n* {{msg-mw|Tooltip-t-specialpages}}\n{{Identical|Special page}}",
        "specialpages-summary": "{{doc-specialpagesummary|specialpages}}",
        "specialpages-note-top": "Heading for {{msg-mw|specialpages-note}}.\n{{Identical|Legend}}",
-       "specialpages-note": "Footer note for the [[Special:SpecialPages]] page",
+       "specialpages-note-restricted": "Footer note for the [[Special:SpecialPages]] page",
+       "specialpages-note-cached": "{{ignore}}\nFooter note for the [[Special:SpecialPages]] page",
        "specialpages-group-maintenance": "{{doc-special-group|like=[[Special:DoubleRedirects]], [[Special:LonelyPages]] and [[Special:WantedPages]]}}",
        "specialpages-group-other": "{{doc-special-group|like=[[Special:AdminLinks]] and [[Special:BookSources]]}}",
        "specialpages-group-login": "{{doc-special-group|like=[[Special:UserLogin]]}}",
index 9da374a..95ac6bd 100644 (file)
        "privacypage": "Project:Tasertit n tusligi",
        "retrievedfrom": "Itwarr-d zi \"$1\"",
        "youhavenewmessages": "Ghar-k / Ghar-m $1 ($2).",
-       "editsection": "Ẓṛeg",
-       "editold": "ẓṛeg",
+       "editsection": "ⵙⵏⴼⵍ",
+       "editold": "ⵙⵏⴼⵍ",
        "viewsourceold": "ẓeṛ aɣbalu",
-       "editlink": "ẓṛg",
+       "editlink": "ⵙⵏⴼⵍ",
        "viewsourcelink": "ẓṛ aghbalu",
        "editsectionhint": "Ẓṛeg tigezmi: $1",
        "toc": "ⵜⵓⵎⴰⵢⵉⵏ",
        "yourpassword": "Tawalt n wadaf:",
        "login": "ⴰⴷⴼ",
        "nav-login-createaccount": "Adef / egg amiḍan",
-       "logout": "Ufugh",
-       "userlogout": "Ufugh",
+       "logout": "ⴼⴼⵖ",
+       "userlogout": "ⴼⴼⵖ",
        "createaccount": "Egg amiḍan",
        "createacct-benefit-body2": "{{PLURAL:$1|ⵜⴰⵙⵏⴰ|ⵜⴰⵙⵏⵉⵡⵉⵏ}}",
        "loginsuccesstitle": "Adaf icna",
        "newarticletext": "Tdefar-d tazdayt n Tasna εad war telli .\nbac ad tegged , arri di taflwit a swadday (xemm i [$1  Tasna n Tallalt] i ineɣmisen ifruryen).\nmala qacek da s ɣalaṭ waha, tecca di tbutunt n '''deffar''' di (browser) inec .",
        "noarticletext": "Rxxu ur din llint ca tira di tasna ya.\nTzmmard [[Special:Search/{{PAGENAME}}|rzu xf yizwl n tasna ya]] di tasniwin nnḍni,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} search the related logs],\nnigh [{{fullurl:{{FULLPAGENAME}}|action=edit}} edit this page]</span>.",
        "previewnote": "'''Wa d Azar-ascan waha;\ntiẓṛigin εad war twaḥḍent!'''",
-       "editing": "Aẓṛag di $1",
-       "editingsection": "Aẓrag  di $1 (tigezmi)",
+       "editing": "ⴰⵙⵏⴼⵍ ⵏ $1",
+       "editingsection": "ⴰⵙⵏⴼⵍ ⵏ $1 (ⵜⵉⴳⵣⵎⵉ)",
        "copyrightwarning": "Maṛṛa tirra di {{SITENAME}} twaggent swadday i $2 (ẓar da $1).\nmala war texsed tirra inac ad twaẓṛegent , ad twamsebḍant .\nUr ten-teg ca da.<br />\ntjadjid-anɣ Ɛawt ila qa d cekk ig yuran manaya, niɣ tesneɣlet-id zi ca n uɣbal nniḍn d alelli.\n'''UR SADDAF CA TIRRA ƔARSENT COPYRIGHTE BLA MA AD-IXES BAB-INES !'''",
        "templatesused": "Timudmiwin itwaggen di Tasna ya:",
        "templatesusedpreview": "Timudmiwin igg itwasxdemen dg uzar-ascan a :",
        "action-read": "ⵖⵔ ⵜⴰⵙⵏⴰ ⴰ",
        "action-edit": "ⵙⵏⴼⵍ ⵜⴰⵙⵏⴰ ⴰ",
        "action-delete": "ⴽⴽⵙ ⵜⴰⵙⵏⴰ ⴰ",
-       "nchanges": "$1 {{PLURAL:$1|tiẓṛegt|tiẓṛigin}}",
+       "nchanges": "$1 {{PLURAL:$1|ⵓⵙⵏⴼⵍ|ⵉⵙⵏⴼⵉⵍⵏ}}",
        "enhancedrc-history": "ⴰⵎⵣⵔⵓⵢ",
        "recentchanges": "Tiẓṛigin tineggura",
        "recentchanges-feed-description": "Bbar tiẓṛigin timayutin n wiki deg usudem(feed) a .",
        "rcshowhideliu": "$1 users ig yudeffen",
        "rcshowhideanons": "$1 users war twasnen",
        "rcshowhidepatr": "Tiẓṛigin ig itwaẓrent di $1",
-       "rcshowhidemine": "$1 tiẓṛigin inu",
+       "rcshowhidemine": "$1 ⵉⵙⵏⴼⵉⵍⵏ ⵉⵏⵓ",
        "rclinks": "Ẓar $1 tiẓṛigin tinggura di $2 n ussan inggura",
        "diff": "imṣebḍan",
        "hist": "ⴰⵎⵣⵔⵓⵢ",
        "filehist": "Amzruy n usatul",
        "filehist-help": "Tka di date/time bac ad tẓerd afaylu mamec ja d-itban di Lwaqt a .",
        "filehist-deleteall": "ⴽⴽⵙ ⵎⴰⵔⵔⴰ",
-       "filehist-deleteone": "sfaḍ",
+       "filehist-deleteone": "ⴽⴽⵙ",
        "filehist-current": "ⴰⵎⵉⵔⴰⵏ",
        "filehist-datetime": "ⴰⵙⴰⴽⵓⴷ/ⴰⴽⵓⴷ",
        "filehist-user": "Aseqdac",
        "filehist-dimensions": "Tisektiwin",
-       "filehist-filesize": "Tiddi n ufaylu",
+       "filehist-filesize": "ⵜⵉⴷⴷⵉ ⵏ ⵓⴼⴰⵢⵍⵓ",
        "filehist-comment": "ⴰⵅⴼⴰⵡⴰⵍ",
        "imagelinks": "Aseqdec usatul",
        "linkstoimage": "{{PLURAL:$1|Tasna ya teqn-ad|$1 Tasniwin a qnent-id}} ɣa ufaylu ya :",
        "statistics-pages": "ⵜⴰⵙⵏⵉⵡⵉⵏ",
        "doubleredirects": "(redirects) ɛɛawdent",
        "brokenredirects": "(redirects) arẓent",
-       "brokenredirects-edit": "arri",
+       "brokenredirects-edit": "ⵙⵏⴼⵍ",
        "brokenredirects-delete": "ⴽⴽⵙ",
        "withoutinterwiki": "Tasna bla tiẓdayin n tutlayt",
        "withoutinterwiki-submit": "Smmrad",
        "actioncomplete": "Tiggawt tsala",
        "deletedtext": "\"$1\" Twakkes.\nXemm $2 i tikkas timaynutin.",
        "dellogpage": "Aɣmis n uṣfaḍ",
-       "deletecomment": "Ssebba:",
+       "deletecomment": "ⵜⴰⵎⵏⵜⵉⵍⵜ:",
        "deleteotherreason": "Ca n ssebba nniḍn:",
        "deletereasonotherlist": "Ssebba nniḍn",
        "rollbacklink": "Sdwl ghar dffar",
        "whatlinkshere-hidelinks": "$1 timqqan",
        "blockip": "Sbdd asqdac a",
        "ipbreason": "ⵜⴰⵎⵏⵜⵉⵍⵜ:",
-       "ipboptions": "2 n timirin:2 hours,1 n wass:1 day,3 n wussan:3 days,1 imalass:1 week,2 imallassn:2 weeks,1 wayur:1 month,3 wayurn:3 months,6 wayurn:6 months,1 asggwas:1 year,tartalla:infinite",
+       "ipboptions": "2 ⵜⵙⵔⴰⴳⵉⵏ:2 hours,1 ⵡⴰⵙⵙ:1 day,3 ⵡⵓⵙⵙⴰⵏ:3 days,1 ⵉⵎⴰⵍⴰⵙⵙ:1 week,2 ⵉⵎⴰⵍⴰⵙⵙⵏ:2 weeks,1 ⵡⴰⵢⵢⵓⵓⵔ:1 month,3 ⵡⴰⵢⵢⵓⵔⵏ:3 months,6 ⵡⴰⵢⵢⵓⵔⵏ:6 months,1 ⵓⵙⴳⴳⵯⴰⵙ:1 year,ⵍⴱⴷⴰ:infinite",
        "autoblocklist-submit": "ⵔⵣⵓ",
        "ipblocklist": "Tabdart n tansiwin IP d isemawen n iseqdacen ig iteblukan",
        "blocklist-reason": "ⵜⴰⵎⵏⵜⵉⵍⵜ",
index 46196c1..1161326 100644 (file)
        "fileduplicatesearch-noresults": "Betg chattà ina datoteca cun il num \"$1\".",
        "specialpages": "Paginas spezialas",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Paginas spezialas normalas.\n* <span class=\"mw-specialpagerestricted\">Paginas spezialas restrenschidas.</span>",
        "specialpages-group-maintenance": "Rapports da mantegnamant",
        "specialpages-group-other": "Autras paginas spezialas",
        "specialpages-group-login": "S'annunziar / crear in conto",
index 55d3bdb..a24c6c2 100644 (file)
        "rcfilters-noresults-conflict": "Nu s-au găsit rezultate deoarece criteriile de căutare sunt în conflict",
        "rcfilters-state-message-subset": "Acest filtru nu are efecte deoarece rezultatele sale sunt incluse în filtrele cu selectie mai largă {{PLURAL:$2|filtru|filtre}} (încercați să subliniați pentru a o deosebi): $1",
        "rcfilters-state-message-fullcoverage": "Selectarea tuturor filtrelor dintr-un grup este aceeași cu cea selectată, astfel încât acest filtru nu are efect. Grupul include: $1",
-       "rcfilters-filtergroup-registration": "Înregistrare utilizator",
-       "rcfilters-filter-registered-label": "Înregistrat",
-       "rcfilters-filter-registered-description": "Editorii conectați.",
-       "rcfilters-filter-unregistered-label": "Neînregistrat",
-       "rcfilters-filter-unregistered-description": "Editorii care nu sunt conectați.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Acest filtru contravine {{PLURAL:$2|filtru|filtre}} de Experiență, care {{PLURAL:$2|găsesc|gasește}} doar userii inreistrați: $1",
        "rcfilters-filtergroup-authorship": "Contribuția autorului",
        "rcfilters-filter-editsbyself-label": "Modificările tale",
        "rcfilters-filter-editsbyself-description": "Contribuțiile tale.",
        "rcfilters-filter-editsbyother-label": "Contribuțiile altora",
        "rcfilters-filter-editsbyother-description": "Toate modificările mai puțin ale tale.",
        "rcfilters-filtergroup-userExpLevel": "Nivel de experiență (numai pentru utilizatorii înregistrați)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Filtrele Experiență găsesc numai utilizatori înregistrați, deci acest filtru este în conflict cu filtrul \"Înregistrat\".",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Filtrul \"Înregistrat\" ​​este în conflict cu unul sau mai multe filtre Experiență, care găsește numai utilizatorii înregistrați. Filtrele conflictuale sunt marcate în zona Filtre active, de mai sus.",
+       "rcfilters-filter-user-experience-level-registered-label": "Înregistrat",
+       "rcfilters-filter-user-experience-level-registered-description": "Editorii conectați.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Neînregistrat",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Editorii care nu sunt conectați.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Nou veniți",
        "rcfilters-filter-user-experience-level-newcomer-description": "Mai puțin de 10 editări și 4 zile de activitate.",
        "rcfilters-filter-user-experience-level-learner-label": "Cursanți",
        "fileduplicatesearch-noresults": "Nu s-a găsit niciun fișier cu numele „$1”.",
        "specialpages": "Pagini speciale",
        "specialpages-note-top": "Legendă",
-       "specialpages-note": "* Pagini speciale normale.\n* <span class=\"mw-specialpagerestricted\">Pagini speciale restricționate.</span>",
        "specialpages-group-maintenance": "Întreținere",
        "specialpages-group-other": "Alte pagini speciale",
        "specialpages-group-login": "Autentificare / creare cont",
        "logentry-delete-delete": "$1 {{GENDER:$2|a șters}} pagina $3",
        "logentry-delete-delete_redir": "$1 {{GENDER:$2|a șters}} pagina de redirecționare $3 prin suprascriere",
        "logentry-delete-restore": "$1 {{GENDER:$2|a restaurat}} pagina $3 ($4)",
-       "restore-count-files": "{{PLURAL:$1|1 fișier|$1 fișiere}}",
+       "restore-count-revisions": "{{PLURAL:$1|1 versiune|$1 versiuni|$1 de versiuni}}",
+       "restore-count-files": "{{PLURAL:$1|1 fișier|$1 fișiere|$1 de fișiere}}",
        "logentry-delete-event": "$1 {{GENDER:$2|a schimbat}} vizibilitatea {{PLURAL:$5|unui eveniment din jurnal|a $5 evenimente din jurnal|a $5 de evenimente din jurnal}} pentru $3: $4",
        "logentry-delete-revision": "$1 {{GENDER:$2|a schimbat}} vizibilitatea {{PLURAL:$5|unei versiuni|a $5 versiuni|a $5 de versiuni}} pentru pagina $3: $4",
        "logentry-delete-event-legacy": "$1 {{GENDER:$2|a modificat}} vizibilitatea evenimentelor din jurnal pentru $3",
index fe2444a..41c9360 100644 (file)
        "changepassword-success": "'A password toje ha state cangiate!",
        "changepassword-throttled": "Tu è pruvate 'nu sacche de vote a trasè.\nPe piacere aspitte $1 apprime de pruvà arrete.",
        "botpasswords": "Password d'u bot",
+       "botpasswords-existing": "Passuord de le bot esistende",
+       "botpasswords-createnew": "Ccreje 'na passuord nove pu bot",
+       "botpasswords-editexisting": "Cange 'na passuord d'u bot ca esiste ggià",
        "botpasswords-label-appid": "Nome d'u bot:",
        "botpasswords-label-create": "Ccreje",
        "botpasswords-label-update": "Aggiorne",
        "botpasswords-updated-title": "Passuord d'u bot cangiate",
        "botpasswords-deleted-title": "Passuord d'u bot scangellate",
        "resetpass_forbidden": "Le Password non ge ponne cangià",
+       "resetpass_forbidden-reason": "Le passuord non ge ponne essere cangiate: $1",
        "resetpass-no-info": "Tu a essere colleghete pe accedere a sta pàgene direttamende.",
        "resetpass-submit-loggedin": "Cange 'a password",
        "resetpass-submit-cancel": "Annulle",
        "minoredit": "Cangiaminde stuèdeche",
        "watchthis": "Condrolle sta pàgene",
        "savearticle": "Registre 'a vôsce",
+       "savechanges": "Reggistre le cangiaminde",
+       "publishpage": "Pubbleche 'a pàgene",
+       "publishchanges": "Pubbleche le cangiaminde",
        "preview": "Andeprime",
        "showpreview": "Vide l'andeprime",
        "showdiff": "Fa vedè le cangiaminde",
        "invalid-content-data": "Condenute d'u date invalide",
        "content-not-allowed-here": "\"$1\" condenute non g'è permesse sus 'a pàgene [[$2]]",
        "editwarning-warning": "Assenne da sta pàgene tu puè perdè tutte le date ca è cangiate.\nCe tu è trasute, tu puè disabbilità st'avvertimende jndr'à sezione \"{{int:prefs-editing}}\" de le preferenze tune.",
+       "editpage-invalidcontentmodel-title": "'U Modelle d'u condenute non gè supportate",
+       "editpage-invalidcontentmodel-text": "'U modelle d'u condenute \"$1\" non g'è supportate.",
        "editpage-notsupportedcontentformat-title": "'U formate d'u condenute non gè supportate",
        "editpage-notsupportedcontentformat-text": "'U formate d'u condenute $1 non g'è supportate da 'u modelle de condenute $2.",
        "content-model-wikitext": "Uicchiteste",
        "grant-group-file-interaction": "Inderaggisce cu le media",
        "grant-group-watchlist-interaction": "Inderaggisce cu le pàggene condrollate",
        "grant-group-email": "Manne 'n'e-mail",
+       "grant-createaccount": "Ccreje le cunde utinde",
+       "grant-createeditmovepage": "Ccreje, cange e spueste le pàggene",
+       "grant-delete": "Scangille pàggene, revisiune e vôsce de l'archivije",
        "newuserlogpage": "Archivije de ccreazione de le utinde",
        "newuserlogpagetext": "Quiste ète l'archivije de le creazziune de l'utinde.",
        "rightslog": "Archivie de le diritte de l'utende",
        "recentchanges-submit": "Fà 'ndrucà",
        "rcfilters-activefilters": "Filtre attive",
        "rcfilters-advancedfilters": "Filtre avanzate",
+       "rcfilters-limit-title": "Cangiaminde da 'ndrucà",
+       "rcfilters-limit-shownum": "Fà 'ndrucà le urteme $1 cangiaminde",
+       "rcfilters-days-title": "Urteme sciurne",
+       "rcfilters-hours-title": "Urteme ore",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|sciurne}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|ore}}",
        "rcfilters-quickfilters": "Filtre reggistrate",
        "rcfilters-quickfilters-placeholder-title": "Nisciune collegamende reggistrate",
+       "rcfilters-savedqueries-defaultlabel": "Filtre reggistrate",
+       "rcfilters-savedqueries-rename": "Renomene",
+       "rcfilters-savedqueries-setdefault": "'Mboste cumme predefinite",
+       "rcfilters-savedqueries-unsetdefault": "Live cumme predefinite",
+       "rcfilters-savedqueries-remove": "Live",
+       "rcfilters-savedqueries-new-name-label": "Nome",
+       "rcfilters-savedqueries-new-name-placeholder": "Dì a ce serve 'u filtre",
        "rcfilters-savedqueries-apply-label": "Ccrèje 'nu filtre",
+       "rcfilters-savedqueries-cancel-label": "Annulle",
+       "rcfilters-filterlist-title": "Filtre",
+       "rcfilters-filterlist-whatsthis": "Cumme funzionane?",
+       "rcfilters-highlightmenu-title": "Scacchie 'nu culore",
+       "rcfilters-highlightmenu-help": "Scacchie 'nu culore pe evidenzià sta probbietà",
+       "rcfilters-filterlist-noresults": "Nisciune filtre acchiate",
+       "rcfilters-filter-bots-label": "Bot",
+       "rcfilters-filter-patrolled-label": "Condrollate",
+       "rcfilters-filter-patrolled-description": "Cangiaminde signate cumme condrollate.",
+       "rcfilters-filter-unpatrolled-label": "Non condrollate",
+       "rcfilters-filter-unpatrolled-description": "Cangiaminde non signate cumme condrollate.",
+       "rcfilters-filtergroup-significance": "Significate",
+       "rcfilters-filter-minor-label": "Cangiaminde stuèdeche",
        "rcnotefrom": "Sotte {{PLURAL:$5|ste 'u cangiamende|stonne le cangiaminde}} da <strong>$3, $4</strong> ('nzigne a <strong>$1</strong> fatte vedè).",
        "rclistfrom": "Fà vedè le urteme cangiaminde partenne da $3 $2",
        "rcshowhideminor": "$1 cangiaminde stuèdeche",
        "rcshowhidemine": "$1 cangiaminde mie",
        "rcshowhidemine-show": "Fà vedè",
        "rcshowhidemine-hide": "Scunne",
+       "rcshowhidecategorization-show": "Fà 'ndrucà",
+       "rcshowhidecategorization-hide": "Scunne",
        "rclinks": "Vide l'urteme $1 cangiaminde jndr'à l'urteme $2 sciurne",
        "diff": "diff",
        "hist": "cunde",
        "mostrevisions": "Pàggene cchiù cangete",
        "prefixindex": "Tutte le pàggene cu 'u prefisse",
        "prefixindex-namespace": "Tutte le pàggene cu 'u prefisse ($1 namespace)",
+       "prefixindex-submit": "Fà 'ndrucà",
        "prefixindex-strip": "Strisce d'u prefisse jndr'à l'elenghe",
        "shortpages": "Pàggene corte",
        "longpages": "Pàggene longhe",
        "protectedpages-performer": "Stoche a protegge l'utende",
        "protectedpages-params": "Parametre de protezzione",
        "protectedpages-reason": "Mutive",
+       "protectedpages-submit": "Fà 'ndrucà le pàggene",
        "protectedpages-unknown-timestamp": "Scanusciute",
        "protectedpages-unknown-performer": "Utende scanusciute",
        "protectedtitles": "Titele prutette",
        "protectedtitles-summary": "Sta pàgene elenghe le titole ca so prutette da 'a ccrejazzione. Pe 'n'elenghe de le pàggene ca sò prutette, 'ndruche [[{{#special:ProtectedPages}}|{{int:protectedpages}}]].",
        "protectedtitlesempty": "Nisciune titele jè pe mò prutette cu ste parametre.",
+       "protectedtitles-submit": "Fà 'ndrucà le titole",
        "listusers": "Liste de l'utende",
        "listusers-editsonly": "Fà vedè sulamende l'utinde cu cangiaminde fatte",
        "listusers-creationsort": "Arrenghete pe date de ccreazione",
        "usereditcount": "$1 {{PLURAL:$1|cangiamende|cangiaminde}}",
        "usercreated": "{{GENDER:$3|Ccrejate}} 'u $1 a le ore $2",
        "newpages": "Pàggene nuève",
+       "newpages-submit": "Fà 'ndrucà",
        "newpages-username": "Nome de l'utende:",
        "ancientpages": "Pàggene vìcchje",
        "move": "Spuèste",
        "apisandbox-intro": "Ause sta pàgene pe sperimendà cu le <strong>API de le web service pe MediaUicchi</strong>.\nFà referimende a [[mw:API:Main page| 'a documendazione de l'API]] pe cchiù dettaglie de l'ause de l'API.\nEsembie: [https://www.mediawiki.org/wiki/API#A_simple_example pigghie 'u condenute d'a Pàgene Prengepàle]. Scacchie 'n'azione pe 'ndrucà otre esembie.\n\nVide ca, pure ca queste jè 'na buatte de sabbie tu puè carrescià le cangiaminde de sta pàgene sus 'a uicchi.",
        "apisandbox-submit": "Fà 'na richieste",
        "apisandbox-reset": "Pulizze",
+       "apisandbox-retry": "Pruève arrete",
        "apisandbox-examples": "Esembie",
        "apisandbox-results": "Resultate",
        "apisandbox-request-url-label": "URL richieste:",
        "apisandbox-request-time": "Tiembe cercate: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-continue": "Condinue",
+       "apisandbox-continue-clear": "Pulizze",
        "booksources": "Sorgende de le libbre",
        "booksources-search-legend": "Cirche pe le fonde de le libbre",
        "booksources-isbn": "ISBN:",
        "fileduplicatesearch-noresults": "Nisciune file chiamate \"$1\" ha state acchiate.",
        "specialpages": "Pàggene speciele",
        "specialpages-note-top": "Leggende",
-       "specialpages-note": "* Pàggene speciale normale.\n* <span class=\"mw-specialpagerestricted\">Pàggene speciale cu le restriziune.</span>",
        "specialpages-group-maintenance": "Report d'a manutenzione",
        "specialpages-group-other": "Otre pàggene speciele",
        "specialpages-group-login": "Tràse / Reggistrate",
index 6606815..f712a14 100644 (file)
        "rcfilters-legend-heading": "<strong>Список сокращений:</strong>",
        "rcfilters-activefilters": "Активные фильтры",
        "rcfilters-advancedfilters": "Расширенные фильтры",
+       "rcfilters-limit-title": "Изменения для показа",
+       "rcfilters-days-title": "Последние дни",
+       "rcfilters-hours-title": "Последние часы",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|день|дня|дней}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|час|часа|часов}}",
        "rcfilters-quickfilters": "Сохранённые фильтры",
        "rcfilters-quickfilters-placeholder-title": "Сохраненных ссылок еще нет",
        "rcfilters-quickfilters-placeholder-description": "Чтобы сохранить настройки фильтра и повторно использовать их позже, щелкните значок закладки в области «Активный фильтр» ниже.",
        "rcfilters-invalid-filter": "Недопустимый фильтр",
        "rcfilters-empty-filter": "Нет активных фильтров. Показываются все правки.",
        "rcfilters-filterlist-title": "Фильтры",
-       "rcfilters-filterlist-whatsthis": "ЧÑ\82о Ñ\8dÑ\82о?",
+       "rcfilters-filterlist-whatsthis": "Ð\9aак Ñ\8dÑ\82о Ñ\80абоÑ\82аеÑ\82?",
        "rcfilters-filterlist-feedbacklink": "Оставить отзыв о новых (бета) фильтрах",
        "rcfilters-highlightbutton-title": "Выделить результаты",
        "rcfilters-highlightmenu-title": "Выберите цвет",
        "rcfilters-filter-editsbyself-description": "Ваш вклад.",
        "rcfilters-filter-editsbyother-label": "Изменения, внесённые другими участниками",
        "rcfilters-filter-editsbyother-description": "Все правки, кроме ваших собственных.",
-       "rcfilters-filtergroup-userExpLevel": "УÑ\80овнÑ\8f Ð¾Ð¿Ñ\8bÑ\82а (Ñ\82олÑ\8cко Ð´Ð»Ñ\8f Ð·Ð°Ñ\80егиÑ\81Ñ\82Ñ\80иÑ\80ованнÑ\8bÑ\85 Ñ\83Ñ\87аÑ\81Ñ\82ников)",
+       "rcfilters-filtergroup-userExpLevel": "РегиÑ\81Ñ\82Ñ\80аÑ\86иÑ\8f Ñ\83Ñ\87аÑ\81Ñ\82ника Ð¸ ÐµÐ³Ð¾ Ð¾Ð¿Ñ\8bÑ\82",
        "rcfilters-filter-user-experience-level-registered-label": "Зарегистрированные",
        "rcfilters-filter-user-experience-level-registered-description": "Вошедшие редакторы.",
        "rcfilters-filter-user-experience-level-unregistered-label": "Незарегистрированные",
        "rcfilters-filter-user-experience-level-unregistered-description": "Редакторы, которые не вошли в систему.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Новички",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Ð\9cенее 10 Ð¿Ñ\80авок Ð¸ 4 Ð´Ð½ÐµÐ¹ работы.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Ð\97аÑ\80егиÑ\81Ñ\82Ñ\80иÑ\80ованнÑ\8bе Ñ\80едакÑ\82оÑ\80Ñ\8b Ñ\81 Ð¼ÐµÐ½ÐµÐµ Ñ\87ем 10 Ð¿Ñ\80авками Ð¸ 4 Ð´Ð½Ñ\8fми работы.",
        "rcfilters-filter-user-experience-level-learner-label": "Учащиеся",
-       "rcfilters-filter-user-experience-level-learner-description": "Ð\91олÑ\8cÑ\88е Ð¾Ð¿Ñ\8bÑ\82а, Ñ\87ем Ñ\83 Â«Ð\9dовиÑ\87ков», Ð½Ð¾ Ð¼ÐµÐ½Ñ\8cÑ\88е, Ñ\87ем Ñ\83 Â«Ð\9eпÑ\8bÑ\82нÑ\8bÑ\85 Ð¿Ð¾Ð»Ñ\8cзоваÑ\82елей».",
+       "rcfilters-filter-user-experience-level-learner-description": "Ð\97аÑ\80егиÑ\81Ñ\82Ñ\80иÑ\80ованнÑ\8bе Ñ\80едакÑ\82оÑ\80Ñ\8b, Ñ\87ей Ð¾Ð¿Ñ\8bÑ\82 Ð½Ð°Ñ\85одиÑ\82Ñ\81Ñ\8f Ð³Ð´Ðµ-Ñ\82о Ð¼ÐµÐ¶Ð´Ñ\83 Ñ\83Ñ\80овнÑ\8fми Â«Ð\9dовиÑ\87ок» Ð¸ Â«Ð\9eпÑ\8bÑ\82нÑ\8bе Ð¿Ð¾Ð»Ñ\8cзоваÑ\82ели».",
        "rcfilters-filter-user-experience-level-experienced-label": "Опытные пользователи",
-       "rcfilters-filter-user-experience-level-experienced-description": "Ð\91олее 30 Ð´Ð½ÐµÐ¹ Ð°ÐºÑ\82ивноÑ\81Ñ\82и Ð¸ 500 Ð¿Ñ\80авок.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Ð\97аÑ\80егиÑ\81Ñ\82Ñ\80иÑ\80ованнÑ\8bе Ñ\80едакÑ\82оÑ\80Ñ\8b Ñ\81 Ð±Ð¾Ð»ÐµÐµ Ñ\87ем 500 Ð¿Ñ\80авок Ð¸ 30 Ð´Ð½Ñ\8fми Ð°ÐºÑ\82ивноÑ\81Ñ\82и.",
        "rcfilters-filtergroup-automated": "Автоматизированные вклады",
        "rcfilters-filter-bots-label": "Бот",
        "rcfilters-filter-bots-description": "Правки, сделанные с помощью автоматизированных инструментов.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "Фильтр \"малые правки\" конфликтует с одним или несколькими фильтрами, поскольку некоторые типы правок не могут быть названы малыми. Конфликтные фильтры отмечены вверху, в области Активных фильтров.",
        "rcfilters-hideminor-conflicts-typeofchange": "Определённые типы правок не могут быть названы «малыми», поэтому этот фильтр конфликтует со следующим фильтром типа правок: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Этот фильтр типа правок конфликтует с фильтром малых правок. Определённые типы правок не могут быть отмечены «малыми».",
-       "rcfilters-filtergroup-lastRevision": "ТекÑ\83Ñ\89аÑ\8f Ð²ÐµÑ\80Ñ\81иÑ\8f",
+       "rcfilters-filtergroup-lastRevision": "Ð\9fоÑ\81ледние Ð²ÐµÑ\80Ñ\81ии",
        "rcfilters-filter-lastrevision-label": "Текущая версия",
-       "rcfilters-filter-lastrevision-description": "Самое последнее изменение на странице.",
-       "rcfilters-filter-previousrevision-label": "Ð\91олее Ñ\80анние Ð²ÐµÑ\80Ñ\81ии",
-       "rcfilters-filter-previousrevision-description": "Все правки, не являющиеся самыми последними на странице.",
+       "rcfilters-filter-lastrevision-description": "ТолÑ\8cко Ñ\81амое последнее изменение на странице.",
+       "rcfilters-filter-previousrevision-label": "Ð\9dе Ð¿Ð¾Ñ\81леднÑ\8fÑ\8f Ð²ÐµÑ\80Ñ\81иÑ\8f",
+       "rcfilters-filter-previousrevision-description": "Все правки, не являющиеся «последней версией».",
        "rcfilters-filter-excluded": "Исключено",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:not</strong> $1",
+       "rcfilters-exclude-button-off": "Исключить выбранное",
+       "rcfilters-exclude-button-on": "Исключение выбранного",
        "rcfilters-view-tags": "Тегированные правки",
        "rcfilters-view-namespaces-tooltip": "Результаты фильтра по пространствам имён",
        "rcfilters-view-tags-tooltip": "Результаты фильтра, использующего метки правок",
        "fileduplicatesearch-noresults": "Не найден файл с именем «$1».",
        "specialpages": "Спецстраницы",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Обычные служебные страницы.\n* <span class=\"mw-specialpagerestricted\">Служебные страницы с ограниченным доступом.</span>",
        "specialpages-group-maintenance": "Отчёты технического обслуживания",
        "specialpages-group-other": "Другие служебные страницы",
        "specialpages-group-login": "Представиться / Зарегистрироваться",
index 010223b..907e6c7 100644 (file)
        "anontalk": "بحث",
        "navigation": "رھنمائي",
        "and": "&#32؛۽",
-       "qbfind": "ڳوليو",
-       "qbbrowse": "جھانگيو",
-       "qbedit": "سنواريو",
-       "qbpageoptions": "هيءُ صفحو",
-       "qbmyoptions": "منهنجا صفحا",
        "faq": "ڪپس",
-       "faqpage": "Project:ڪپس",
        "actions": "ڪارگذاريون",
        "namespaces": "نانءُپولارَ",
        "variants": "بَدَلَ",
        "edit-local": "مقامي تشريح کي ترميميو",
        "create": "سرجيو",
        "create-local": "مقامي تشريح ڏيو",
-       "editthispage": "هيءُ صفحو سنواريو",
-       "create-this-page": "اهو صفحو نئين سر جوڙيو",
        "delete": "ڊاھيو",
-       "deletethispage": "هيءُ صفحو ڊاهيو",
-       "undeletethispage": "هيءُ صفحو اڻ ڊاهيو",
        "undelete_short": "اڻڊاهيو {{PLURAL:$1|هڪ ترميم|$1 ترميمون}}",
        "viewdeleted_short": "ڏسو {{PLURAL:$1|هڪ ڊاٺل ترميم|$1 ڊاٺل ترميمون}}",
        "protect": "تحفظيو",
        "protect_change": "تبديل ڪريو",
-       "protectthispage": "هيءُ صفحو تحفظيو",
        "unprotect": "تحفظ بدلايو",
-       "unprotectthispage": "هن صفحي جو تحفظ بدلايو",
        "newpage": "نئون صفحو",
-       "talkpage": "هن صفحي تي بحث ڪريو",
        "talkpagelinktext": "بحث",
        "specialpage": "خاص صفحو",
        "personaltools": "ذاتي اوزار",
-       "articlepage": "مسودو ڏسو",
        "talk": "بحث",
        "views": "ڏيٺون",
        "toolbox": "اوزارَ",
        "tool-link-userrights": "{{GENDER:$1|يوزر}} گروھ تبديل ڪريو",
        "tool-link-userrights-readonly": "{{GENDER:$1|يوزر}} گروھ ڏسو",
        "tool-link-emailuser": "ھن {{GENDER:$1|يوزر}} ڏانھن برقٽپال موڪليو",
-       "userpage": "يوزر صفحو ڏسو",
-       "projectpage": "رٿائي صفحو ڏسو",
        "imagepage": "ذريعاتي صفحو ڏسو",
        "mediawikipage": "نياپي جو صفحو ڏسو",
        "templatepage": "سانچي جو صفحو ڏسو",
        "rcfilters-search-placeholder": "تازيون تبديليون ڇاڻيو (جھانگيو يا لکڻ شروع ڪريو)",
        "rcfilters-empty-filter": "ڪي بہ سرگرم ڇاڻيون ناھن. سڀ ڀاڱيداريون ڏيکاريل آھن.",
        "rcfilters-filterlist-title": "ڇاڻيون",
-       "rcfilters-filterlist-whatsthis": "Ù\87Ù\8a Ú\87ا Ø¢Ù\87Ù\8a؟",
+       "rcfilters-filterlist-whatsthis": "Ù\87Ù\8a ÚªÙ\8aئÙ\86 ÚªÙ\85 ÚªÙ\86 Ù¿Ø§؟",
        "rcfilters-highlightbutton-title": "نتيجن کي نمايان (هاءِ لائيٽ) ڪيو",
        "rcfilters-highlightmenu-title": "رنگ چونڊيو",
-       "rcfilters-filter-registered-label": "رجسٽر ٿيل",
-       "rcfilters-filter-unregistered-label": "اڻ رجسٽر ٿيل",
        "rcfilters-filter-editsbyself-label": "مون پاران تبديليون",
        "rcfilters-filter-editsbyother-label": "ٻين پاران تبديليون",
+       "rcfilters-filter-user-experience-level-registered-label": "رجسٽر ٿيل",
+       "rcfilters-filter-user-experience-level-unregistered-label": "اڻرجسٽر ٿيل",
        "rcfilters-filter-user-experience-level-newcomer-label": "نوان ايندڙ",
        "rcfilters-filter-user-experience-level-learner-label": "سکندڙ",
        "rcfilters-filter-user-experience-level-experienced-label": "تجربيڪار واھپ",
        "tooltip-compareselectedversions": "هن صفحي جن ٻن چونڊيل پرتن درميان تفاوت ڏسو.",
        "tooltip-watch": "هيءُ صفحو پنهنجي نظر ۾ فھرست ۾ شامل ڪريو",
        "tooltip-rollback": "\"واپس ورايو\" ھن صفحي ۾ پوئين ڀاڱيدار جي ڪيل ترميم(ن) کي ھڪ ٽڙڪ سان اڻڪري ٿو",
+       "tooltip-preferences-save": "ترجيحون سانڍيو",
        "tooltip-summary": "ننڍو خلاصو ڏيو",
        "anonymous": "گمنام {{PLURAL:$1|يوزر|يوزرس}} جو {{SITENAME}}",
        "simpleantispam-label": "اينٽي-اسپام روڪ.\nھن کي <strong>نہ</strong> ڀريو!",
index 368e648..824851e 100644 (file)
        "retrievedfrom": "Yurrid z \"$1\"",
        "youhavenewmessages": "{{PLURAL:$3|{{GENDER:$3|ⴷⴰⵔⴽ|ⴷⴰⵔⵎ}}}} $1 ($2).",
        "newmessageslinkplural": "{{PLURAL:$1|ⵜⵓⵣⵉⵏⵜ ⵜⴰⵎⴰⵢⵏⵓⵜ|ⵜⵓⵣⵉⵏⵉⵏ ⵜⵉⵎⴰⵢⵏⵓⵜⵉⵏ}}",
-       "newmessagesdifflinkplural": "{{PLURAL:$1|â´°âµ\99âµ\8fâ´¼âµ\8d âµ\89ⴳⴳⵯâµ\94â´°âµ\8f|âµ\89âµ\99âµ\8fâ´¼ⵍⵏ ⴳⴳⵯⵔⴰⵏⵉⵏ}}",
+       "newmessagesdifflinkplural": "{{PLURAL:$1|âµ\93âµ\99âµ\8fâ´¼âµ\8d âµ\89ⴳⴳⵯâµ\94â´°âµ\8f|âµ\89âµ\99âµ\8fâ´¼âµ\89ⵍⵏ ⴳⴳⵯⵔⴰⵏⵉⵏ}}",
        "youhavenewmessagesmulti": "{{GENDER:|ⴷⴰⵔⴽ|ⴷⴰⵔⵎ}} ⵜⵓⵣⵉⵏⵉⵏ ⵜⵉⵎⴰⵢⵏⵓⵜⵉⵏ ⴳ $1",
        "editsection": "ⵙⵏⴼⵍ",
        "editold": "ⵙⵏⴼⵍ",
        "notloggedin": "Ur tmlit mat git",
        "createaccount": "Murzm amidan nek (lkunt)..",
        "createaccountmail": "S tirawt taliktunant",
-       "createacct-benefit-body1": "{{PLURAL:$1|ⴰⵙⵏⴼⵍ|ⵉⵙⵏⴼⵍⵏ}}",
+       "createacct-benefit-body1": "{{PLURAL:$1|â´°âµ\99âµ\8fâ´¼âµ\8d|âµ\89âµ\99âµ\8fâ´¼âµ\89âµ\8dâµ\8f}}",
        "createacct-benefit-body2": "{{PLURAL:$1|ⵜⴰⵙⵏⴰ|ⵜⴰⵙⵏⵉⵡⵉⵏ}}",
        "createacct-benefit-body3": "{{PLURAL:$1|ⴰⵏⴰⵎⵓ ⵉⴳⴳⵯⵔⴰⵏ|ⵉⵏⴰⵎⵓⵜⵏ ⴳⴳⵯⵔⴰⵏⵉⵏ}}",
        "badretype": "Tasarut lin tgit ur dis tucka.",
        "action-delete": "ⴽⴽⵙ ⵜⴰⵙⵏⴰ ⴰⴷ",
        "nchanges": "$1 {{PLURAL:$1|ⵓⵙⵏⴼⵍ|ⵉⵙⵏⴼⵍⵏ}}",
        "enhancedrc-history": "ⴰⵎⵣⵔⵓⵢ",
-       "recentchanges": "ⵉⵙⵏⴼⵍⵏ ⴳⴳⵯⵔⴰⵏⵉⵏ",
+       "recentchanges": "âµ\89âµ\99âµ\8fâ´¼âµ\89âµ\8dâµ\8f â´³â´³âµ¯âµ\94â´°âµ\8fâµ\89âµ\8f",
        "recentchanges-legend": "Tixtiɣitin (options) n imbddl imaynutn",
        "recentchanges-summary": "Ml imbddln imaynutn  n wiki ɣ tasna yad",
        "recentchanges-feed-description": "ⴹⴼⵓⵔ ⵉⵙⵏⴼⵍⵏ ⴰⴽⴽⵯ ⵉⴳⴳⵯⵔⴰⵏ ⵏ ⵓⵡⵉⴽⵉ ⴳ ⵉⴼⵉⵍⵉ ⴰⴷ.",
        "recentchanges-label-minor": "ⵡⴰⴷ ⵉⴳⴰ ⴰⵙⵏⴼⵍ ⵓⵎⵥⵉⵢ",
        "recentchanges-label-bot": "ⴰⵙⵏⴼⵍ ⴰⴷ ⵉⵙⴽⵔ ⵜ ⵢⴰⵏ ⵓⵔⵓⴱⵓ",
        "recentchanges-label-unpatrolled": "Ambddl ad ura jju ittmẓra",
+       "recentchanges-label-plusminus": "ⵜⵏⴼⵍ ⵜⵉⴷⴷⵉ ⵏ ⵜⴰⵙⵏⴰ ⵙ ⵡⵓⵟⵟⵓⵏ ⴰⴷ ⵏ ⵉⴷ ⴱⴰⵢⵜ",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (ⵥⵔ ⵓⵍⴰ [[Special:NewPages|ⵜⴰⵍⴳⴰⵎⵜ ⵏ ⵜⴰⵙⵏⵉⵡⵉⵏ ⵜⵉⵎⴰⵢⵏⵓⵜⵉⵏ]])",
        "rcfilters-savedqueries-new-name-label": "ⵉⵙⵎ",
        "rcfilters-filterlist-whatsthis": "ⵎⴰⵜⵜⴰ ⵓⵢⴰ?",
        "rcfilters-filter-bots-label": "ⴱⵓⵜ",
        "rcnotefrom": "Had imbddln lli ittuyskarn z '''$2''' ('''$1''' ɣ uggar).",
        "rclistfrom": "Mel imbdeltn imaynutn z $3 $2",
-       "rcshowhideminor": "$1 ⵉⵙⵏⴼⵍⵏ ⵓⵎⵥⵉⵢⵏ",
+       "rcshowhideminor": "$1 âµ\89âµ\99âµ\8fâ´¼âµ\89âµ\8dâµ\8f âµ\93âµ\8eâµ¥âµ\89âµ¢âµ\8f",
        "rcshowhideminor-hide": "ⵙⵙⵏⵜⵍ",
        "rcshowhidebots": "$1 ⵉⴷ ⴱⵓⵜ",
        "rcshowhidebots-hide": "ⵙⵙⵏⵜⵍ",
        "recentchangeslinked": "Imbddel zun ɣwid",
        "recentchangeslinked-feed": "Imbddeln zund ɣwid",
        "recentchangeslinked-toolbox": "Imbddeln zund ɣwid",
-       "recentchangeslinked-title": "ⵉⵙⵏⴼⵍⵏ ⵇⵇⵏⵏⵉⵏ ⵙ \"$1\"",
+       "recentchangeslinked-title": "âµ\89âµ\99âµ\8fâ´¼âµ\89âµ\8dâµ\8f âµ\87âµ\87âµ\8fâµ\8fâµ\89âµ\8f âµ\99 \"$1\"",
        "recentchangeslinked-summary": "Ɣid umuɣ iymbddeln li ittyskarnin tigira yad ɣ tisniwin li ittuyzdayn d kra n tasna (ulla i igmamn n kra taggayt ittuyzlayn). Tisniwin  ɣ [[Special:Watchlist|Umuɣ n tisniwin li ttsaggat]].",
        "recentchangeslinked-page": "ⵉⵙⵎ ⵏ ⵜⴰⵙⵏⴰ:",
        "recentchangeslinked-to": "Afficher les changements vers les pages liées au lieu de la page donnée\nMel imbddeln z tisniwin li ittuyzdayni bla tasna li trit.",
        "listusers": "ⵜⴰⵍⴳⴰⵎⵜ ⵏ ⵉⵙⵎⵔⴰⵙⵏ",
        "usercreated": "{{GENDER:$3|ⵉⵙⵏⵓⵍⴼⴰ|ⵜⵙⵏⵓⵍⴼⴰ}} ⴳ $1 ⴳ $2",
        "newpages": "ⵜⴰⵙⵏⵉⵡⵉⵏ ⵜⵉⵎⴰⵢⵏⵓⵜⵉⵏ",
-       "move": "âµ\99âµ\8eâ´°ⵜⵜⵉ",
+       "move": "âµ\99âµ\8eâµ\93ⵜⵜⵉ",
        "movethispage": "ⵙⵎⴰⵜⵜⵉ ⵜⴰⵙⵏⴰ ⴰⴷ",
        "unusedcategoriestext": "Taggayin ad llant waxxa gis nt ur tlli kra n tasna wala kra n taggayin yaḍnin",
        "notargettitle": "F walu",
        "restriction-type": "ⵜⵓⵔⴰⴳⵜ:",
        "restriction-level": "Restriction level:",
        "restriction-edit": "ⵙⵏⴼⵍ",
-       "restriction-move": "âµ\99âµ\8eâ´°ⵜⵜⵉ",
+       "restriction-move": "âµ\99âµ\8eâµ\93ⵜⵜⵉ",
        "undeletelink": "mel/rard",
        "undeleteviewlink": "Ẓṛ",
        "undelete-search-submit": "ⵙⵉⴳⴳⵍ",
        "tooltip-ca-unprotect": "ⵙⵏⴼⵍ ⴰⴼⵔⴰⴳ ⵏ ⵜⴰⵙⵏⴰ ⴰⴷ",
        "tooltip-ca-delete": "ⴽⴽⵙ ⵜⴰⵙⵏⴰ ⴰⴷ",
        "tooltip-ca-undelete": "Rard imbddeln imzwura li ittyskarnin ɣ tasna yad",
-       "tooltip-ca-move": "âµ\99âµ\8eâ´°ⵜⵜⵉ ⵜⴰⵙⵏⴰ ⴰⴷ",
+       "tooltip-ca-move": "âµ\99âµ\8eâµ\93ⵜⵜⵉ ⵜⴰⵙⵏⴰ ⴰⴷ",
        "tooltip-ca-watch": "ⵔⵏⵓ ⵜⴰⵙⵏⴰ ⴰⴷ ⵉ ⵜⵍⴳⴰⵎⵜ {{GENDER:|ⵏⵏⴽ|ⵏⵏⵎ}} ⵏ ⵓⴹⴼⴼⵓⵔ",
        "tooltip-ca-unwatch": "ⵙⵉⵜⵜⵉ ⵜⴰⵙⵏⴰ ⴰⴷ ⵣⴳ ⵜⵍⴳⴰⵎⵜ {{GENDER:|ⵏⵏⴽ|ⵏⵏⵎ}} ⵏ ⵓⴹⴼⴼⵓⵔ",
        "tooltip-search": "ⵙⵉⴳⴳⵍ ⴳ {{SITENAME}}",
        "tooltip-n-mainpage-description": "Kid tasna tamuqrant",
        "tooltip-n-portal": "f' usenfar, matzdart atitskrt, maniɣrattaft ɣayli trit",
        "tooltip-n-currentevents": "Tiɣri izrbn i kullu maɣid immusn",
-       "tooltip-n-recentchanges": "ⵜⴰⵍⴳⴰⵎⵜ ⵏ ⵉⵙⵏⴼⵍⵏ ⴳⴳⵯⵔⴰⵏⵉⵏ ⴳ ⵓⵡⵉⴽⵉ",
+       "tooltip-n-recentchanges": "âµ\9câ´°âµ\8dⴳⴰâµ\8eâµ\9c âµ\8f âµ\89âµ\99âµ\8fâ´¼âµ\89âµ\8dâµ\8f â´³â´³âµ¯âµ\94â´°âµ\8fâµ\89âµ\8f â´³ âµ\93ⵡâµ\89â´½âµ\89",
        "tooltip-n-randompage": "Srbu yat tasna ɣik nna ka tga",
        "tooltip-n-help": "Adɣar n w-aws",
        "tooltip-t-whatlinkshere": "Umuɣ n kullu tisnatin n Wiki lid ilkkmn ɣid",
        "tags-active-no": "ⵓⵀⵓ",
        "tags-edit": "ⵙⵏⴼⵍ",
        "tags-delete": "ⴽⴽⵙ",
-       "tags-hitcount": "$1 {{PLURAL:$1|ⵓⵙⵏⴼⵍ|ⵉⵙⵏⴼⵍⵏ}}",
+       "tags-hitcount": "$1 {{PLURAL:$1|âµ\93âµ\99âµ\8fâ´¼âµ\8d|âµ\89âµ\99âµ\8fâ´¼âµ\89âµ\8dâµ\8f}}",
        "tags-create-submit": "ⵙⵏⵓⵍⴼⵓ",
        "comparepages": "ⵙⵎⵣⴰⵣⴰⵍ ⵜⴰⵙⵏⵉⵡⵉⵏ",
        "compare-page1": "ⵜⴰⵙⵏⴰ 1",
diff --git a/languages/i18n/skr-arab.json b/languages/i18n/skr-arab.json
new file mode 100644 (file)
index 0000000..ce26e62
--- /dev/null
@@ -0,0 +1,810 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Saraiki"
+               ]
+       },
+       "tog-underline": "لنک  ہیٹھ لکیر",
+       "tog-hideminor": "چھوٹیاں تبدیلیاں لُکاؤ",
+       "tog-numberheadings": "سرخیاں کوں خود کار نمبر ݙیوو",
+       "tog-showtoolbar": "آلات ترمیم ݙکھاؤ",
+       "tog-editondblclick": "ڈبل کلک نال ورقے وچ تبدیلیاں کرو",
+       "tog-uselivepreview": "براہ راست نمائش ورتو",
+       "underline-always": "ہمیشہ",
+       "underline-never": "کݙاہیں وی کائناں",
+       "underline-default": "سکن یا براؤزر دا طے شدہ",
+       "editfont-style": "خانہ ترمیم دا فونٹ",
+       "editfont-default": "براؤزر دا طے شدہ",
+       "editfont-monospace": "مونوسپیسڈ فونٹ",
+       "editfont-sansserif": "سنس سیرف فونٹ",
+       "editfont-serif": "سیرف فونٹ",
+       "sunday": "اتوار",
+       "monday": "سونوار",
+       "tuesday": "منگل",
+       "wednesday": "بدھ",
+       "thursday": "خمیس",
+       "friday": "جمعہ",
+       "saturday": "چھݨ چھݨ",
+       "sun": "اتوار",
+       "mon": "سونوار",
+       "tue": "منگل",
+       "wed": "بدھ",
+       "thu": "خمیس",
+       "fri": "جمعہ",
+       "sat": "چھݨ چھݨ",
+       "january": "جنوری",
+       "february": "فروری",
+       "march": "مارچ",
+       "april": "اپريل",
+       "may_long": "مئی",
+       "june": "جون",
+       "july": "جولائی",
+       "august": "اگست",
+       "september": "ستمبر",
+       "october": "اکتوبر",
+       "november": "نومبر",
+       "december": "دسمبر",
+       "january-gen": "جنوری",
+       "february-gen": "فروری",
+       "march-gen": "مارچ",
+       "april-gen": "اپريل",
+       "may-gen": "مئی",
+       "june-gen": "جون",
+       "july-gen": "جولائی",
+       "august-gen": "اگست",
+       "september-gen": "ستمبر",
+       "october-gen": "اکتوبر",
+       "november-gen": "نومبر",
+       "december-gen": "دسمبر",
+       "jan": "جنوری",
+       "feb": "فروری",
+       "mar": "مارچ",
+       "apr": "اپریل",
+       "may": "مئی",
+       "jun": "جون",
+       "jul": "جولائی",
+       "aug": "اگست",
+       "sep": "ستمبر",
+       "oct": "اکتوبر",
+       "nov": "نومبر",
+       "dec": "دسمبر",
+       "january-date": "$1 جنوری",
+       "february-date": "$1 فروری",
+       "march-date": "$1 مارچ",
+       "april-date": "$1 اپریل",
+       "may-date": "$1 مئی",
+       "june-date": "$1 جون",
+       "july-date": "$1 جولائی",
+       "august-date": "$1 اگست",
+       "september-date": "$1 ستمبر",
+       "october-date": "$1 اکتوبر",
+       "november-date": "$1 نومبر",
+       "december-date": "$1 دسمبر",
+       "period-am": "سویر",
+       "period-pm": "شام",
+       "pagecategories": "{{PLURAL:$1|زمرہ|زمرہ جات}}",
+       "category_header": "زمرہ \"$1\" وچ ورقے",
+       "subcategories": "ذیلی زمرہ جات",
+       "category-media-header": "زمرہ \"$1\" وچ میڈیا",
+       "hidden-categories": "{{PLURAL:$1|پوشیدہ زمرہ|پوشیدہ زمرہ جات}}",
+       "listingcontinuesabbrev": "جاری۔",
+       "noindex-category": "غیر فہرست شدہ صفحات",
+       "broken-file-category": "ٹٹے ہوۓ جوڑاں آلے صفحے",
+       "about": "تعارف",
+       "article": "مواد آلا ورقہ",
+       "newwindow": "(نویں ونڈو وچ کھولو)",
+       "cancel": "مکاؤ",
+       "moredotdotdot": "ٻئے",
+       "mypage": "ورقہ",
+       "mytalk": "ڳالھ مہاڑ",
+       "anontalk": "ڳالھ مہاڑ",
+       "navigation": "رہنمائی",
+       "and": "&#32;تے",
+       "faq": "عام طور تے پچھے ونڄݨ آلے سوال",
+       "actions": "کم",
+       "namespaces": "ناں دیاں جہاواں",
+       "variants": "قسماں",
+       "navigation-heading": "فہرست رہنمائی",
+       "errorpagetitle": "نقص",
+       "returnto": "واپس $1 چلو",
+       "tagline": " {{SITENAME}} توں",
+       "help": "مدد",
+       "search": "کھوج",
+       "searchbutton": "کھوج",
+       "go": "ڄلو",
+       "searcharticle": "ڄلو",
+       "history": "پچھلے کم",
+       "history_short": "تاریخچہ",
+       "history_small": "تاریخچہ",
+       "printableversion": "چھپݨ جوگا ورقہ",
+       "permalink": "پکا جوڑ",
+       "print": "چھاپو",
+       "view": "ݙکھالے",
+       "view-foreign": "$1 تے ݙیکھو",
+       "edit": "لکھو",
+       "create": "بݨاؤ",
+       "create-local": "آپنی لکھت رلاؤ",
+       "delete": "مٹاؤ",
+       "protect": "حفاظت کرو",
+       "protect_change": "تبدیل کرو",
+       "unprotect": "تحفظ وچ تبدیلی",
+       "newpage": "نواں ورقہ",
+       "talkpagelinktext": "ڳالھ مہاڑ",
+       "specialpage": "خاص ورقہ",
+       "personaltools": "ذاتی آوزار",
+       "talk": "ڳالھ مہاڑ",
+       "views": "ݙکھالے",
+       "toolbox": "آوزار",
+       "tool-link-emailuser": "ایں {{GENDER:$1|صارف}} کوں ای میل کرو",
+       "imagepage": "فائل آلا ورقہ ݙیکھو",
+       "mediawikipage": "سنیہے آلا ورقہ ݙیکھو",
+       "templatepage": "سانچے آلا ورقہ ݙیکھو",
+       "viewhelppage": "مدد آلا ورقہ ݙیکھو",
+       "categorypage": "کیٹاگری آلا ورقہ ݙیکھو",
+       "viewtalkpage": "مباحثہ ݙیکھو",
+       "otherlanguages": "ٻنھاں زباناں وچ",
+       "redirectedfrom": "($1 کنوں ولدا رجوع )",
+       "redirectpagesub": "صفحہ ریڈائریکٹ کرو",
+       "redirectto": "اڳے کرو:",
+       "lastmodifiedat": "ایہ ورقہ چھیکڑی واری  $1 کوں $2 تے تبدیل تھیا ہائی۔",
+       "protectedpage": "آم شام ورقہ",
+       "jumpto": "ٹپ مارو",
+       "jumptonavigation": "رہنمائی",
+       "jumptosearch": "ڳولو",
+       "aboutsite": "{{SITENAME}} دا تعارف",
+       "aboutpage": "Project:تعارف",
+       "copyrightpage": "{{ns:project}}:حقوق تصانیف",
+       "currentevents": "حالیہ واقعات",
+       "currentevents-url": "Project:حالیہ واقعات",
+       "disclaimers": "لاتعلقی اظہار",
+       "disclaimerpage": "Project:عام لاتعلقی اظہار",
+       "edithelp": "لکھݨ وچ مدد",
+       "helppage-top-gethelp": "مدد",
+       "mainpage": "وݙا ورقہ",
+       "mainpage-description": "پہلا ورقہ",
+       "portal": "بیٹھک",
+       "portal-url": "Project:دیوان عام",
+       "privacy": "پرائیویسی پالیسی",
+       "privacypage": "Project:پرائیویسی پالیسی",
+       "ok": "ٹھیک ہے",
+       "retrievedfrom": "\"$1\" توں گھدا",
+       "youhavenewmessages": "{{PLURAL:$3| تہاݙے کیتے}} $1 ($2).",
+       "newmessageslinkplural": "{{PLURAL:$1|نواں سنیہا|999=نویں سنیہے}}",
+       "newmessagesdifflinkplural": "چھیکڑی {{PLURAL:$1|تبدیلی|تبدیلیاں}}",
+       "editsection": "لکھو",
+       "editold": "لکھو",
+       "viewsourceold": "ماخذ ݙیکھو",
+       "editlink": "لکھو",
+       "viewsourcelink": "ماخذ ݙیکھو",
+       "editsectionhint": "حصہ لکھو: $1",
+       "toc": "حصے",
+       "showtoc": "ݙیکھاؤ",
+       "hidetoc": "لُکاؤ",
+       "collapsible-collapse": "لکاؤ",
+       "collapsible-expand": "ودھاؤ",
+       "confirmable-yes": "ڄیا",
+       "confirmable-no": "کو",
+       "viewdeleted": "ݙیکھو $1؟",
+       "feedlinks": "فیڈ",
+       "site-atom-feed": "$1 اٹوم فیڈ",
+       "page-atom-feed": "$1 اٹوم فیڈ",
+       "red-link-title": "$1 (ایہ ورقہ اڄݨ تائیں کائنی بݨیا)",
+       "nstab-main": "ورقہ",
+       "nstab-user": "صفحۂ صارف",
+       "nstab-media": "میڈیا آلا ورقہ",
+       "nstab-special": "خاص ورقہ",
+       "nstab-project": "پروجیکٹ ورقہ",
+       "nstab-image": "فائل",
+       "nstab-mediawiki": "سنیہہ",
+       "nstab-template": "سانچہ",
+       "nstab-help": "مدد ورقہ",
+       "nstab-category": "زمرہ",
+       "mainpage-nstab": "وݙا ورقہ",
+       "nosuchaction": "کوئی اینجھا کم کائنی",
+       "nosuchspecialpage": "اینجھا کوئی خاص ورقہ کائنی",
+       "error": "نقص",
+       "databaseerror": "ڈیٹابیس دی غلطی",
+       "databaseerror-error": "نقص: $1",
+       "badtitle": "بھیڑا عنوان",
+       "viewsource": "ماخذ ݙیکھو",
+       "viewsource-title": "$1 دا مسودہ ݙیکھو",
+       "yourname": "صارف دا ناں",
+       "userlogin-yourname": "صارف ناں",
+       "userlogin-yourname-ph": "آپݨا ورتݨ ناں صارف درج کرو",
+       "createacct-another-username-ph": "آپݨا ورتݨ ناں صارف درج کرو",
+       "yourpassword": "پاس ورڈ",
+       "userlogin-yourpassword": "پاس ورڈ",
+       "userlogin-yourpassword-ph": "پاس ورڈ درج کرو",
+       "createacct-yourpassword-ph": "پاس ورڈ درج کرو",
+       "yourpasswordagain": "پاس ورڈ ولدا لکھو",
+       "createacct-yourpasswordagain": "پاس ورڈ دی تصدیق کرو",
+       "createacct-yourpasswordagain-ph": "پاس ورڈ ولدا درج کرو",
+       "userlogin-remembermypassword": "میکوں لاگ ان رکھو",
+       "userlogin-signwithsecure": "محفوظ رابطہ (کنکشن) استعمال کرو",
+       "cannotlogin-title": "لاگ ان نی تھی سڳدے",
+       "cannotlogin-text": "لاگ ان تھیوݨ ناممکن ہے",
+       "cannotloginnow-title": "ہݨ لاگ ان نہوے تھی سڳدے",
+       "login": "لاگ ان تھیوو",
+       "logout": "لاگ آؤٹ",
+       "userlogout": "لاگ آؤٹ",
+       "notloggedin": "لاگ ان نہوے تھئے",
+       "userlogin-noaccount": "تہاݙا کھاتہ کائنی؟",
+       "userlogin-joinproject": "جُڑ ونڄو {{SITENAME}} نال",
+       "createaccount": "کھاتہ کھولو",
+       "userlogin-resetpassword-link": "پاسورڈ بھل ڳئے ہو؟",
+       "userlogin-helplink2": "لاگ ان تھیوݨ کیتے مدد دی لوڑ ہے؟",
+       "createacct-emailrequired": "ای میل پتہ",
+       "createacct-emailoptional": "ای-میل پتہ، آپشنل",
+       "createacct-email-ph": "اپنا ای-میل پتہ لکھو",
+       "createacct-another-email-ph": "اپنا ای-میل پتہ لکھو",
+       "createacct-reason": "سبب",
+       "createacct-submit": "اپݨاں کھاتا کھولو",
+       "createacct-another-submit": "کھاتہ کھولو",
+       "createacct-benefit-heading": "{{SITENAME}} تہاݙے وانگوں علم دوست افراد دا مرہون منت ہے۔",
+       "createacct-benefit-body1": "$1 {{PLURAL:$1|تبدیلی|تبدیلیاں}}",
+       "createacct-benefit-body2": "\n$1 {{PLURAL:$1|ورقہ|ورقے}}",
+       "createacct-benefit-body3": "ہݨ دے {{PLURAL:$1|کم|کماں}}",
+       "loginerror": "لاگ ان وچ غلطی",
+       "createacct-error": "کھاتہ بݨاوݨ وچ غلطی",
+       "loginsuccesstitle": "لاگ ان تھی ڳیا",
+       "mailmypassword": "نواں پاس ورڈ بݨاؤ",
+       "accountcreated": "کھاتہ کھل ڳیا",
+       "loginlanguagelabel": "زبان: $1",
+       "pt-login": "لاگ ان تھیوو",
+       "pt-login-button": "لاگ ان تھیوو",
+       "pt-createaccount": "کھاتہ کھولو",
+       "pt-userlogout": "لاگ آؤٹ",
+       "changepassword": "پاس ورڈ تبدیل کرو",
+       "oldpassword": "پراݨا پاس ورڈ",
+       "newpassword": "نواں پاس ورڈ",
+       "retypenew": "نواں پاس ورڈ ولدا لکھو",
+       "botpasswords-label-create": "بݨاؤ",
+       "botpasswords-label-update": "اپ ݙیٹ",
+       "botpasswords-label-cancel": "منسوخ",
+       "botpasswords-label-delete": "مٹاؤ",
+       "botpasswords-label-resetpassword": "پاس ورڈ تبدیل کرو",
+       "resetpass-submit-loggedin": "پاس ورڈ تبدیل کرو",
+       "resetpass-submit-cancel": "منسوخ",
+       "resetpass-temp-password": "عارضی لنگھݨ لفظ، پاس ورڈ",
+       "passwordreset": "نواں پاس ورڈ بݨاؤ",
+       "passwordreset-username": "صارف دا ناں",
+       "passwordreset-domain": "ڈومین",
+       "passwordreset-email": "ای میل پتہ",
+       "passwordreset-emailtitle": "{{SITENAME}} کھاتہ دی تفصیلات",
+       "changeemail-none": "(کوئی وی کائنی)",
+       "changeemail-password": "تہاݙا {{SITENAME}} پاس ورڈ:",
+       "changeemail-submit": "ای-میل بدلو",
+       "resettokens-tokens": "ٹوکن",
+       "resettokens-token-label": "$1 (موجودہ قدر: $2)",
+       "bold_sample": "موٹی لکھائی",
+       "bold_tip": "موٹی لکھائی",
+       "italic_sample": "ترچھا متن",
+       "italic_tip": "ترچھی لکھائی",
+       "link_sample": "جوڑ",
+       "link_tip": "اندرونی جوڑ",
+       "extlink_sample": "http://www.example.com جوڑ دا ناں",
+       "extlink_tip": "باہرلے جوڑ (remember http:// prefix)",
+       "headline_sample": "شہ سرخی",
+       "headline_tip": "ݙوجھے درجے دی سرخی",
+       "nowiki_sample": "فارمیٹ نہ تھئی ہوئی لکھائی اتھ درج کرو",
+       "nowiki_tip": "ویکی فارمیٹ کوں نظرانداز کرو",
+       "image_tip": "پیوستہ فائل",
+       "media_tip": "فائل دا جوڑ",
+       "sig_tip": "تہاݙے دستخط ویلے دے نال",
+       "hr_tip": "اُفقی لکیر (زیادہ استعمال نہ کریں)",
+       "summary": "خلاصہ",
+       "subject": "عنوان:",
+       "minoredit": "ایہ ہک چھوٹی تبدیلی ہے",
+       "watchthis": "ایں ورقے تے اکھ رکھو",
+       "savearticle": "محفوظ",
+       "savechanges": "تبدیلیاں محفوظ کرو",
+       "publishpage": "ورقہ شائع کرو",
+       "publishchanges": "تبدیلیاں شائع کرو",
+       "preview": "نمائش",
+       "showpreview": "نمائش",
+       "showdiff": "تبدیلیاں ݙکھاؤ",
+       "loginreqlink": "لاگ ان",
+       "newarticle": "(نواں)",
+       "userpage-userdoesnotexist-view": "صارف کھاتہ \"$1\" رجسٹرڈ کائنی۔",
+       "continue-editing": "خانہ ترمیم وچ ونڄو",
+       "editing": "تساں \"$1\" لکھدے پئے ہو",
+       "creating": "زیر تخلیق $1",
+       "editingsection": "«$1» دے قطعہ دی ترمیم",
+       "yourtext": "تہاݙی لکھائی",
+       "yourdiff": "فرق",
+       "templatesused": "ایں ورقے تے  ورتے ڳئے {{PLURAL:$1|سانچے|سانچہ}}:",
+       "templatesusedpreview": "ایں کچے کم تے  ورتے ڳئے {{PLURAL:$1|سانچے|سانچہ}}:",
+       "template-protected": "(بچایا گیا)",
+       "template-semiprotected": "(نیم محفوظ)",
+       "hiddencategories": "ایہ ورقہ {{PLURAL:$1|1 لُکے زمریاں|$1 لکا زمرہ }} وچ شامل ہے:",
+       "permissionserrors": "خطائے اجازت",
+       "moveddeleted-notice": "ایہ ورقہ مٹایا ڳیا ہے۔ مٹاوݨ دا لاگ ہیٹھاں ݙتا ہویا ہے",
+       "content-model-wikitext": "ویکی متن",
+       "content-model-text": "سادہ متن",
+       "content-model-javascript": "جاوا  سکرپٹ",
+       "content-json-empty-object": "خالی آبجیکٹ",
+       "content-json-empty-array": "خالی ایرے",
+       "undo-failure": "متنازع تبدیلیاں پاروں ایہ تبدیلی واپس نی تھی سڳدی۔",
+       "viewpagelogs": "صفحے دے لاگ ݙیکھو",
+       "currentrev-asof": "حالیہ نسخہ بمطابق $1",
+       "revisionasof": "دی تبدیلیاں $1",
+       "previousrevision": "→ پراݨا نسخہ",
+       "nextrevision": "نویں تبدیلی →",
+       "currentrevisionlink": "موجودہ حالت",
+       "cur": " رائج",
+       "next": "اڳوں تے",
+       "last": "پچھلا",
+       "page_first": "پہلا",
+       "page_last": "چھیکڑی",
+       "history-fieldset-title": "دہرائی کیتے لبھت",
+       "histfirst": "قدیم ترین",
+       "histlast": "تازہ ترین",
+       "historyempty": "(خالی)",
+       "history-feed-title": "ریویژن رکارڈ",
+       "history-feed-description": "وکی تے ایں ورقے دی ریویژن ہسٹری",
+       "history-feed-item-nocomment": "$2 کوں $1",
+       "rev-delundel": "ݙکھاؤ/لکاؤ",
+       "rev-showdeleted": "ݙیکھاؤ",
+       "revdelete-show-file-submit": "ڄیا",
+       "revdelete-hide-comment": "تبدیلی دا خلاصہ",
+       "mergehistory-reason": "سبب",
+       "mergelog": "لاگ رلاؤ",
+       "revertmerge": "وکھریاں کرو",
+       "history-title": "\"$1\" دا ریکارڈ",
+       "difference-title": "\"$1\" دے نسخیاں دے درمیان فرق",
+       "lineno": "سطر $1:",
+       "compareselectedversions": "منتخب متـن دا موازنہ",
+       "editundo": "واپس",
+       "diff-empty": "(کوئی فرق کائنی)",
+       "searchresults": "کھوج دا نتارا",
+       "searchresults-title": "\"$1\" دے کھوج نتارے",
+       "prevn": "پچھلے {{PLURAL:$1|$1}}",
+       "nextn": "اگلے {{PLURAL:$1|$1}}",
+       "prev-page": "پچھلا ورقہ",
+       "next-page": "اڳلا ورقہ",
+       "prevn-title": "پہلے $1 {{PLURAL:$1|نتیجے}}",
+       "nextn-title": "اگلے $1 {{PLURAL:$1|نتیجے}}",
+       "shown-title": "وکھاؤ $1 {{PLURAL:$1|نتیجے}}",
+       "viewprevnext": "($1 {{int:pipe-separator}} $2) ݙیکھو ($3)",
+       "searchprofile-articles": "لسٹ ورقے",
+       "searchprofile-images": "ملٹی میڈیا",
+       "searchprofile-everything": "سب کجھ",
+       "searchprofile-advanced": "اگلا",
+       "searchprofile-articles-tooltip": "$1 وچ ڳولو",
+       "searchprofile-images-tooltip": "فائلاں ڳولو",
+       "searchprofile-everything-tooltip": " سارا مواد ڳولو",
+       "searchprofile-advanced-tooltip": "کسٹم نانواں وچ ڳولو",
+       "search-result-size": "$1 ({{PLURAL:$2|1 لفظ|$2 الفاظ}})",
+       "search-redirect": "($1 کنوں ولدا رجوع )",
+       "search-section": "(قطعہ $1)",
+       "search-file-match": "فائل مواد نال ملدا ہے",
+       "search-suggest": "بھلا تہاݙا مطلب ہائی: $1",
+       "searchall": "یکے",
+       "search-nonefound": "سوال دے نال رلدے ملدے نتارے کائنی۔",
+       "powersearch-togglelabel": "ݙیکھو",
+       "powersearch-toggleall": "یکے",
+       "powersearch-togglenone": "کوئی وی کائنی",
+       "preferences": "ترجیحات",
+       "mypreferences": "ترجیحات",
+       "prefs-edits": "تبدیلیاں دی گنتی:",
+       "prefsnologintext2": "آپݨیاں ترجیہاں تبدیل کرݨ کیتے لاگ ان تھیوو",
+       "prefs-skin": "جِلد",
+       "skin-preview": "نمائش",
+       "datedefault": "کوئی ترجیح کائنی",
+       "saveprefs": "بچاؤ",
+       "searchresultshead": "ڳولو",
+       "stub-threshold-sample-link": "نمونہ",
+       "stub-threshold-disabled": "غیر فعال",
+       "servertime": "سرور دا وقت:",
+       "guesstimezone": "براؤزر توں بھرو۔",
+       "timezoneregion-africa": "افریقہ",
+       "timezoneregion-america": "امریکہ",
+       "timezoneregion-antarctica": "انٹارکٹیکا",
+       "timezoneregion-arctic": "قطب شمالی",
+       "timezoneregion-asia": "ایشیاء",
+       "timezoneregion-atlantic": "بحر اوقیانوس",
+       "timezoneregion-australia": "آسٹریلیا",
+       "timezoneregion-europe": "یورپ",
+       "timezoneregion-indian": "بحر ہند",
+       "timezoneregion-pacific": "بحر الکاہل",
+       "prefs-searchoptions": "ڳولو",
+       "prefs-namespaces": "ناں دیاں جہاواں",
+       "default": "پہلے کنوں طے تھیا ہویا",
+       "prefs-files": "فائلاں",
+       "prefs-custom-css": "کسٹم سی ایس ایس",
+       "prefs-custom-js": "کسٹم جاوا سکرپٹ",
+       "prefs-emailconfirm-label": "ای میل دی تصدیق",
+       "youremail": "ای میل",
+       "prefs-registration": "رجسٹریشن ویلہ:",
+       "yourrealname": "اصلی ناں:",
+       "yourlanguage": "زبان",
+       "email": "ای میل",
+       "prefs-info": "بنیادی معلومات",
+       "prefs-i18n": "بین الاقوامیت",
+       "prefs-signature": "دستخط",
+       "prefs-dateformat": "تاریخ دی ترتیب",
+       "prefs-timeoffset": "وقت دی ترتیب",
+       "prefs-advancedediting": "عام آپشن",
+       "prefs-editor": "خانہ ترمیم",
+       "prefs-preview": "نمائش",
+       "prefs-tokenwatchlist": "ٹوکن",
+       "group": "گروپ:",
+       "group-user": "ورتݨ آلے",
+       "group-bot": "بوٹ",
+       "group-sysop": "منتظمین",
+       "group-all": "(سارے)",
+       "grouppage-bot": "{{ns:project}}:بوٹ",
+       "grouppage-sysop": "{{ns:project}}:ایڈمنسٹریٹر",
+       "right-writeapi": "اے پی آئی تحریر دا استعمال",
+       "newuserlogpage": "یوزر بنݨاوݨ آلی لاگ",
+       "rightslog": "ورتݨ والے دے حقاں دی لاگ",
+       "action-edit": "ایں ورقے تے لکھو",
+       "action-createaccount": "ایہ ورتݨ آلا کھاتہ کھولو",
+       "enhancedrc-history": "پچھلا کم",
+       "recentchanges": "نویاں تبدیلیاں",
+       "recentchanges-legend": "اِختیاراتِ حالیہ تبدیلیاں",
+       "recentchanges-feed-description": "ایں فیڈ وچ وکی تے تھیوݨ آلیاں نویاں نکور تبدیلیاں ݙیکھو۔",
+       "recentchanges-label-newpage": "ایں تبدیلی نواں ورقہ بݨایا ہے",
+       "recentchanges-label-minor": "ایہ ہک چھوٹی تبدیلی ہے",
+       "recentchanges-label-bot": "ایہ تبدیلی  بوٹ نے کیتی ہے۔",
+       "recentchanges-label-unpatrolled": "ایہ تبدیلی اڄݨ تائیں واپس کائنی ولی۔",
+       "recentchanges-label-plusminus": "ورقے دا تبدیل شدہ حجم بلحاظ تعداد بائٹ",
+       "recentchanges-legend-heading": "<strong>اختصارات:</strong>",
+       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (ایہ وی ݙیکھو [[Special:NewPages|نویں ورقیاں دی لسٹ]])",
+       "recentchanges-submit": "ݙیکھاؤ",
+       "rcfilters-savedqueries-rename": "نواں ناں لکھو",
+       "rcfilters-savedqueries-setdefault": "ݙیفالٹ بݨاؤ",
+       "rcfilters-savedqueries-unsetdefault": "ݙیفالٹ توں ہٹاؤ",
+       "rcfilters-savedqueries-remove": "مٹاؤ",
+       "rcfilters-savedqueries-new-name-label": "ناں",
+       "rcfilters-savedqueries-cancel-label": "منسوخ",
+       "rcfilters-filter-bots-label": "ٻوٹ",
+       "rclistfrom": "$3 $2 توں ہونے آلیاں نویاں تبدیلیاں ݙکھاؤ",
+       "rcshowhideminor": "$1 معمولی تبدیلیاں",
+       "rcshowhideminor-show": "ݙیکھاؤ",
+       "rcshowhideminor-hide": "لُکاؤ",
+       "rcshowhidebots": "$1 بوٹ",
+       "rcshowhidebots-show": "ݙیکھاؤ",
+       "rcshowhidebots-hide": "لُکاؤ",
+       "rcshowhideliu": "مندرج صارفین $1",
+       "rcshowhideliu-show": "ݙیکھاؤ",
+       "rcshowhideliu-hide": "لُکاؤ",
+       "rcshowhideanons": "گمنام صارف $1",
+       "rcshowhideanons-show": "ݙیکھاؤ",
+       "rcshowhideanons-hide": "لُکاؤ",
+       "rcshowhidepatr": "$1 مراجعت شدہ ترامیم",
+       "rcshowhidepatr-show": "ݙیکھاؤ",
+       "rcshowhidepatr-hide": "لُکاؤ",
+       "rcshowhidemine": "ذاتی ترامیم میݙے کم $1",
+       "rcshowhidemine-show": "ݙیکھاؤ",
+       "rcshowhidemine-hide": "لُکاؤ",
+       "rcshowhidecategorization-show": "ݙیکھاؤ",
+       "rcshowhidecategorization-hide": "لُکاؤ",
+       "rclinks": "آخری $2 ݙینہ دیاں $1 تبدیلیاں ݙکھاؤ",
+       "diff": "فرق",
+       "hist": "پچھلا کم",
+       "hide": "لُکاؤ",
+       "show": "ݙیکھاؤ",
+       "minoreditletter": "چھوٹا کم",
+       "newpageletter": "نواں",
+       "boteditletter": " خودکار",
+       "rc-change-size-new": "تبدیلی دے بعد $1 {{PLURAL:$1|بائٹ}}",
+       "rc-old-title": "اصلاً «$1» دے عنوان نال تخلیق شدہ",
+       "recentchangeslinked": "رلدیاں ملدیاں تبدیلیاں",
+       "recentchangeslinked-feed": "رلدیاں ملدیاں تبدیلیاں",
+       "recentchangeslinked-toolbox": "رلدیاں ملدیاں تبدیلیاں",
+       "recentchangeslinked-title": "\"$1\" دے متعلقہ تبدیلیاں",
+       "recentchangeslinked-page": "ورقے دا ناں",
+       "recentchangeslinked-to": "کھلے ہوئے ورقے دی بجائے ایندے نال جُڑے ہوئے ورقے دیاں تبدیلیاں ݙکھاؤ",
+       "upload": "فائل چڑھاؤ",
+       "uploadlogpage": "اپلوڈ لاگ",
+       "filename": "فائل دا ناں",
+       "filedesc": "خلاصہ",
+       "fileuploadsummary": "خلاصہ",
+       "filereuploadsummary": "حالیہ تبدیلیاں",
+       "filesource": "ماخذ",
+       "savefile": "فائل بچاؤ",
+       "upload-dialog-title": "فائل چڑھاؤ",
+       "upload-dialog-button-cancel": "منسوخ",
+       "upload-dialog-button-back": "پچھوں",
+       "upload-dialog-button-done": "مکمل",
+       "upload-dialog-button-save": "بچاؤ",
+       "upload-dialog-button-upload": "اپلوڈ",
+       "upload-form-label-infoform-title": "تفصیلات",
+       "upload-form-label-infoform-name": "ناں",
+       "upload-form-label-infoform-description": "تفصیل",
+       "upload-form-label-usage-title": "استعمال",
+       "upload-form-label-usage-filename": "فائل دا ناں",
+       "upload-form-label-own-work": "یہ میݙا ذاتی کم ہے",
+       "upload-form-label-infoform-categories": "قسماں، زمرے",
+       "upload-form-label-infoform-date": "تاریخ",
+       "license": "اجازت نامہ:",
+       "license-header": "اجازہ کاری",
+       "listfiles-delete": "مٹاؤ",
+       "imgfile": "فائل",
+       "listfiles": "فائل لسٹ",
+       "listfiles_thumb": "تھمب نیل",
+       "listfiles_date": "تاریخ",
+       "listfiles_name": "ناں",
+       "listfiles_user": "ورتݨ والا",
+       "listfiles_size": "حجم",
+       "listfiles_description": "تفصیل",
+       "listfiles_count": "ورژن",
+       "listfiles-latestversion": "موجودہ ورژن",
+       "listfiles-latestversion-yes": "ڄیا",
+       "listfiles-latestversion-no": "کو",
+       "file-anchor-link": "فائل",
+       "filehist": "فائل دا تاریخچہ",
+       "filehist-help": "کہیں خاص ویلے تے تاریخ کوں فائل کینویں  نظردی ہائی، ݙیکݨ کیتے اوں ویلے تے کلک کرو۔",
+       "filehist-deleteall": "سارے مٹاؤ",
+       "filehist-deleteone": "مٹاؤ",
+       "filehist-revert": "واپس",
+       "filehist-current": "موجودہ",
+       "filehist-datetime": "تریخ/ویلہ",
+       "filehist-thumb": "تھمب نیل",
+       "filehist-thumbtext": "مورخہ $1 دا تھمب نیل",
+       "filehist-nothumb": "کوئی تھمبنیل کائنی۔",
+       "filehist-user": "ورتݨ والا",
+       "filehist-dimensions": "پاسے",
+       "filehist-filesize": "تصویر دا سائز",
+       "filehist-comment": "رائے",
+       "imagelinks": "فائل ورتݨ",
+       "linkstoimage": "اِیں فائل نال ہیٹھاں درج  {{PLURAL:$1|ورقہ مربوط ہے|$1 صفحات مربوط ہن}}:",
+       "nolinkstoimage": "ایں فائل نال کوئی ورقہ کائنی ڄُڑیا ہویا۔",
+       "linkstoimage-redirect": "$1 (فائل وت رجوع) $2",
+       "sharedupload-desc-here": "ایہ فائل $1 توں ہے تے ݙوجھیاں منصوبیاں تے وی ورتی ویسی۔\nایندی وضاحت [$2 فائل دی وضاحت دا ورقہ]  تے تھلے ݙتی ڳئی۔",
+       "filepage-nofile": "ایں ناں دی کوئی فائل کائنی۔",
+       "upload-disallowed-here": "تساں ایں فائل تے لکھ نی سڳدے۔",
+       "filedelete-comment": "سبب:",
+       "filedelete-submit": "مٹاؤ",
+       "randompage": "رلے ملے ورقے",
+       "randomincategory-submit": "ڄلو",
+       "statistics": "شماريات",
+       "pageswithprop-submit": "ڄلو",
+       "double-redirect-fixer": "ریڈائرکٹ فکسر",
+       "brokenredirects-edit": "لکھو",
+       "brokenredirects-delete": "مٹاؤ",
+       "withoutinterwiki-submit": "ݙیکھاؤ",
+       "nbytes": "$1 {{PLURAL:$1|بائٹ}}",
+       "nmembers": "{{PLURAL:$1|رکن|اراکین}}",
+       "prefixindex": "سارے ورقے بمع سابقہ",
+       "protectedpages-page": "ورقہ",
+       "protectedpages-reason": "سبب",
+       "listusers": "ورتݨ والیاں دے ناں",
+       "newpages": "نویں ورقے",
+       "move": "ٹرو",
+       "pager-newer-n": "{{PLURAL:$1|newer 1|زیادہ نواں $1}}",
+       "pager-older-n": "{{PLURAL:$1|قدیم}} $1",
+       "apisandbox-reset": "صاف",
+       "booksources": "کتابی وسائل",
+       "booksources-search-legend": "ایں مضمون تے کتاباں لبھو",
+       "booksources-search": "ڳولو",
+       "specialloguserlabel": "کرݨ آلا :",
+       "speciallogtitlelabel": "ہدف (عنوان یا {{ns:user}}: صارف کیتے صارف دا ناں):",
+       "log": "لاگز",
+       "all-logs-page": "سارےعوامی لاگ",
+       "logempty": "لاگ وچ رلدیاں ملدیاں چیزاں کائنی۔",
+       "allpages": "سارے مقالے",
+       "allarticles": "سارے مقالے",
+       "allpagessubmit": "ڄلو",
+       "allpages-hide-redirects": "رجوع مکررات لکاؤ",
+       "categories": "زمرہ",
+       "listgrouprights-members": "(رکناں دی لسٹ)",
+       "emailuser": "ایں ورتݨ والے کوں ای میل کرو",
+       "usermessage-editor": "نظامی پیغام رساں",
+       "watchlist": "زیرنظر فہرست",
+       "mywatchlist": "زیرنظر فہرست",
+       "watchlistfor2": "$1 تے $2 کیتے",
+       "watch": "اکھ تلے رکھو",
+       "unwatch": "اکھ ہیٹھوں ہٹاؤ",
+       "wlshowlast": "ݙیکھاؤ چھیکڑی $1 گھنٹے $2 ݙینہ",
+       "watchlist-options": "نظر تھلے رکھݨ دیاں راہواں",
+       "enotif_reset": "سارے ورقے ڈیکھ گھدن",
+       "dellogpage": "مٹاوݨ آلی لاگ",
+       "rollbacklink": "واپس",
+       "protectlogpage": "بچت لاگ",
+       "protectedarticle": "\"[[$1]]\" بچایا گیا اے",
+       "modifiedarticleprotection": "«[[$1]]» دا درجہ حفاظت تبدیل کیتا",
+       "protect-default": "تمام صارفین کوں اجازت ہے",
+       "restriction-edit": "لکھو",
+       "restriction-move": "ٹرو",
+       "namespace": "ناں دی جگہ:",
+       "invert": "انتخاب معکوس",
+       "namespace_association": "رلدے ناں دی تھاں",
+       "blanknamespace": "(مکھ)",
+       "contributions": " $1 ورتن آلے دا حصہ",
+       "contributions-title": "صارف $1 دی شراکتاں",
+       "mycontris": "شراکتاں",
+       "anoncontribs": "شراکتاں",
+       "contribsub2": "{{GENDER:$3|$1}} ($2)",
+       "nocontribs": "ایں معیار دے مطابق کوئی تبدیلی نی لبھی۔",
+       "uctop": "(موجودہ)",
+       "month": "مہینے توں (تے پہلاں):",
+       "year": "سال توں (تے پہلاں):",
+       "sp-contributions-newbies": "صرف نویں ورتݨ آلیاں دے کم ݙکھاؤ",
+       "sp-contributions-blocklog": "لاگ روکو",
+       "sp-contributions-uploads": "اپلوڈ کردہ",
+       "sp-contributions-logs": "لاگز",
+       "sp-contributions-talk": "ڳالھ مہاڑ",
+       "sp-contributions-search": "حصے پاؤݨ آلیاں دی تلاش",
+       "sp-contributions-username": "آئی پی پتہ یا ورتݨ آلا ناں:",
+       "sp-contributions-toponly": "صرف اوہ تبدیلیاں ݙکھاؤ جیہڑیاں ہُݨے ہُݨے تھیاں ہن۔",
+       "sp-contributions-newonly": "صرف نویں ورقیاں بݨݨ آلیاں لکھتاں ݙیکھاؤ",
+       "sp-contributions-submit": "ڳولو",
+       "whatlinkshere": "مربوط ورقے",
+       "whatlinkshere-title": "«$1» دے نال جُڑے ہوے ورقے",
+       "whatlinkshere-page": "ورقہ",
+       "linkshere": "<strong>[[:$1]]</strong> نال درج ذیل ورقے مربوط ہن:",
+       "nolinkshere": "<strong>[[:$1]]</strong> نال کوئی ورقہ مربوط کائنی۔",
+       "isredirect": "صفحہ ریڈائریکٹ کرو",
+       "istemplate": "شامل شدہ",
+       "isimage": "فائل دا ربط",
+       "whatlinkshere-prev": "{{PLURAL:$1|پچھلا|پچھلے $1}}",
+       "whatlinkshere-next": "{{PLURAL:$1|اگلا|اگلے $1}}",
+       "whatlinkshere-links": "→ روابط",
+       "whatlinkshere-hideredirs": "رجوع مکررات $1",
+       "whatlinkshere-hidetrans": "استعمالات $1",
+       "whatlinkshere-hidelinks": "روابط $1",
+       "whatlinkshere-hideimages": "تصویر دے روابط $1",
+       "whatlinkshere-filters": "نتارے",
+       "infiniteblock": "بے انت",
+       "blocklink": "پابندی لاؤ",
+       "contribslink": "حصے داری",
+       "blocklogpage": "لاگ روکو",
+       "blocklogentry": "«[[$1]]» تے $2 کیتے پابندی عائد کی ڳئی ہے $3",
+       "reblock-logentry": "[[$1]] دی ترتیبات پابندی کوں تبدیل کیتاڳئے، ہݨ میعاد $2 $3 تے مُکسی",
+       "block-log-flags-nocreate": "کھاتا کھولݨ تے پابندی ہے",
+       "proxyblocker": "پراکسی روکݨ آلا",
+       "movelogpage": "ناں تبدیل کرݨ دا لاگ",
+       "export": "ورقے ٻاہر بھیجو",
+       "thumbnail-more": "وݙا کرو",
+       "importlogpage": "لاگ گھن آؤ",
+       "tooltip-pt-userpage": "تہاݙا صارف ورقہ",
+       "tooltip-pt-mytalk": "{{GENDER:|Your}} گالھ مہاڑ",
+       "tooltip-pt-preferences": "تہاݙیاں ترجیحاں",
+       "tooltip-pt-watchlist": " انہاں ورقیاں دی لسٹ جنہاں وچ تساں تبدیلیاں کرݨ کیتے ݙیہدے پئے ہو۔",
+       "tooltip-pt-mycontris": "میݙے کم",
+       "tooltip-pt-login": "لاگ ان تھیوو تاں چنگا ہے، ضروری کائنی۔",
+       "tooltip-pt-logout": "لاگ آؤٹ",
+       "tooltip-pt-createaccount": "ایہ تہاݙے کیتے چنگا ہے جو کھاتہ کھولو تے لاگ ان تھیوو، پر ایہ لازمی کائنی۔",
+       "tooltip-ca-talk": "مضمون بارے بحث",
+       "tooltip-ca-edit": "ایں ورقے تے لکھو",
+       "tooltip-ca-addsection": "نواں حصہ شروع کرو",
+       "tooltip-ca-viewsource": "ایہ ورقہ محفوظ تھیا ہویا ہے۔ \nتساں صرف ایندا ماخذ ݙیکھ سڳدے ہو۔",
+       "tooltip-ca-history": "ایں ورقے دا پراݨا روپ۔",
+       "tooltip-ca-protect": "ایہ ورقہ محفوظ کرو",
+       "tooltip-ca-delete": "ایں ورقے کوں مٹاؤ",
+       "tooltip-ca-move": "ایں ورقے کوں گھن ڄلو",
+       "tooltip-ca-watch": "ایں ورقے کوں آپݨی دید آلے ورقیاں وچ رکھو",
+       "tooltip-ca-unwatch": "ایں ورقے کوں آپݨی دید آلے ورقیاں وچ رکھو",
+       "tooltip-search": "ڳولو {{SITENAME}}",
+       "tooltip-search-go": "جے ایں عنوان دا ورقہ ہے تاں اتھ ونڄو",
+       "tooltip-search-fulltext": "ایں عبارت کوں ورقیاں وچ ڳولو",
+       "tooltip-p-logo": "پہلا ورقہ ݙیکھو",
+       "tooltip-n-mainpage": "پہلا ورقہ ݙیکھو",
+       "tooltip-n-mainpage-description": "پہلے ورقے تے ونڄو",
+       "tooltip-n-portal": "ایں مںصوبے بارے، تساں کیا کر سڳدو، ، چیزاں کتھوں ڳولوں",
+       "tooltip-n-currentevents": "موجودہ حالات وچ پچھلیاں معلومات ݙیکھو",
+       "tooltip-n-recentchanges": "وکی تے نویاں تبدیلیاں۔",
+       "tooltip-n-randompage": "کوئی صفہ کھولو۔",
+       "tooltip-n-help": "لبھݨ دی جاہ",
+       "tooltip-t-whatlinkshere": "ایں نال جڑے سارے وکی ورقے۔",
+       "tooltip-t-recentchangeslinked": "ایں ورقے توں جڑے ورقیاں وچ نویاں تبدیلیاں",
+       "tooltip-feed-atom": "اِیں ورقے دا اٹوم فیڈ",
+       "tooltip-t-emailuser": "{{GENDER:$1|اایں صارف}} کوں ای میل بھیجو",
+       "tooltip-t-upload": "فائل چڑھاؤ",
+       "tooltip-t-specialpages": "سارے خاص ورقیاں دی تندیر",
+       "tooltip-t-print": "ایں ورقے دا چھپݨ آلا انگ ݙیکھو",
+       "tooltip-t-permalink": "اس صفے دے ایں روپ نال پکا جوڑ",
+       "tooltip-ca-nstab-main": "مواد آلا صفہ ݙیکھو",
+       "tooltip-ca-nstab-user": "صارف دا ورقہ ݙیکھو",
+       "tooltip-ca-nstab-special": "ایہ ہک خاص ورقہ ہے، اینکوں تبدیل نسے کرسڳدے",
+       "tooltip-ca-nstab-project": "منصبے آلا ورقہ ݙیکھو",
+       "tooltip-ca-nstab-image": "فائل دا ورقہ ݙیکھو",
+       "tooltip-ca-nstab-mediawiki": "نظامی سنیہہ ݙیکھو",
+       "tooltip-ca-nstab-template": "سانچہ ݙیکھو",
+       "tooltip-ca-nstab-category": "کیٹاگری آلا ورقہ ݙیکھو",
+       "tooltip-minoredit": "ایں کوں نکی ترممیم وچ ڳݨو",
+       "tooltip-save": "تبدیلیاں محفوظ کرو",
+       "tooltip-preview": "محفوظ کرݨ کنے پہلے تبدیلیاں ݙیکھو، مہربانی ہوسی۔",
+       "tooltip-diff": "ایں لکھت وچ کیتیاں ڳیاں تبدیلیاں ݙیکھاؤ",
+       "tooltip-watch": "ایں ورقے کوں آپݨی دید آلے ورقیاں وچ رکھو",
+       "tooltip-rollback": "رول بیک\" ہک کلک وچ ورقے کوں پچھلی حالت وچ گھن ویسی\"",
+       "tooltip-undo": "واپس تے کلک کرݨ نال  پچھلی ترمیم تے پُڄ ویسو، نمائشی انداز وچ ترمیم دا خانہ کھلسی۔ تساں مختصر سسب وی بیان کر سڳدے ہو۔",
+       "tooltip-summary": "مختصر خلاصہ درج کرو",
+       "simpleantispam-label": "سپام روک پھاٹک\nاینکوں <strong>نہ</strong>  بھرو!",
+       "pageinfo-title": "«$1» دی معلومات",
+       "pageinfo-header-basic": "بنیادی معلومات",
+       "pageinfo-header-edits": "تاریخچۂ ترمیم",
+       "pageinfo-header-restrictions": "ورقے دی حفاظت",
+       "pageinfo-header-properties": "صفحہ دی خاصیتاں",
+       "pageinfo-display-title": "عنوان",
+       "pageinfo-default-sort": "کلید برائے ابتدائی ترتیب",
+       "pageinfo-length": "ورقے دی لمناݨ (بائٹ وچ)",
+       "pageinfo-article-id": "ورقے دی شناخت",
+       "pageinfo-language": "زبان",
+       "pageinfo-content-model": "انداز متن",
+       "pageinfo-robot-policy": "روبوٹ دی فہرست سازی",
+       "pageinfo-robot-index": "مجاز",
+       "pageinfo-robot-noindex": "ممنوع",
+       "pageinfo-watchers": "تعداد ناظرین",
+       "pageinfo-few-watchers": "$1 کنوں گھٹ {{PLURAL:$1|ناظر|ناظرین}}",
+       "pageinfo-redirects-name": "رجوعاں  دی تعداد",
+       "pageinfo-subpages-name": "ایں ورقے دے ذیلی ورقیاں دی تعداد",
+       "pageinfo-firstuser": "صفحہ ساز",
+       "pageinfo-firsttime": "صفحہ سازی دی تاریخ",
+       "pageinfo-lastuser": "چھیکڑی ترمیم کنندہ",
+       "pageinfo-lasttime": "چھیکڑی ترمیم دی تاریخ",
+       "pageinfo-edits": "ترامیم دی مجموعی تعداد",
+       "pageinfo-authors": "مختلف مصنفین دی  تعداد",
+       "pageinfo-recent-edits": "حالیہ ترامیم دی تعداد (گزشتہ $1 وچ)",
+       "pageinfo-recent-authors": "مختلف مصنفین دی حالیہ تعداد",
+       "pageinfo-magic-words": "جادوئی {{PLURAL:$1|لفظ|الفاظ}} ($1)",
+       "pageinfo-hidden-categories": "پوشیدہ {{PLURAL:$1|زمرہ|زمرہ جات}} ($1)",
+       "pageinfo-templates": "زیر استعمال {{PLURAL:$1|سانچہ|سانچے}} ($1)",
+       "pageinfo-toolboxlink": "معلومات صفحہ",
+       "pageinfo-contentpage": "شمار بطور ورقہ",
+       "pageinfo-contentpage-yes": "ڄیا",
+       "patrol-log-page": "گشت لاگ",
+       "previousdiff": "← پرانی لکھائی",
+       "nextdiff": "نویں لکھائی →",
+       "widthheightpage": "$1×$2، $3 {{PLURAL:$3|ورقہ|ورقے}}",
+       "file-info-size": "\n$1 × $2 پکسل، فائل دا حجم: $3، MIME قسم: $4",
+       "file-info-size-pages": "$1 × $2 پکسل، فائل دا حجم: $3، MIME قسم: $4، $5 {{PLURAL:$5|ورقہ|ورقے}}",
+       "file-nohires": "ایں توں زیادہ ریزولیوشن دستیاب کائنی۔",
+       "svg-long-desc": "ایس وی جی فائل، ابعاد $1 × $2 پکسل، فائل دا حجم: $3",
+       "show-big-image": "اصل فائل",
+       "show-big-image-preview": "ایں نمائش دا حجم:$1",
+       "show-big-image-other": "ٻیاں {{PLURAL:$2|قرارداد|قراردادیں}}: $1۔",
+       "show-big-image-size": "$1 × $2 پکسلز",
+       "metadata": "میٹا ڈیٹا",
+       "metadata-help": "ایں فائل وچ ٻیاں معلومات وی ہن۔ شاید او تہاݙے کیمرے یا سیکنر توں آیاں ہن، جیندے نال تساں ایہ فائل بݨائی ہائی۔\nجے ایہ فائل آپݨی اصل حالت وچ نہ ہووے تاں کجھ معلومات تبدیل تھئی ہوئی فائل دی پوری پوری عکاسی کائناں کریسی۔",
+       "metadata-fields": "تصویر دے میٹاڈیٹا دے او خانے جہڑے پیغام میں درج ہن او تصویر دے صفحے تے شامل ہوندے ہن۔ ایہ ااوں ویلے ظاہر تھیندن جڈݨ میٹاڈیٹا کوں ودھایا ونڄے۔\nٻئے خانے شروع وچ لُڳے ہوندن۔\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
+       "exif-orientation": "اورینٹیشن",
+       "exif-xresolution": "افقی ریزولوشن",
+       "exif-yresolution": "عمودی ریزولیشن",
+       "exif-datetime": "فائل بدلݨ دی تاریخ تے ویلا",
+       "exif-make": "کیمرہ ساز کمپنی",
+       "exif-model": "کیمرے دا ماڈل",
+       "exif-software": "مستعمل سافٹ ویئر",
+       "exif-exifversion": "اکزیف ورژن",
+       "exif-colorspace": "رنگ فضا",
+       "exif-datetimeoriginal": "ڈیٹا بݨاوݨ دی تاریخ تے ویلا",
+       "exif-datetimedigitized": "ڈجیٹائزنگ دا ویلہ تے تریخ",
+       "exif-orientation-1": "عام",
+       "namespacesall": "یکے",
+       "monthsall": "یکے",
+       "imgmultipagenext": "اگلا →",
+       "imgmultigo": "ونڄو!",
+       "imgmultigoto": "$1 تے ونڄو",
+       "watchlisttools-clear": "زیرنظر فہرست دی صفائی",
+       "watchlisttools-view": "متعلقہ تبدیلیاں ݙیکو",
+       "watchlisttools-edit": "زیرنظر فہرست  کوں ݙیکھو تے تبدیلی کرو",
+       "watchlisttools-raw": "کچی زیرِنظرفہرست وچ تبدیلی کرو",
+       "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|تبادلۂ خیال]])",
+       "redirect": "فائل، صارف، ورقہ،دہرائی یا آئی ڈی لاگ دے ذریعے ولدا واپس",
+       "redirect-submit": "ڄلو",
+       "redirect-lookup": "تلاش:",
+       "redirect-value": "قدر:",
+       "redirect-user": "صارف دی شناخت",
+       "redirect-page": "ورقے دی شناخت",
+       "redirect-revision": "ورقے دا رویژن",
+       "redirect-file": "فائل دا ناں",
+       "specialpages": "خاص ورقے",
+       "tag-filter": "[[Special:Tags|Tag]] نتارا:",
+       "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|ٹیگ|ٹیگز}}]]: $2)",
+       "tags-active-yes": "ڄیا",
+       "tags-active-no": "کو",
+       "tags-hitcount": "$1 {{PLURAL:$1|تبدیلی|تبدیلیاں}}",
+       "logentry-delete-delete": "$1 {{GENDER:$2|مٹایا ڳیا}} ورقہ $3",
+       "logentry-delete-restore": "$1 {{GENDER:$2|بحال تھی ڳیوہے}} page $3 ($4)",
+       "revdelete-content-hid": "مواد لکیا",
+       "logentry-move-move": "$1 {{جنس:$2|پلٹی}} صفہ $3 توں $4",
+       "logentry-newusers-create": "صارف کھاتہ $1 {{GENDER:$2|بݨایا ڳیا}}",
+       "logentry-newusers-autocreate": "صارف کھاتہ $1 خودکار طور  {{GENDER:$2|تخلیق تھیا}}",
+       "logentry-upload-upload": "$1 {{GENDER:$2|اپلوڈ}} $3",
+       "logentry-upload-overwrite": "$1 نے $3 دا نواں نسخہ {{GENDER:$2|اپلوڈ کیتا}}",
+       "searchsuggest-search": "ڳولو",
+       "duration-days": "$1 {{PLURAL:$1|ݙینہ}}",
+       "randomrootpage": "بے ترتيب بنیادی صفحہ"
+}
index 1c6df44..b60523e 100644 (file)
        "rcfilters-hideminor-conflicts-typeofchange-global": "Filter »Manjša urejanja« je v sporu z enim ali več filtri Vrsta spremembe, ker nekaterih vrst urejanj ni možno označiti kot »manjša«. Filtri v sporu so označeni v območju Dejavni filtri zgoraj.",
        "rcfilters-hideminor-conflicts-typeofchange": "Nekaterih vrst sprememb ni možno označiti kot »manjše«, zato je ta filter v sporu z naslednjimi filtri Vrsta spremembe: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Ta filter Vrsta spremembe je v sporu s filtrom »Manjše urejanje«. Nekaterih vrst sprememb ni možno označiti kot »manjše«.",
-       "rcfilters-filtergroup-lastRevision": "Zadnja redakcija",
-       "rcfilters-filter-lastrevision-label": "Zadnja redakcija",
-       "rcfilters-filter-lastrevision-description": "Najnovejša sprememba strani.",
-       "rcfilters-filter-previousrevision-label": "Zgodnejše redakcije",
-       "rcfilters-filter-previousrevision-description": "Vse spremembe, ki niso najnovejša sprememba strani.",
+       "rcfilters-filtergroup-lastRevision": "Najnovejše redakcije",
+       "rcfilters-filter-lastrevision-label": "Najnovejša redakcija",
+       "rcfilters-filter-lastrevision-description": "Samo najnovejša sprememba strani.",
+       "rcfilters-filter-previousrevision-label": "Nenajnovejša redakcija",
+       "rcfilters-filter-previousrevision-description": "Vse spremembe, ki niso »najnovejša redakcija«.",
        "rcfilters-filter-excluded": "Izključeno",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:ne</strong> $1",
        "rcfilters-exclude-button-off": "Izključi izbrane",
        "fileduplicatesearch-noresults": "Datoteke imenovane »$1« ni mogoče najti.",
        "specialpages": "Posebne strani",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Navadne posebne strani.\n* <span class=\"mw-specialpagerestricted\">Omejene posebne strani.</span>",
+       "specialpages-note-restricted": "* Navadne posebne strani.\n* <span class=\"mw-specialpagerestricted\">Omejene posebne strani.</span>",
        "specialpages-group-maintenance": "Vzdrževalna poročila",
        "specialpages-group-other": "Ostale posebne strani",
        "specialpages-group-login": "Prijavite se / ustvarite račun",
index 1f4b643..1ee5c3b 100644 (file)
        "fileduplicatesearch-noresults": "Датотека под називом „$1“ није пронађена.",
        "specialpages": "Посебне странице",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Нормалне посебне странице\n* <span class=\"mw-specialpagerestricted\">Ограничене посебне странице</span>",
        "specialpages-group-maintenance": "Извештаји одржавања",
        "specialpages-group-other": "Остале посебне странице",
        "specialpages-group-login": "Пријава / регистрација",
index ac3c379..7bf39b3 100644 (file)
        "fileduplicatesearch-noresults": "Datoteka pod nazivom „$1“ nije pronađena.",
        "specialpages": "Posebne stranice",
        "specialpages-note-top": "Legenda",
-       "specialpages-note": "* Normalne posebne stranice\n* <span class=\"mw-specialpagerestricted\">Ograničene posebne stranice</span>",
        "specialpages-group-maintenance": "Izveštaji održavanja",
        "specialpages-group-other": "Ostale posebne stranice",
        "specialpages-group-login": "Prijava / registracija",
index 1303239..961ba5a 100644 (file)
        "rcfilters-legend-heading": "<strong>Lista över förkortningar:</strong>",
        "rcfilters-activefilters": "Aktiva filter",
        "rcfilters-advancedfilters": "Avancerade filter",
+       "rcfilters-limit-title": "Ändringar att visa",
+       "rcfilters-limit-shownum": "Visa de senaste $1 ändringarna",
+       "rcfilters-days-title": "Senaste dagarna",
+       "rcfilters-hours-title": "Senaste timmarna",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|dag|dagar}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|timme|timmar}}",
        "rcfilters-quickfilters": "Sparade filter",
        "rcfilters-quickfilters-placeholder-title": "Inga länkar har sparats ännu",
        "rcfilters-quickfilters-placeholder-description": "För att spara dina filterinställningar och återanvända dem senare, klicka på bokmärkesikonen under \"Aktiva filter\" nedan.",
        "rcfilters-invalid-filter": "Ogiltigt filter",
        "rcfilters-empty-filter": "Inga aktiva filter. Alla bidrag visas.",
        "rcfilters-filterlist-title": "Filter",
-       "rcfilters-filterlist-whatsthis": "Vad är detta?",
+       "rcfilters-filterlist-whatsthis": "Hur fungerar desse?",
        "rcfilters-filterlist-feedbacklink": "Ge återkoppling på nya (beta)filter",
        "rcfilters-highlightbutton-title": "Markera resultat",
        "rcfilters-highlightmenu-title": "Välj en färg",
        "rcfilters-filter-editsbyself-description": "Dina egna bidrag.",
        "rcfilters-filter-editsbyother-label": "Ändringar av andra",
        "rcfilters-filter-editsbyother-description": "Alla ändringar förutom dina egna.",
-       "rcfilters-filtergroup-userExpLevel": "Erfarenhetsnivå (endast för registrerade användare)",
+       "rcfilters-filtergroup-userExpLevel": "Användarregistrering och -erfarenhet",
        "rcfilters-filter-user-experience-level-registered-label": "Registrerade",
        "rcfilters-filter-user-experience-level-registered-description": "Inloggade redigerare.",
        "rcfilters-filter-user-experience-level-unregistered-label": "Oregistrerade",
        "rcfilters-filter-user-experience-level-unregistered-description": "Redigerare som inte är inloggade.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Nykomlingar",
-       "rcfilters-filter-user-experience-level-newcomer-description": "Färre än 10 redigeringar och 4 dagars aktivitet.",
+       "rcfilters-filter-user-experience-level-newcomer-description": "Registrerade redigerare med färre än 10 redigeringar och 4 dagars aktivitet.",
        "rcfilters-filter-user-experience-level-learner-label": "Nybörjare",
-       "rcfilters-filter-user-experience-level-learner-description": "Mer erfarenhet än \"Nybörjare\" men mindre än \"Erfarna användare\".",
+       "rcfilters-filter-user-experience-level-learner-description": "Registrerade redigerare vars erfarenhet hamnar mellan \"Nybörjare\" och \"Erfarna användare\".",
        "rcfilters-filter-user-experience-level-experienced-label": "Erfarna användare",
-       "rcfilters-filter-user-experience-level-experienced-description": "Fler än 30 dagars aktivitet och 500 redigeringar.",
+       "rcfilters-filter-user-experience-level-experienced-description": "Registrerade redigerare med fler än 500 redigeringar och 30 dagars aktivitet.",
        "rcfilters-filtergroup-automated": "Automatiserade bidrag",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Redigeringar gjorda av automatiserade verktyg.",
        "rcfilters-hideminor-conflicts-typeofchange-global": "Filtret \"Mindre redigering\" är i konflikt med en eller flera ändringstypfilter, eftersom vissa ändringstyper inte kan betecknas som \"mindre\". Filtren som är i konflikt är markerade i området med aktiva filter ovan.",
        "rcfilters-hideminor-conflicts-typeofchange": "Vissa ändringstyper kan inte betecknas som \"mindre\", så detta filter är i konflikt med följande ändringstypfilter: $1",
        "rcfilters-typeofchange-conflicts-hideminor": "Detta ändringstypfilter är i konflikt med filtret \"Mindre ändringar\". Vissa ändringstyper kan inte betecknas som \"mindre\".",
-       "rcfilters-filtergroup-lastRevision": "Senaste version",
+       "rcfilters-filtergroup-lastRevision": "Senaste versioner",
        "rcfilters-filter-lastrevision-label": "Senaste version",
-       "rcfilters-filter-lastrevision-description": "Den senaste ändringen av en sida.",
-       "rcfilters-filter-previousrevision-label": "Tidigare versioner",
-       "rcfilters-filter-previousrevision-description": "Alla ändringar som inte är den senaste ändringen av en sida.",
+       "rcfilters-filter-lastrevision-description": "Endast senaste ändringen av en sida.",
+       "rcfilters-filter-previousrevision-label": "Inte den senaste versionen",
+       "rcfilters-filter-previousrevision-description": "Alla ändringar som inte är den \"senaste versionen\".",
        "rcfilters-filter-excluded": "Exkluderad",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:not</strong> $1",
+       "rcfilters-exclude-button-off": "Exkludera markerade",
+       "rcfilters-exclude-button-on": "Exkluderar markerade",
        "rcfilters-view-tags": "Märkta redigeringar",
        "rcfilters-view-namespaces-tooltip": "Filtrera resultat efter namnrymder",
        "rcfilters-view-tags-tooltip": "Filtrera resultat med redigeringsmärken",
        "delete-warning-toobig": "Denna sida har en lång redigeringshistorik med mer än $1 {{PLURAL:$1|sidversion|sidversioner}}. Att radera sidan kan skapa problem med hanteringen av databasen på {{SITENAME}}; var försiktig.",
        "deleteprotected": "Du kan inte radera denna sida eftersom den är skyddad.",
        "deleting-backlinks-warning": "<strong>Varning:</strong>\n[[Special:WhatLinksHere/{{FULLPAGENAME}}|Andra sidor]] länkar till eller inkluderar sidan som du är på väg att radera.",
+       "deleting-subpages-warning": "<strong>Varning:</strong> Sidan du håller på att radera har [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|en undersida|$1 undersidor|51=över 50 undersidor}}]].",
        "rollback": "Rulla tillbaka ändringar",
        "rollbacklink": "rulla tillbaka",
        "rollbacklinkcount": "rulla tillbaka $1 {{PLURAL:$1|redigering|redigeringar}}",
        "fileduplicatesearch-noresults": "Ingen fil med namnet \"$1\" hittades.",
        "specialpages": "Specialsidor",
        "specialpages-note-top": "Teckenförklaring",
-       "specialpages-note": "* Normala specialsidor.\n* <span class=\"mw-specialpagerestricted\">Specialsidor med begränsad åtkomst.</span>",
        "specialpages-group-maintenance": "Underhållsrapporter",
        "specialpages-group-other": "Övriga specialsidor",
        "specialpages-group-login": "Logga in / skapa konto",
index 2e31c50..493d2e6 100644 (file)
        "fileduplicatesearch-noresults": "\"$1\" అనే పేరుగల దస్త్రమేమీ కనబడలేదు.",
        "specialpages": "ప్రత్యేక పేజీలు",
        "specialpages-note-top": "సూచిక",
-       "specialpages-note": "* మామూలు ప్రత్యేక పుటలు.\n* <span class=\"mw-specialpagerestricted\">నియంత్రిత ప్రత్యేక పుటలు.</span>",
        "specialpages-group-maintenance": "నిర్వహణా నివేదికలు",
        "specialpages-group-other": "ఇతర ప్రత్యేక పేజీలు",
        "specialpages-group-login": "ప్రవేశించండి / ఖాతాను సృష్టించుకోండి",
index 05cf315..3d72f11 100644 (file)
        "page_first": "uluk",
        "page_last": "ikus",
        "histfirst": "sedu liu hotu",
-       "histlast": "Foun liu hotu",
+       "histlast": "foun liu hotu",
        "historyempty": "(mamuk)",
        "history-feed-item-nocomment": "$1 iha $2",
        "rev-delundel": "hatudu/subar",
index c1ccd60..25c0457 100644 (file)
@@ -13,7 +13,8 @@
                        "아라",
                        "Macofe",
                        "AryanSogd",
-                       "ToJack"
+                       "ToJack",
+                       "Vashgird"
                ]
        },
        "tog-underline": "Пайвандҳо хаткашида:",
        "license": "Иҷозатнома:",
        "license-header": "Иҷозатнома",
        "nolicense": "Ҳеҷ яке интихоб нашудааст",
+       "licenses-edit": "Тағйири имконоти иҷозатнома",
        "license-nopreview": "(Пешнамоиш вуҷуд надорад)",
        "upload_source_url": "(як нишони интернетии мӯътабар ва оммавӣ)",
        "upload_source_file": " (парвандае дар компютери шумо)",
index 92f157d..146647e 100644 (file)
@@ -70,7 +70,8 @@
                        "Олександр",
                        "Similartothissimilartothat",
                        "Bunyk",
-                       "Choomaq"
+                       "Choomaq",
+                       "SimondR"
                ]
        },
        "tog-underline": "Підкреслювання посилань:",
        "rcfilters-invalid-filter": "Недійсний фільтр",
        "rcfilters-empty-filter": "Без фільтрів. Показано всі зміни.",
        "rcfilters-filterlist-title": "Фільтри",
-       "rcfilters-filterlist-whatsthis": "Що Ñ\86е?",
+       "rcfilters-filterlist-whatsthis": "Як Ñ\86е Ð¿Ñ\80аÑ\86Ñ\8eÑ\94?",
        "rcfilters-filterlist-feedbacklink": "Надайте відгук про нові (бета) фільтри",
        "rcfilters-highlightbutton-title": "Виділити результати",
        "rcfilters-highlightmenu-title": "Вибрати колір",
        "rcfilters-noresults-conflict": "Результатів не знайдено через конфлікт у пошукових критеріях",
        "rcfilters-state-message-subset": "Цей фільтр не має впливу, оскільки його результати включені в результати {{PLURAL:$2|цього, ширшого, фільтра|цих, ширших, фільтрів}} (спробуйте увімкнути виділення, щоб вирізнити їх): $1",
        "rcfilters-state-message-fullcoverage": "Вибір усіх фільтрів у групі — це все одно, що не вибирати жодного з них, тобто таке фільтрування не має впливу. Гупа містить: $1",
-       "rcfilters-filtergroup-registration": "Реєстрація користувача",
-       "rcfilters-filter-registered-label": "Зареєстровані",
-       "rcfilters-filter-registered-description": "Користувачі, що увійшли в систему.",
-       "rcfilters-filter-unregistered-label": "Незареєстровані",
-       "rcfilters-filter-unregistered-description": "Користувачі, які не ввійшли в систему.",
-       "rcfilters-filter-unregistered-conflicts-user-experience-level": "Цей фільтр конфліктує з {{PLURAL:$2|таким фільтром|такими фільтрами}} досвіду, {{PLURAL:$2|який знаходить|які знаходять}} лише зареєстрованих користувачів: $1",
        "rcfilters-filtergroup-authorship": "Авторство внеску",
        "rcfilters-filter-editsbyself-label": "Зміни, здійснені Вами",
        "rcfilters-filter-editsbyself-description": "Ваш власний внесок.",
        "rcfilters-filter-editsbyother-label": "Зміни, здійснені іншими",
        "rcfilters-filter-editsbyother-description": "Усі зміни, за винятком Ваших власних.",
        "rcfilters-filtergroup-userExpLevel": "Рівень досвіду (тільки для зареєстрованих користувачів)",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered": "Фільтри досвіду знаходять лише зареєстрованих користувачів, тож цей фільтр конфліктує з фільтром «Незареєстровані».",
-       "rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global": "Фільтр «Незареєстровані» конфліктує з одним або більше фільтрами досвіду, які знаходять лише зареєстрованих користувачів. Конфліктні фільтри позначені вище в ділянці активних фільтрів.",
+       "rcfilters-filter-user-experience-level-registered-label": "Зареєстровані",
+       "rcfilters-filter-user-experience-level-registered-description": "Користувачі, що увійшли в систему.",
+       "rcfilters-filter-user-experience-level-unregistered-label": "Незареєстровані",
+       "rcfilters-filter-user-experience-level-unregistered-description": "Користувачі, які не ввійшли в систему.",
        "rcfilters-filter-user-experience-level-newcomer-label": "Новачки",
        "rcfilters-filter-user-experience-level-newcomer-description": "Менше ніж 10 редагувань і 4 дні активності.",
        "rcfilters-filter-user-experience-level-learner-label": "Учні",
        "fileduplicatesearch-noresults": "Файл з назвою «$1» не знайдено.",
        "specialpages": "Спеціальні сторінки",
        "specialpages-note-top": "Легенда",
-       "specialpages-note": "* Звичайні службові сторінки\n* <span class=\"mw-specialpagerestricted\">Сторінки з обмеженим доступом.</span>",
        "specialpages-group-maintenance": "Технічні звіти",
        "specialpages-group-other": "Інші",
        "specialpages-group-login": "Вхід до системи / реєстрація",
index d415870..8dd0c53 100644 (file)
        "fileduplicatesearch-noresults": "Không tìm thấy tập tin nào tên “$1”.",
        "specialpages": "Các trang đặc biệt",
        "specialpages-note-top": "Chú giải",
-       "specialpages-note": "* Trang đặc biệt thông thường.\n* <strong class=\"mw-specialpagerestricted\">Trang đặc biệt được hạn chế.</strong>",
        "specialpages-group-maintenance": "Báo cáo bảo quản",
        "specialpages-group-other": "Trang đặc biệt khác",
        "specialpages-group-login": "Đăng nhập / Mở tài khoản",
index 4001765..da6046e 100644 (file)
        "recentchanges-legend-plusminus": "(''±123'')",
        "recentchanges-submit": "ווייזן",
        "rcfilters-activefilters": "אַקטיווע פילטערס",
+       "rcfilters-days-show-days": "$1 {{PLURAL:$1|טאג|טעג}}",
+       "rcfilters-days-show-hours": "$1 {{PLURAL:$1|שעה|שעה'ן}}",
        "rcfilters-quickfilters": "אויפֿגעהיטענע פֿילטערס",
        "rcfilters-quickfilters-placeholder-title": "קיין לינקען נאך נישט אויפֿגעהיטן",
        "rcfilters-savedqueries-defaultlabel": "אױפֿגעהיטענע פֿילטערס",
        "rcfilters-invalid-filter": "אומגילטיגער פֿילטער",
        "rcfilters-empty-filter": "קיין אַקטיווע פילטערס. אלע ביישטייערונגען געוויזן.",
        "rcfilters-filterlist-title": "פֿילטערס",
-       "rcfilters-filterlist-whatsthis": "וואס איז דאס?",
+       "rcfilters-filterlist-whatsthis": "ווי ארבעט דאס?",
+       "rcfilters-highlightbutton-title": "ארויסשטאַרצן רעזולטאַטן",
        "rcfilters-highlightmenu-title": "אויסקלויבן א קאליר",
        "rcfilters-filterlist-noresults": "קיין פֿילטערס נישט געטראפֿן",
-       "rcfilters-filtergroup-registration": "באניצער איינשרייבונג",
-       "rcfilters-filter-registered-label": "אײַנגעשריבן",
        "rcfilters-filter-editsbyself-label": "ענדערונגען פון אייך",
        "rcfilters-filter-editsbyself-description": "אייערע אייגענע בײשטײערונגען.",
        "rcfilters-filter-editsbyother-label": "ענדערונגען פֿון אנדערע",
        "rcfilters-filter-editsbyother-description": "אלע ענדערונגען אחוץ אייערע אייגענע.",
+       "rcfilters-filter-user-experience-level-registered-label": "אײַנגעשריבן",
        "rcfilters-filter-user-experience-level-learner-label": "לערנער",
        "rcfilters-filter-bots-label": "באט",
        "rcfilters-filter-humans-label": "מענטש (נישט קיין באט)",
        "rcfilters-filter-minor-label": "מינערדיקע רעדאַקטירונגען",
        "rcfilters-filter-pageedits-label": "בלאט רעדאקטירונגען",
        "rcfilters-filter-newpages-label": "בלאַט־שאַפֿונגען",
-       "rcfilters-filtergroup-lastRevision": "לעצטע ווערסיע",
+       "rcfilters-filtergroup-lastRevision": "לעצטע ווערסיעס",
        "rcfilters-filter-lastrevision-label": "לעצטע ווערסיע",
-       "rcfilters-filter-previousrevision-label": "פֿר×\99ער×\93×\99קע ווערסיעס",
+       "rcfilters-filter-previousrevision-label": "× ×\99ש×\98 ×\93×\99 ×\9cעצ×\98ע ווערסיעס",
        "rcfilters-filter-excluded": "אויסגעשלאסן",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:נישט</strong> $1",
        "rcnotefrom": "פֿאלגנד {{PLURAL:$5|איז די ענדערונג| זענען די ענדערונגען}} זײַט <strong>$3, $4</strong> (ביז <strong>$1</strong>).",
        "undeletecomment": "אורזאַך:",
        "cannotundelete": "טייל אדער גארע צוריקשטעלונג איז דורכגעפאלן: $1",
        "undeletedpage": "'''דער בלאט $1 איז געווארן צוריקגעשטעלט.'''\n\nזעט דעם [[Special:Log/delete| אויסמעקן לאג]] פֿאר א ליסטע פון די לעצטע אויסגעמעקטע און צוריקגעשטעלטע בלעטער.",
-       "undelete-header": "זעט [[Special:Log/delete|דעם אויסמעקונג זשורנאַל]] פֿאַר בלעטער וואָס זענען לעצטנס געווארן אויסגעמעקט recently deleted pages.",
+       "undelete-header": "זעט [[Special:Log/delete|דעם אויסמעקונג זשורנאַל]] פֿאַר בלעטער וואָס זענען לעצטנס געווארן אויסגעמעקט.",
        "undelete-search-title": "זוכן אויסגעמעקטע בלעטער",
        "undelete-search-box": "זוכן אויסגעמעקטע בלעטער",
        "undelete-search-prefix": "ווײַז בלעטער וואס הייבן אן מיט:",
        "version-entrypoints-header-url": "URL",
        "version-libraries-library": "ביבליאטעק",
        "version-libraries-version": "ווערסיע",
+       "redirect": "ווייטערפֿירן לויט טעקע, באַניצער, בלאַט, ווערסיע אדער לאגבוך אידענטיפֿצירער",
        "redirect-submit": "גייט",
        "redirect-lookup": "זוכן:",
        "redirect-value": "ווערט:",
        "fileduplicatesearch-noresults": "קיין טעקע מיטן נאמען \"$1\" נישט געטראפֿן.",
        "specialpages": "ספעציעלע בלעטער",
        "specialpages-note-top": "לעגענדע",
-       "specialpages-note": "* נארמאַלע באַזונדערע בלעטער.\n* <span class=\"mw-specialpagerestricted\">באַגרענעצטע באַזונדערע בלעטער.</span>",
        "specialpages-group-maintenance": "אויפֿהאַלטונג באַריכטן",
        "specialpages-group-other": "אַנדערע ספעציעלע בלעטער",
        "specialpages-group-login": "ארײַנלאגירן / שאַפֿן קאנטע",
index 7c9cd49..2f02916 100644 (file)
        "recentchanges-legend-heading": "<strong>標記:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (睇埋[[Special:NewPages|新開版]])",
        "recentchanges-submit": "顯示",
-       "rcfilters-filter-editsbyself-description": "你改嘅嘢。",
+       "rcfilters-filter-editsbyself-description": "你嘅貢獻。",
        "rcfilters-filter-editsbyother-label": "其他人改嘅嘢",
-       "rcfilters-filter-editsbyother-description": "其他人(唔係你)改嘅嘢",
+       "rcfilters-filter-editsbyother-description": "所有改過嘅嘢(除咗你自己)",
        "rcfilters-filtergroup-userExpLevel": "經驗級別(只限簽咗到嘅用戶)",
        "rcfilters-filter-user-experience-level-newcomer-label": "新手",
-       "rcfilters-filter-user-experience-level-newcomer-description": "少過4日、10次編輯",
+       "rcfilters-filter-user-experience-level-newcomer-description": "少過4日、10次編輯嘅用戶",
        "rcfilters-filter-user-experience-level-learner-label": "學徒",
        "rcfilters-filter-user-experience-level-learner-description": "編輯數同經驗多過「新手」但少過「老手」。",
        "rcfilters-filter-user-experience-level-experienced-label": "老手",
-       "rcfilters-filter-user-experience-level-experienced-description": "超過30日同埋500次編輯",
+       "rcfilters-filter-user-experience-level-experienced-description": "超過30日同埋500次編輯嘅用戶",
        "rcfilters-filtergroup-automated": "自動貢獻",
        "rcfilters-filter-bots-label": "機械人",
        "rcfilters-filter-bots-description": "用自動工具做嘅貢獻",
        "fileduplicatesearch-result-n": "個檔案 \"$1\" 有$2項完全相同嘅重覆。",
        "fileduplicatesearch-noresults": "檔案名\"$1\"找不到",
        "specialpages": "特別頁",
-       "specialpages-note": "* 標準特別頁。\n* <span class=\"mw-specialpagerestricted\">有限制嘅特別頁。</span>",
        "specialpages-group-maintenance": "維護報告",
        "specialpages-group-other": "其它特別頁",
        "specialpages-group-login": "簽到/開新戶口",
index 2c12452..c46ad3a 100644 (file)
@@ -96,7 +96,8 @@
                        "D41D8CD98F",
                        "Wmr",
                        "逆襲的天邪鬼",
-                       "WhitePhosphorus"
+                       "WhitePhosphorus",
+                       "A2093064"
                ]
        },
        "tog-underline": "链接下划线:",
        "rcfilters-hideminor-conflicts-typeofchange-global": "“小编辑”过滤器与一个或多个更改类型过滤器冲突,因为其中某种更改类型不可指定为“小编辑”。冲突过滤器已在上方活跃过滤器中被标记。",
        "rcfilters-hideminor-conflicts-typeofchange": "某种更改类型不可指定为“小编辑”,因此该过滤器与以下更改类型过滤器相冲突:$1",
        "rcfilters-typeofchange-conflicts-hideminor": "这种更改类型过滤器与“小编辑”过滤器相冲突。某种更改类型不可指定为“小编辑”。",
-       "rcfilters-filtergroup-lastRevision": "最新版本",
-       "rcfilters-filter-lastrevision-label": "最新版本",
-       "rcfilters-filter-lastrevision-description": "对页面的最近更改。",
-       "rcfilters-filter-previousrevision-label": "早期版本",
-       "rcfilters-filter-previousrevision-description": "除最近更改外,所有对某一页面的更改。",
+       "rcfilters-filtergroup-lastRevision": "最新修订版本",
+       "rcfilters-filter-lastrevision-label": "最新修订版本",
+       "rcfilters-filter-lastrevision-description": "å\8fªå\8c\85æ\8b¬å¯¹é¡µé\9d¢ç\9a\84æ\9c\80è¿\91æ\9b´æ\94¹ã\80\82",
+       "rcfilters-filter-previousrevision-label": "不是最新修订版本",
+       "rcfilters-filter-previousrevision-description": "所有不是“最新修订版本”的更改。",
        "rcfilters-filter-excluded": "已排除",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:不是</strong>$1",
        "rcfilters-exclude-button-off": "排除选项",
        "rcfilters-view-namespaces-tooltip": "按名字空间过滤结果",
        "rcfilters-view-tags-tooltip": "按编辑标签过滤结果",
        "rcfilters-view-return-to-default-tooltip": "返回主过滤菜单",
-       "rcfilters-liveupdates-button": "å\9c¨çº¿更新",
+       "rcfilters-liveupdates-button": "å®\9eæ\97更新",
        "rcnotefrom": "下面{{PLURAL:$5|是}}<strong>$3 $4</strong>之后的更改(最多显示<strong>$1</strong>个)。",
        "rclistfromreset": "重置时间选择",
        "rclistfrom": "显示$3 $2之后的新更改",
        "fileduplicatesearch-noresults": "没有文件命名为\"$1\"发现。",
        "specialpages": "特殊页面",
        "specialpages-note-top": "说明",
-       "specialpages-note": "*普通特殊页面。\n*<span class=\"mw-specialpagerestricted\">受限特殊页面。</span>",
+       "specialpages-note-restricted": "* 普通特殊页面。\n* <span class=\"mw-specialpagerestricted\">受限特殊页面。</span>",
        "specialpages-group-maintenance": "维护报告",
        "specialpages-group-other": "其它特殊页面",
        "specialpages-group-login": "登录/创建账户",
index 16a8aaf..933567b 100644 (file)
        "rcfilters-filter-excluded": "已排除",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:不是</strong>$1",
        "rcfilters-view-tags": "標記的編輯",
+       "rcfilters-liveupdates-button": "實時更新",
        "rcnotefrom": "以下{{PLURAL:$5|為}}自 <strong>$3 $4</strong> 以來的變更 (最多顯示 <strong>$1</strong> 筆)。",
        "rclistfromreset": "重設日期選擇",
        "rclistfrom": "顯示自 $3 $2 以來的新變更",
        "fileduplicatesearch-noresults": "查無名稱為 \"$1\" 的檔案。",
        "specialpages": "特殊頁面",
        "specialpages-note-top": "說明",
-       "specialpages-note": "* 一般特殊頁面。\n* <span class=\"mw-specialpagerestricted\">受限制的特殊頁面。</span>",
        "specialpages-group-maintenance": "維護報表",
        "specialpages-group-other": "其它特殊頁面",
        "specialpages-group-login": "登入 / 建立帳號",
        "mw-widgets-usersmultiselect-placeholder": "加入更多...",
        "date-range-from": "開始日期:",
        "date-range-to": "結束日期:",
-       "sessionmanager-tie": "ç\84¡æ³\95å\90\88ä½µå¤\9aå\80\8bè«\8bæ±\82èª\8d証類型:$1。",
+       "sessionmanager-tie": "ç\84¡æ³\95å\90\88ä½µå¤\9aå\80\8bè«\8bæ±\82èª\8dè­\89類型:$1。",
        "sessionprovider-generic": "$1 連線階段",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "以 cookie 為基礎的連線階段",
        "sessionprovider-nocookies": "Cookie 功能可能已被關閉,請確認您改開啟 Cookie 功能並重新啟動。",
        "log-action-filter-suppress-reblock": "由重新封鎖禁止顯示使用者",
        "log-action-filter-upload-upload": "新上傳",
        "log-action-filter-upload-overwrite": "重新上傳",
-       "authmanager-authn-not-in-progress": "èª\8d証尚未進行或連線階段資料已遺失,請重頭再開始。",
+       "authmanager-authn-not-in-progress": "èª\8dè­\89尚未進行或連線階段資料已遺失,請重頭再開始。",
        "authmanager-authn-no-primary": "提供的憑證無法用來認証。",
        "authmanager-authn-no-local-user": "提供的憑證沒有與任何在此 wiki 上的使用者相關聯。",
        "authmanager-authn-no-local-user-link": "提供的憑證有效但沒有與任何在此 wiki 上的使用者相關聯。請採用其他方式登入,或建立新使用者,您將會有選項可以連結您先前的憑證到新帳號。",
        "authprovider-confirmlink-ok-help": "顯示連結失敗訊息後繼續。",
        "authprovider-resetpass-skip-label": "略過",
        "authprovider-resetpass-skip-help": "略過重設密碼。",
-       "authform-nosession-login": "å·²æ\88\90å\8a\9fèª\8d証,但您的瀏覽器無法 \"記住\" 登入資訊。\n\n$1",
+       "authform-nosession-login": "å·²æ\88\90å\8a\9fèª\8dè­\89,但您的瀏覽器無法 \"記住\" 登入資訊。\n\n$1",
        "authform-nosession-signup": "已建立帳號,但您的瀏覽器無法 \"記住\" 登入資訊。\n\n$1",
        "authform-newtoken": "缺少密鑰。$1",
        "authform-notoken": "缺少密鑰",
        "linkaccounts-submit": "連結帳號",
        "unlinkaccounts": "取消連結帳號",
        "unlinkaccounts-success": "已取消連結帳號。",
-       "authenticationdatachange-ignored": "èª\8d証資料變更未被處理,可能未設定提供者?",
+       "authenticationdatachange-ignored": "èª\8dè­\89資料變更未被處理,可能未設定提供者?",
        "userjsispublic": "請注意:JavaScript 子頁面可被其他使用者檢視,不應包含機密資料。",
        "usercssispublic": "請注意:CSS 子頁面可被其他使用者檢視,不應包含機密資料。",
        "restrictionsfield-badip": "無效的 IP 位址或範圍:$1",
diff --git a/languages/messages/MessagesSkr.php b/languages/messages/MessagesSkr.php
new file mode 100644 (file)
index 0000000..2072b8d
--- /dev/null
@@ -0,0 +1,11 @@
+<?php
+/** Saraiki (multiple scripts)
+ *
+ * To improve a translation please visit https://translatewiki.net
+ *
+ * @ingroup Language
+ * @file
+ *
+ */
+
+$fallback = 'skr-arab';
diff --git a/languages/messages/MessagesSkr_arab.php b/languages/messages/MessagesSkr_arab.php
new file mode 100644 (file)
index 0000000..901e2aa
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+/** Saraiki (Arabic script) (سرائیکی)
+ *
+ * To improve a translation please visit https://translatewiki.net
+ *
+ * @ingroup Language
+ * @file
+ *
+ */
+
+$fallback = 'ur, pnb';
+
+$rtl = true;
index 478a0c4..04565f2 100644 (file)
@@ -344,7 +344,7 @@ abstract class Maintenance {
         * @return mixed
         */
        protected function getStdin( $len = null ) {
-               if ( $len == Maintenance::STDIN_ALL ) {
+               if ( $len == self::STDIN_ALL ) {
                        return file_get_contents( 'php://stdin' );
                }
                $f = fopen( 'php://stdin', 'rt' );
@@ -457,7 +457,7 @@ abstract class Maintenance {
         * @return int
         */
        public function getDbType() {
-               return Maintenance::DB_STD;
+               return self::DB_STD;
        }
 
        /**
index 2262338..b21bfbb 100644 (file)
@@ -39,7 +39,7 @@ class AddRFCAndPMIDInterwiki extends LoggedUpdateMaintenance {
        }
 
        protected function updateSkippedMessage() {
-               return 'RFC and PMID already added to interwiki database table';
+               return 'RFC and PMID already added to interwiki database table.';
        }
 
        protected function doDBUpdates() {
index 8a837d2..d273a6a 100644 (file)
@@ -140,9 +140,6 @@ class GenerateSitemap extends Maintenance {
         */
        private $identifier;
 
-       /**
-        * Constructor
-        */
        public function __construct() {
                parent::__construct();
                $this->addDescription( 'Creates a sitemap for the site' );
index cf0acde..9e9fd3e 100644 (file)
@@ -40,7 +40,6 @@ class CheckLanguageCLI {
        private $includeExif = false;
 
        /**
-        * Constructor.
         * @param array $options Options for script.
         */
        public function __construct( array $options ) {
@@ -557,7 +556,6 @@ class CheckExtensionsCLI extends CheckLanguageCLI {
        private $extensions;
 
        /**
-        * Constructor.
         * @param array $options Options for script.
         * @param string $extension The extension name (or names).
         */
diff --git a/maintenance/populatePPSortKey.php b/maintenance/populatePPSortKey.php
new file mode 100644 (file)
index 0000000..7e3c2c3
--- /dev/null
@@ -0,0 +1,106 @@
+<?php
+/**
+ * Populate the pp_sortkey fields in the page_props table
+ *
+ * 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
+ *
+ * @file
+ * @ingroup Maintenance
+ */
+
+require_once __DIR__ . '/Maintenance.php';
+
+use Wikimedia\Rdbms\IDatabase;
+
+/**
+ * Usage:
+ *  populatePPSortKey.php
+ */
+class PopulatePPSortKey extends LoggedUpdateMaintenance {
+       public function __construct() {
+               parent::__construct();
+               $this->addDescription( 'Populate the pp_sortkey field' );
+               $this->setBatchSize( 100 );
+       }
+
+       protected function doDBUpdates() {
+               $dbw = $this->getDB( DB_MASTER );
+
+               $lastProp = null;
+               $lastPageValue = 0;
+               $editedRowCount = 0;
+
+               $this->output( "Populating page_props.pp_sortkey...\n" );
+               while ( true ) {
+                       $conditions = [ 'pp_sortkey IS NULL' ];
+                       if ( $lastPageValue !== 0 ) {
+                               $conditions[] = 'pp_page > ' . $dbw->addQuotes( $lastPageValue ) . ' OR ' .
+                                       '( pp_page = ' . $dbw->addQuotes( $lastPageValue ) .
+                                       ' AND pp_propname > ' . $dbw->addQuotes( $lastProp ) . ' )';
+                       }
+
+                       $res = $dbw->select(
+                               'page_props',
+                               [ 'pp_propname', 'pp_page', 'pp_sortkey', 'pp_value' ],
+                               $conditions,
+                               __METHOD__,
+                               [
+                                       'ORDER BY' => 'pp_page, pp_propname',
+                                       'LIMIT' => $this->mBatchSize
+                               ]
+                       );
+
+                       if ( $res->numRows() === 0 ) {
+                               break;
+                       }
+
+                       $this->beginTransaction( $dbw, __METHOD__ );
+
+                       foreach ( $res as $row ) {
+                               if ( !is_numeric( $row->pp_value ) ) {
+                                       continue;
+                               }
+                               $dbw->update(
+                                       'page_props',
+                                       [ 'pp_sortkey' => $row->pp_value ],
+                                       [
+                                               'pp_page' => $row->pp_page,
+                                               'pp_propname' => $row->pp_propname
+                                       ],
+                                       __METHOD__
+                               );
+                               $editedRowCount++;
+                       }
+
+                       $this->output( "Updated " . $editedRowCount . " rows\n" );
+                       $this->commitTransaction( $dbw, __METHOD__ );
+
+                       // We need to get the last element's page ID
+                       $lastPageValue = $row->pp_page;
+                       // And the propname...
+                       $lastProp = $row->pp_propname;
+               }
+
+               $this->output( "Populating page_props.pp_sortkey complete.\n" );
+       }
+
+       protected function getUpdateKey() {
+               return 'populate pp_sortkey';
+       }
+}
+
+$maintClass = 'PopulatePPSortKey';
+require_once RUN_MAINTENANCE_IF_MAIN;
index 117e9cc..f14856a 100644 (file)
@@ -48,7 +48,7 @@ class Sqlite {
         * @return bool True if no error or error string in case of errors
         */
        public static function checkSqlSyntax( $files ) {
-               if ( !Sqlite::isPresent() ) {
+               if ( !self::isPresent() ) {
                        throw new MWException( "Can't check SQL syntax: SQLite not found" );
                }
                if ( !is_array( $files ) ) {
index 5d773d1..01cf3c3 100644 (file)
@@ -47,7 +47,7 @@ class UserOptions {
         */
        function __construct( $opts, $args ) {
                if ( !$this->checkOpts( $opts, $args ) ) {
-                       UserOptions::showUsageAndExit();
+                       self::showUsageAndExit();
                } else {
                        $this->mReady = $this->initializeOpts( $opts, $args );
                }
index 92a218a..d2e4fcc 100644 (file)
--- a/phpcs.xml
+++ b/phpcs.xml
        <arg name="bootstrap" value="vendor/mediawiki/mediawiki-codesniffer/utils/bootstrap-ci.php"/>
        <arg name="encoding" value="UTF-8"/>
        <arg name="extensions" value="php,php5,inc,sample"/>
-       <exclude-pattern>node_modules/</exclude-pattern>
-       <exclude-pattern>vendor/</exclude-pattern>
        <exclude-pattern type="relative">^extensions/</exclude-pattern>
        <exclude-pattern type="relative">^skins/</exclude-pattern>
-       <exclude-pattern>.git</exclude-pattern>
-       <exclude-pattern>AdminSettings.php</exclude-pattern>
-       <exclude-pattern>LocalSettings.php</exclude-pattern>
-       <exclude-pattern>StartProfiler.php</exclude-pattern>
+       <exclude-pattern>AdminSettings\.php</exclude-pattern>
+       <exclude-pattern>LocalSettings\.php</exclude-pattern>
+       <exclude-pattern>StartProfiler\.php</exclude-pattern>
 </ruleset>
index 33fb8f1..f725efe 100644 (file)
@@ -1060,6 +1060,9 @@ return [
                'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.hlist' => [
+               'styles' => [
+                       'resources/src/mediawiki/mediawiki.hlist-allskins.less',
+               ],
                'skinStyles' => [
                        'default' => 'resources/src/mediawiki/mediawiki.hlist.css',
                ],
index ae68fc4..4749222 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * QUnit 1.23.1
+ * QUnit 2.4.0
  * https://qunitjs.com/
  *
  * Copyright jQuery Foundation and other contributors
  * Released under the MIT license
  * https://jquery.org/license
  *
- * Date: 2016-04-12T17:29Z
+ * Date: 2017-07-08T15:20Z
  */
 
 /** Font Family and Sizes */
@@ -27,7 +27,7 @@
 }
 
 
-/** Header */
+/** Header (excluding toolbar) */
 
 #qunit-header {
        padding: 0.5em 0 0.5em 1em;
        color: #FFF;
 }
 
-#qunit-testrunner-toolbar label {
-       display: inline-block;
-       padding: 0 0.5em 0 0.1em;
-}
-
 #qunit-banner {
        height: 5px;
 }
 
-#qunit-testrunner-toolbar {
-       padding: 0.5em 1em 0.5em 1em;
-       color: #5E740B;
-       background-color: #EEE;
-       overflow: hidden;
-}
-
 #qunit-filteredTest {
        padding: 0.5em 1em 0.5em 1em;
-       background-color: #F4FF77;
        color: #366097;
+       background-color: #F4FF77;
 }
 
 #qunit-userAgent {
        padding: 0.5em 1em 0.5em 1em;
-       background-color: #2B81AF;
        color: #FFF;
+       background-color: #2B81AF;
        text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
 }
 
-#qunit-modulefilter-container {
-       float: right;
-       padding: 0.2em;
+
+/** Toolbar */
+
+#qunit-testrunner-toolbar {
+       padding: 0.5em 1em 0.5em 1em;
+       color: #5E740B;
+       background-color: #EEE;
+}
+
+#qunit-testrunner-toolbar .clearfix {
+       height: 0;
+       clear: both;
 }
 
-.qunit-url-config {
+#qunit-testrunner-toolbar label {
        display: inline-block;
-       padding: 0.1em;
 }
 
-.qunit-filter {
-       display: block;
+#qunit-testrunner-toolbar input[type=checkbox],
+#qunit-testrunner-toolbar input[type=radio] {
+       margin: 3px;
+       vertical-align: -2px;
+}
+
+#qunit-testrunner-toolbar input[type=text] {
+       box-sizing: border-box;
+       height: 1.6em;
+}
+
+.qunit-url-config,
+.qunit-filter,
+#qunit-modulefilter {
+       display: inline-block;
+       line-height: 2.1em;
+}
+
+.qunit-filter,
+#qunit-modulefilter {
        float: right;
+       position: relative;
        margin-left: 1em;
 }
 
+.qunit-url-config label {
+       margin-right: 0.5em;
+}
+
+#qunit-modulefilter-search {
+       box-sizing: border-box;
+       width: 400px;
+}
+
+#qunit-modulefilter-search-container:after {
+       position: absolute;
+       right: 0.3em;
+       content: "\25bc";
+       color: black;
+}
+
+#qunit-modulefilter-dropdown {
+       /* align with #qunit-modulefilter-search */
+       box-sizing: border-box;
+       width: 400px;
+       position: absolute;
+       right: 0;
+       top: 50%;
+       margin-top: 0.8em;
+
+       border: 1px solid #D3D3D3;
+       border-top: none;
+       border-radius: 0 0 .25em .25em;
+       color: #000;
+       background-color: #F5F5F5;
+       z-index: 99;
+}
+
+#qunit-modulefilter-dropdown a {
+       color: inherit;
+       text-decoration: none;
+}
+
+#qunit-modulefilter-dropdown .clickable.checked {
+       font-weight: bold;
+       color: #000;
+       background-color: #D2E0E6;
+}
+
+#qunit-modulefilter-dropdown .clickable:hover {
+       color: #FFF;
+       background-color: #0D3349;
+}
+
+#qunit-modulefilter-actions {
+       display: block;
+       overflow: auto;
+
+       /* align with #qunit-modulefilter-dropdown-list */
+       font: smaller/1.5em sans-serif;
+}
+
+#qunit-modulefilter-dropdown #qunit-modulefilter-actions > * {
+       box-sizing: border-box;
+       max-height: 2.8em;
+       display: block;
+       padding: 0.4em;
+}
+
+#qunit-modulefilter-dropdown #qunit-modulefilter-actions > button {
+       float: right;
+       font: inherit;
+}
+
+#qunit-modulefilter-dropdown #qunit-modulefilter-actions > :last-child {
+       /* insert padding to align with checkbox margins */
+       padding-left: 3px;
+}
+
+#qunit-modulefilter-dropdown-list {
+       max-height: 200px;
+       overflow-y: auto;
+       margin: 0;
+       border-top: 2px groove threedhighlight;
+       padding: 0.4em 0 0;
+       font: smaller/1.5em sans-serif;
+}
+
+#qunit-modulefilter-dropdown-list li {
+       white-space: nowrap;
+       overflow: hidden;
+       text-overflow: ellipsis;
+}
+
+#qunit-modulefilter-dropdown-list .clickable {
+       display: block;
+       padding-left: 0.15em;
+}
+
+
 /** Tests: Pass/Fail */
 
 #qunit-tests {
 #qunit-tests li.running,
 #qunit-tests li.pass,
 #qunit-tests li.fail,
-#qunit-tests li.skipped {
+#qunit-tests li.skipped,
+#qunit-tests li.aborted {
        display: list-item;
 }
 
 }
 
 #qunit-tests.hidepass li.running,
-#qunit-tests.hidepass li.pass {
+#qunit-tests.hidepass li.pass:not(.todo) {
        visibility: hidden;
        position: absolute;
        width:   0;
 }
 
 #qunit-tests del {
-       background-color: #E0F2BE;
        color: #374E0C;
+       background-color: #E0F2BE;
        text-decoration: none;
 }
 
 #qunit-tests ins {
-       background-color: #FFCACA;
        color: #500;
+       background-color: #FFCACA;
        text-decoration: none;
 }
 
 
 #qunit-banner.qunit-fail                    { background-color: #EE5757; }
 
+
+/*** Aborted tests */
+#qunit-tests .aborted { color: #000; background-color: orange; }
 /*** Skipped tests */
 
 #qunit-tests .skipped {
        background-color: #EBECE9;
 }
 
+#qunit-tests .qunit-todo-label,
 #qunit-tests .qunit-skipped-label {
        background-color: #F4FF77;
        display: inline-block;
        margin: -0.4em 0.4em -0.4em 0;
 }
 
+#qunit-tests .qunit-todo-label {
+       background-color: #EEE;
+}
+
 /** Result */
 
 #qunit-testresult {
-       padding: 0.5em 1em 0.5em 1em;
-
        color: #2B81AF;
        background-color: #D2E0E6;
 
        border-bottom: 1px solid #FFF;
 }
+#qunit-testresult .clearfix {
+       height: 0;
+       clear: both;
+}
 #qunit-testresult .module-name {
        font-weight: 700;
 }
+#qunit-testresult-display {
+       padding: 0.5em 1em 0.5em 1em;
+       width: 85%;
+       float:left;
+}
+#qunit-testresult-controls {
+       padding: 0.5em 1em 0.5em 1em;
+  width: 10%;
+       float:left;
+}
 
 /** Fixture */
 
index 5df0822..bb8f31d 100644 (file)
 /*!
- * QUnit 1.23.1
+ * QUnit 2.4.0
  * https://qunitjs.com/
  *
  * Copyright jQuery Foundation and other contributors
  * Released under the MIT license
  * https://jquery.org/license
  *
- * Date: 2016-04-12T17:29Z
+ * Date: 2017-07-08T15:20Z
  */
+(function (global$1) {
+  'use strict';
 
-( function( global ) {
-
-var QUnit = {};
-
-var Date = global.Date;
-var now = Date.now || function() {
-       return new Date().getTime();
-};
-
-var setTimeout = global.setTimeout;
-var clearTimeout = global.clearTimeout;
-
-// Store a local window from the global to allow direct references.
-var window = global.window;
-
-var defined = {
-       document: window && window.document !== undefined,
-       setTimeout: setTimeout !== undefined,
-       sessionStorage: ( function() {
-               var x = "qunit-test-string";
-               try {
-                       sessionStorage.setItem( x, x );
-                       sessionStorage.removeItem( x );
-                       return true;
-               } catch ( e ) {
-                       return false;
-               }
-       }() )
-};
-
-var fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" );
-var globalStartCalled = false;
-var runStarted = false;
-
-var toString = Object.prototype.toString,
-       hasOwn = Object.prototype.hasOwnProperty;
-
-// Returns a new Array with the elements that are in a but not in b
-function diff( a, b ) {
-       var i, j,
-               result = a.slice();
-
-       for ( i = 0; i < result.length; i++ ) {
-               for ( j = 0; j < b.length; j++ ) {
-                       if ( result[ i ] === b[ j ] ) {
-                               result.splice( i, 1 );
-                               i--;
-                               break;
-                       }
-               }
-       }
-       return result;
-}
-
-// From jquery.js
-function inArray( elem, array ) {
-       if ( array.indexOf ) {
-               return array.indexOf( elem );
-       }
-
-       for ( var i = 0, length = array.length; i < length; i++ ) {
-               if ( array[ i ] === elem ) {
-                       return i;
-               }
-       }
-
-       return -1;
-}
-
-/**
- * Makes a clone of an object using only Array or Object as base,
- * and copies over the own enumerable properties.
- *
- * @param {Object} obj
- * @return {Object} New object with only the own properties (recursively).
- */
-function objectValues ( obj ) {
-       var key, val,
-               vals = QUnit.is( "array", obj ) ? [] : {};
-       for ( key in obj ) {
-               if ( hasOwn.call( obj, key ) ) {
-                       val = obj[ key ];
-                       vals[ key ] = val === Object( val ) ? objectValues( val ) : val;
-               }
-       }
-       return vals;
-}
-
-function extend( a, b, undefOnly ) {
-       for ( var prop in b ) {
-               if ( hasOwn.call( b, prop ) ) {
-
-                       // Avoid "Member not found" error in IE8 caused by messing with window.constructor
-                       // This block runs on every environment, so `global` is being used instead of `window`
-                       // to avoid errors on node.
-                       if ( prop !== "constructor" || a !== global ) {
-                               if ( b[ prop ] === undefined ) {
-                                       delete a[ prop ];
-                               } else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) {
-                                       a[ prop ] = b[ prop ];
-                               }
-                       }
-               }
-       }
-
-       return a;
-}
-
-function objectType( obj ) {
-       if ( typeof obj === "undefined" ) {
-               return "undefined";
-       }
-
-       // Consider: typeof null === object
-       if ( obj === null ) {
-               return "null";
-       }
-
-       var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ),
-               type = match && match[ 1 ];
-
-       switch ( type ) {
-               case "Number":
-                       if ( isNaN( obj ) ) {
-                               return "nan";
-                       }
-                       return "number";
-               case "String":
-               case "Boolean":
-               case "Array":
-               case "Set":
-               case "Map":
-               case "Date":
-               case "RegExp":
-               case "Function":
-               case "Symbol":
-                       return type.toLowerCase();
-       }
-       if ( typeof obj === "object" ) {
-               return "object";
-       }
-}
-
-// Safe object type checking
-function is( type, obj ) {
-       return QUnit.objectType( obj ) === type;
-}
-
-// Doesn't support IE6 to IE9, it will return undefined on these browsers
-// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
-function extractStacktrace( e, offset ) {
-       offset = offset === undefined ? 4 : offset;
-
-       var stack, include, i;
-
-       if ( e.stack ) {
-               stack = e.stack.split( "\n" );
-               if ( /^error$/i.test( stack[ 0 ] ) ) {
-                       stack.shift();
-               }
-               if ( fileName ) {
-                       include = [];
-                       for ( i = offset; i < stack.length; i++ ) {
-                               if ( stack[ i ].indexOf( fileName ) !== -1 ) {
-                                       break;
-                               }
-                               include.push( stack[ i ] );
-                       }
-                       if ( include.length ) {
-                               return include.join( "\n" );
-                       }
-               }
-               return stack[ offset ];
-
-       // Support: Safari <=6 only
-       } else if ( e.sourceURL ) {
-
-               // Exclude useless self-reference for generated Error objects
-               if ( /qunit.js$/.test( e.sourceURL ) ) {
-                       return;
-               }
-
-               // For actual exceptions, this is useful
-               return e.sourceURL + ":" + e.line;
-       }
-}
-
-function sourceFromStacktrace( offset ) {
-       var error = new Error();
-
-       // Support: Safari <=7 only, IE <=10 - 11 only
-       // Not all browsers generate the `stack` property for `new Error()`, see also #636
-       if ( !error.stack ) {
-               try {
-                       throw error;
-               } catch ( err ) {
-                       error = err;
-               }
-       }
-
-       return extractStacktrace( error, offset );
-}
-
-/**
- * Config object: Maintain internal state
- * Later exposed as QUnit.config
- * `config` initialized at top of scope
- */
-var config = {
+  global$1 = global$1 && 'default' in global$1 ? global$1['default'] : global$1;
 
-       // The queue of tests to run
-       queue: [],
+  var window = global$1.window;
+  var self$1 = global$1.self;
+  var console = global$1.console;
+  var setTimeout = global$1.setTimeout;
+  var clearTimeout = global$1.clearTimeout;
 
-       // Block until document ready
-       blocking: true,
+  var document = window && window.document;
+  var navigator = window && window.navigator;
 
-       // By default, run previously failed tests first
-       // very useful in combination with "Hide passed tests" checked
-       reorder: true,
+  var localSessionStorage = function () {
+       var x = "qunit-test-string";
+       try {
+               global$1.sessionStorage.setItem(x, x);
+               global$1.sessionStorage.removeItem(x);
+               return global$1.sessionStorage;
+       } catch (e) {
+               return undefined;
+       }
+  }();
 
-       // By default, modify document.title when suite is done
-       altertitle: true,
+  var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
+    return typeof obj;
+  } : function (obj) {
+    return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
+  };
 
-       // HTML Reporter: collapse every test except the first failing test
-       // If false, all failing tests will be expanded
-       collapse: true,
 
-       // By default, scroll to top of the page when suite is done
-       scrolltop: true,
 
-       // Depth up-to which object will be dumped
-       maxDepth: 5,
 
-       // When enabled, all tests must call expect()
-       requireExpects: false,
 
-       // Placeholder for user-configurable form-exposed URL parameters
-       urlConfig: [],
 
-       // Set of all modules.
-       modules: [],
 
-       // Stack of nested modules
-       moduleStack: [],
 
-       // The first unnamed module
-       currentModule: {
-               name: "",
-               tests: []
-       },
 
-       callbacks: {}
-};
-
-// Push a loose unnamed module to the modules collection
-config.modules.push( config.currentModule );
-
-var loggingCallbacks = {};
-
-// Register logging callbacks
-function registerLoggingCallbacks( obj ) {
-       var i, l, key,
-               callbackNames = [ "begin", "done", "log", "testStart", "testDone",
-                       "moduleStart", "moduleDone" ];
-
-       function registerLoggingCallback( key ) {
-               var loggingCallback = function( callback ) {
-                       if ( objectType( callback ) !== "function" ) {
-                               throw new Error(
-                                       "QUnit logging methods require a callback function as their first parameters."
-                               );
-                       }
-
-                       config.callbacks[ key ].push( callback );
-               };
-
-               // DEPRECATED: This will be removed on QUnit 2.0.0+
-               // Stores the registered functions allowing restoring
-               // at verifyLoggingCallbacks() if modified
-               loggingCallbacks[ key ] = loggingCallback;
-
-               return loggingCallback;
-       }
-
-       for ( i = 0, l = callbackNames.length; i < l; i++ ) {
-               key = callbackNames[ i ];
-
-               // Initialize key collection of logging callback
-               if ( objectType( config.callbacks[ key ] ) === "undefined" ) {
-                       config.callbacks[ key ] = [];
-               }
-
-               obj[ key ] = registerLoggingCallback( key );
-       }
-}
-
-function runLoggingCallbacks( key, args ) {
-       var i, l, callbacks;
-
-       callbacks = config.callbacks[ key ];
-       for ( i = 0, l = callbacks.length; i < l; i++ ) {
-               callbacks[ i ]( args );
-       }
-}
-
-// DEPRECATED: This will be removed on 2.0.0+
-// This function verifies if the loggingCallbacks were modified by the user
-// If so, it will restore it, assign the given callback and print a console warning
-function verifyLoggingCallbacks() {
-       var loggingCallback, userCallback;
-
-       for ( loggingCallback in loggingCallbacks ) {
-               if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) {
-
-                       userCallback = QUnit[ loggingCallback ];
-
-                       // Restore the callback function
-                       QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ];
-
-                       // Assign the deprecated given callback
-                       QUnit[ loggingCallback ]( userCallback );
-
-                       if ( global.console && global.console.warn ) {
-                               global.console.warn(
-                                       "QUnit." + loggingCallback + " was replaced with a new value.\n" +
-                                       "Please, check out the documentation on how to apply logging callbacks.\n" +
-                                       "Reference: https://api.qunitjs.com/category/callbacks/"
-                               );
-                       }
-               }
-       }
-}
-
-( function() {
-       if ( !defined.document ) {
-               return;
-       }
-
-       // `onErrorFnPrev` initialized at top of scope
-       // Preserve other handlers
-       var onErrorFnPrev = window.onerror;
-
-       // Cover uncaught exceptions
-       // Returning true will suppress the default browser handler,
-       // returning false will let it run.
-       window.onerror = function( error, filePath, linerNr ) {
-               var ret = false;
-               if ( onErrorFnPrev ) {
-                       ret = onErrorFnPrev( error, filePath, linerNr );
-               }
-
-               // Treat return value as window.onerror itself does,
-               // Only do our handling if not suppressed.
-               if ( ret !== true ) {
-                       if ( QUnit.config.current ) {
-                               if ( QUnit.config.current.ignoreGlobalErrors ) {
-                                       return true;
-                               }
-                               QUnit.pushFailure( error, filePath + ":" + linerNr );
-                       } else {
-                               QUnit.test( "global failure", extend( function() {
-                                       QUnit.pushFailure( error, filePath + ":" + linerNr );
-                               }, { validTest: true } ) );
-                       }
-                       return false;
-               }
-
-               return ret;
-       };
-}() );
-
-// Figure out if we're running the tests from a server or not
-QUnit.isLocal = !( defined.document && window.location.protocol !== "file:" );
-
-// Expose the current QUnit version
-QUnit.version = "1.23.1";
-
-extend( QUnit, {
-
-       // Call on start of module test to prepend name to all tests
-       module: function( name, testEnvironment, executeNow ) {
-               var module, moduleFns;
-               var currentModule = config.currentModule;
-
-               if ( arguments.length === 2 ) {
-                       if ( objectType( testEnvironment ) === "function" ) {
-                               executeNow = testEnvironment;
-                               testEnvironment = undefined;
-                       }
-               }
-
-               // DEPRECATED: handles setup/teardown functions,
-               // beforeEach and afterEach should be used instead
-               if ( testEnvironment && testEnvironment.setup ) {
-                       testEnvironment.beforeEach = testEnvironment.setup;
-                       delete testEnvironment.setup;
-               }
-               if ( testEnvironment && testEnvironment.teardown ) {
-                       testEnvironment.afterEach = testEnvironment.teardown;
-                       delete testEnvironment.teardown;
-               }
-
-               module = createModule();
-
-               moduleFns = {
-                       beforeEach: setHook( module, "beforeEach" ),
-                       afterEach: setHook( module, "afterEach" )
-               };
-
-               if ( objectType( executeNow ) === "function" ) {
-                       config.moduleStack.push( module );
-                       setCurrentModule( module );
-                       executeNow.call( module.testEnvironment, moduleFns );
-                       config.moduleStack.pop();
-                       module = module.parentModule || currentModule;
-               }
-
-               setCurrentModule( module );
-
-               function createModule() {
-                       var parentModule = config.moduleStack.length ?
-                               config.moduleStack.slice( -1 )[ 0 ] : null;
-                       var moduleName = parentModule !== null ?
-                               [ parentModule.name, name ].join( " > " ) : name;
-                       var module = {
-                               name: moduleName,
-                               parentModule: parentModule,
-                               tests: [],
-                               moduleId: generateHash( moduleName )
-                       };
-
-                       var env = {};
-                       if ( parentModule ) {
-                               extend( env, parentModule.testEnvironment );
-                               delete env.beforeEach;
-                               delete env.afterEach;
-                       }
-                       extend( env, testEnvironment );
-                       module.testEnvironment = env;
-
-                       config.modules.push( module );
-                       return module;
-               }
-
-               function setCurrentModule( module ) {
-                       config.currentModule = module;
-               }
-
-       },
-
-       // DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0.
-       asyncTest: asyncTest,
-
-       test: test,
-
-       skip: skip,
-
-       only: only,
-
-       // DEPRECATED: The functionality of QUnit.start() will be altered in QUnit 2.0.
-       // In QUnit 2.0, invoking it will ONLY affect the `QUnit.config.autostart` blocking behavior.
-       start: function( count ) {
-               var globalStartAlreadyCalled = globalStartCalled;
-
-               if ( !config.current ) {
-                       globalStartCalled = true;
-
-                       if ( runStarted ) {
-                               throw new Error( "Called start() outside of a test context while already started" );
-                       } else if ( globalStartAlreadyCalled || count > 1 ) {
-                               throw new Error( "Called start() outside of a test context too many times" );
-                       } else if ( config.autostart ) {
-                               throw new Error( "Called start() outside of a test context when " +
-                                       "QUnit.config.autostart was true" );
-                       } else if ( !config.pageLoaded ) {
-
-                               // The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it
-                               config.autostart = true;
-                               return;
-                       }
-               } else {
-
-                       // If a test is running, adjust its semaphore
-                       config.current.semaphore -= count || 1;
-
-                       // If semaphore is non-numeric, throw error
-                       if ( isNaN( config.current.semaphore ) ) {
-                               config.current.semaphore = 0;
-
-                               QUnit.pushFailure(
-                                       "Called start() with a non-numeric decrement.",
-                                       sourceFromStacktrace( 2 )
-                               );
-                               return;
-                       }
-
-                       // Don't start until equal number of stop-calls
-                       if ( config.current.semaphore > 0 ) {
-                               return;
-                       }
-
-                       // Throw an Error if start is called more often than stop
-                       if ( config.current.semaphore < 0 ) {
-                               config.current.semaphore = 0;
-
-                               QUnit.pushFailure(
-                                       "Called start() while already started (test's semaphore was 0 already)",
-                                       sourceFromStacktrace( 2 )
-                               );
-                               return;
-                       }
-               }
-
-               resumeProcessing();
-       },
-
-       // DEPRECATED: QUnit.stop() will be removed in QUnit 2.0.
-       stop: function( count ) {
-
-               // If there isn't a test running, don't allow QUnit.stop() to be called
-               if ( !config.current ) {
-                       throw new Error( "Called stop() outside of a test context" );
-               }
-
-               // If a test is running, adjust its semaphore
-               config.current.semaphore += count || 1;
-
-               pauseProcessing();
-       },
-
-       config: config,
-
-       is: is,
-
-       objectType: objectType,
-
-       extend: extend,
-
-       load: function() {
-               config.pageLoaded = true;
-
-               // Initialize the configuration options
-               extend( config, {
-                       stats: { all: 0, bad: 0 },
-                       moduleStats: { all: 0, bad: 0 },
-                       started: 0,
-                       updateRate: 1000,
-                       autostart: true,
-                       filter: ""
-               }, true );
-
-               config.blocking = false;
-
-               if ( config.autostart ) {
-                       resumeProcessing();
-               }
-       },
-
-       stack: function( offset ) {
-               offset = ( offset || 0 ) + 2;
-               return sourceFromStacktrace( offset );
-       }
-} );
-
-registerLoggingCallbacks( QUnit );
-
-function begin() {
-       var i, l,
-               modulesLog = [];
-
-       // If the test run hasn't officially begun yet
-       if ( !config.started ) {
-
-               // Record the time of the test run's beginning
-               config.started = now();
-
-               verifyLoggingCallbacks();
-
-               // Delete the loose unnamed module if unused.
-               if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) {
-                       config.modules.shift();
-               }
-
-               // Avoid unnecessary information by not logging modules' test environments
-               for ( i = 0, l = config.modules.length; i < l; i++ ) {
-                       modulesLog.push( {
-                               name: config.modules[ i ].name,
-                               tests: config.modules[ i ].tests
-                       } );
-               }
-
-               // The test run is officially beginning now
-               runLoggingCallbacks( "begin", {
-                       totalTests: Test.count,
-                       modules: modulesLog
-               } );
-       }
-
-       config.blocking = false;
-       process( true );
-}
-
-function process( last ) {
-       function next() {
-               process( last );
-       }
-       var start = now();
-       config.depth = ( config.depth || 0 ) + 1;
-
-       while ( config.queue.length && !config.blocking ) {
-               if ( !defined.setTimeout || config.updateRate <= 0 ||
-                               ( ( now() - start ) < config.updateRate ) ) {
-                       if ( config.current ) {
-
-                               // Reset async tracking for each phase of the Test lifecycle
-                               config.current.usedAsync = false;
-                       }
-                       config.queue.shift()();
-               } else {
-                       setTimeout( next, 13 );
-                       break;
-               }
-       }
-       config.depth--;
-       if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
-               done();
-       }
-}
-
-function pauseProcessing() {
-       config.blocking = true;
-
-       if ( config.testTimeout && defined.setTimeout ) {
-               clearTimeout( config.timeout );
-               config.timeout = setTimeout( function() {
-                       if ( config.current ) {
-                               config.current.semaphore = 0;
-                               QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) );
-                       } else {
-                               throw new Error( "Test timed out" );
-                       }
-                       resumeProcessing();
-               }, config.testTimeout );
-       }
-}
-
-function resumeProcessing() {
-       runStarted = true;
-
-       // A slight delay to allow this iteration of the event loop to finish (more assertions, etc.)
-       if ( defined.setTimeout ) {
-               setTimeout( function() {
-                       if ( config.current && config.current.semaphore > 0 ) {
-                               return;
-                       }
-                       if ( config.timeout ) {
-                               clearTimeout( config.timeout );
-                       }
-
-                       begin();
-               }, 13 );
-       } else {
-               begin();
-       }
-}
-
-function done() {
-       var runtime, passed;
-
-       config.autorun = true;
-
-       // Log the last module results
-       if ( config.previousModule ) {
-               runLoggingCallbacks( "moduleDone", {
-                       name: config.previousModule.name,
-                       tests: config.previousModule.tests,
-                       failed: config.moduleStats.bad,
-                       passed: config.moduleStats.all - config.moduleStats.bad,
-                       total: config.moduleStats.all,
-                       runtime: now() - config.moduleStats.started
-               } );
-       }
-       delete config.previousModule;
-
-       runtime = now() - config.started;
-       passed = config.stats.all - config.stats.bad;
-
-       runLoggingCallbacks( "done", {
-               failed: config.stats.bad,
-               passed: passed,
-               total: config.stats.all,
-               runtime: runtime
-       } );
-}
-
-function setHook( module, hookName ) {
-       if ( module.testEnvironment === undefined ) {
-               module.testEnvironment = {};
-       }
-
-       return function( callback ) {
-               module.testEnvironment[ hookName ] = callback;
-       };
-}
-
-var focused = false;
-var priorityCount = 0;
-var unitSampler;
-
-function Test( settings ) {
-       var i, l;
-
-       ++Test.count;
-
-       extend( this, settings );
-       this.assertions = [];
-       this.semaphore = 0;
-       this.usedAsync = false;
-       this.module = config.currentModule;
-       this.stack = sourceFromStacktrace( 3 );
-
-       // Register unique strings
-       for ( i = 0, l = this.module.tests; i < l.length; i++ ) {
-               if ( this.module.tests[ i ].name === this.testName ) {
-                       this.testName += " ";
-               }
-       }
-
-       this.testId = generateHash( this.module.name, this.testName );
-
-       this.module.tests.push( {
-               name: this.testName,
-               testId: this.testId
-       } );
-
-       if ( settings.skip ) {
-
-               // Skipped tests will fully ignore any sent callback
-               this.callback = function() {};
-               this.async = false;
-               this.expected = 0;
-       } else {
-               this.assert = new Assert( this );
-       }
-}
-
-Test.count = 0;
-
-Test.prototype = {
-       before: function() {
-               if (
-
-                       // Emit moduleStart when we're switching from one module to another
-                       this.module !== config.previousModule ||
-
-                               // They could be equal (both undefined) but if the previousModule property doesn't
-                               // yet exist it means this is the first test in a suite that isn't wrapped in a
-                               // module, in which case we'll just emit a moduleStart event for 'undefined'.
-                               // Without this, reporters can get testStart before moduleStart  which is a problem.
-                               !hasOwn.call( config, "previousModule" )
-               ) {
-                       if ( hasOwn.call( config, "previousModule" ) ) {
-                               runLoggingCallbacks( "moduleDone", {
-                                       name: config.previousModule.name,
-                                       tests: config.previousModule.tests,
-                                       failed: config.moduleStats.bad,
-                                       passed: config.moduleStats.all - config.moduleStats.bad,
-                                       total: config.moduleStats.all,
-                                       runtime: now() - config.moduleStats.started
-                               } );
-                       }
-                       config.previousModule = this.module;
-                       config.moduleStats = { all: 0, bad: 0, started: now() };
-                       runLoggingCallbacks( "moduleStart", {
-                               name: this.module.name,
-                               tests: this.module.tests
-                       } );
-               }
-
-               config.current = this;
-
-               if ( this.module.testEnvironment ) {
-                       delete this.module.testEnvironment.beforeEach;
-                       delete this.module.testEnvironment.afterEach;
-               }
-               this.testEnvironment = extend( {}, this.module.testEnvironment );
-
-               this.started = now();
-               runLoggingCallbacks( "testStart", {
-                       name: this.testName,
-                       module: this.module.name,
-                       testId: this.testId
-               } );
-
-               if ( !config.pollution ) {
-                       saveGlobal();
-               }
-       },
-
-       run: function() {
-               var promise;
-
-               config.current = this;
-
-               if ( this.async ) {
-                       QUnit.stop();
-               }
-
-               this.callbackStarted = now();
-
-               if ( config.notrycatch ) {
-                       runTest( this );
-                       return;
-               }
-
-               try {
-                       runTest( this );
-               } catch ( e ) {
-                       this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " +
-                               this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
-
-                       // Else next test will carry the responsibility
-                       saveGlobal();
-
-                       // Restart the tests if they're blocking
-                       if ( config.blocking ) {
-                               QUnit.start();
-                       }
-               }
-
-               function runTest( test ) {
-                       promise = test.callback.call( test.testEnvironment, test.assert );
-                       test.resolvePromise( promise );
-               }
-       },
-
-       after: function() {
-               checkPollution();
-       },
-
-       queueHook: function( hook, hookName ) {
-               var promise,
-                       test = this;
-               return function runHook() {
-                       config.current = test;
-                       if ( config.notrycatch ) {
-                               callHook();
-                               return;
-                       }
-                       try {
-                               callHook();
-                       } catch ( error ) {
-                               test.pushFailure( hookName + " failed on " + test.testName + ": " +
-                               ( error.message || error ), extractStacktrace( error, 0 ) );
-                       }
-
-                       function callHook() {
-                               promise = hook.call( test.testEnvironment, test.assert );
-                               test.resolvePromise( promise, hookName );
-                       }
-               };
-       },
-
-       // Currently only used for module level hooks, can be used to add global level ones
-       hooks: function( handler ) {
-               var hooks = [];
-
-               function processHooks( test, module ) {
-                       if ( module.parentModule ) {
-                               processHooks( test, module.parentModule );
-                       }
-                       if ( module.testEnvironment &&
-                               QUnit.objectType( module.testEnvironment[ handler ] ) === "function" ) {
-                               hooks.push( test.queueHook( module.testEnvironment[ handler ], handler ) );
-                       }
-               }
-
-               // Hooks are ignored on skipped tests
-               if ( !this.skip ) {
-                       processHooks( this, this.module );
-               }
-               return hooks;
-       },
-
-       finish: function() {
-               config.current = this;
-               if ( config.requireExpects && this.expected === null ) {
-                       this.pushFailure( "Expected number of assertions to be defined, but expect() was " +
-                               "not called.", this.stack );
-               } else if ( this.expected !== null && this.expected !== this.assertions.length ) {
-                       this.pushFailure( "Expected " + this.expected + " assertions, but " +
-                               this.assertions.length + " were run", this.stack );
-               } else if ( this.expected === null && !this.assertions.length ) {
-                       this.pushFailure( "Expected at least one assertion, but none were run - call " +
-                               "expect(0) to accept zero assertions.", this.stack );
-               }
-
-               var i,
-                       bad = 0;
-
-               this.runtime = now() - this.started;
-               config.stats.all += this.assertions.length;
-               config.moduleStats.all += this.assertions.length;
-
-               for ( i = 0; i < this.assertions.length; i++ ) {
-                       if ( !this.assertions[ i ].result ) {
-                               bad++;
-                               config.stats.bad++;
-                               config.moduleStats.bad++;
-                       }
-               }
-
-               runLoggingCallbacks( "testDone", {
-                       name: this.testName,
-                       module: this.module.name,
-                       skipped: !!this.skip,
-                       failed: bad,
-                       passed: this.assertions.length - bad,
-                       total: this.assertions.length,
-                       runtime: this.runtime,
-
-                       // HTML Reporter use
-                       assertions: this.assertions,
-                       testId: this.testId,
-
-                       // Source of Test
-                       source: this.stack,
-
-                       // DEPRECATED: this property will be removed in 2.0.0, use runtime instead
-                       duration: this.runtime
-               } );
-
-               // QUnit.reset() is deprecated and will be replaced for a new
-               // fixture reset function on QUnit 2.0/2.1.
-               // It's still called here for backwards compatibility handling
-               QUnit.reset();
-
-               config.current = undefined;
-       },
-
-       queue: function() {
-               var priority,
-                       test = this;
-
-               if ( !this.valid() ) {
-                       return;
-               }
-
-               function run() {
-
-                       // Each of these can by async
-                       synchronize( [
-                               function() {
-                                       test.before();
-                               },
-
-                               test.hooks( "beforeEach" ),
-                               function() {
-                                       test.run();
-                               },
-
-                               test.hooks( "afterEach" ).reverse(),
-
-                               function() {
-                                       test.after();
-                               },
-                               function() {
-                                       test.finish();
-                               }
-                       ] );
-               }
-
-               // Prioritize previously failed tests, detected from sessionStorage
-               priority = QUnit.config.reorder && defined.sessionStorage &&
-                               +sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName );
-
-               return synchronize( run, priority, config.seed );
-       },
-
-       pushResult: function( resultInfo ) {
-
-               // Destructure of resultInfo = { result, actual, expected, message, negative }
-               var source,
-                       details = {
-                               module: this.module.name,
-                               name: this.testName,
-                               result: resultInfo.result,
-                               message: resultInfo.message,
-                               actual: resultInfo.actual,
-                               expected: resultInfo.expected,
-                               testId: this.testId,
-                               negative: resultInfo.negative || false,
-                               runtime: now() - this.started
-                       };
-
-               if ( !resultInfo.result ) {
-                       source = sourceFromStacktrace();
-
-                       if ( source ) {
-                               details.source = source;
-                       }
-               }
-
-               runLoggingCallbacks( "log", details );
-
-               this.assertions.push( {
-                       result: !!resultInfo.result,
-                       message: resultInfo.message
-               } );
-       },
-
-       pushFailure: function( message, source, actual ) {
-               if ( !( this instanceof Test ) ) {
-                       throw new Error( "pushFailure() assertion outside test context, was " +
-                               sourceFromStacktrace( 2 ) );
-               }
-
-               var details = {
-                               module: this.module.name,
-                               name: this.testName,
-                               result: false,
-                               message: message || "error",
-                               actual: actual || null,
-                               testId: this.testId,
-                               runtime: now() - this.started
-                       };
-
-               if ( source ) {
-                       details.source = source;
-               }
-
-               runLoggingCallbacks( "log", details );
-
-               this.assertions.push( {
-                       result: false,
-                       message: message
-               } );
-       },
-
-       resolvePromise: function( promise, phase ) {
-               var then, message,
-                       test = this;
-               if ( promise != null ) {
-                       then = promise.then;
-                       if ( QUnit.objectType( then ) === "function" ) {
-                               QUnit.stop();
-                               then.call(
-                                       promise,
-                                       function() { QUnit.start(); },
-                                       function( error ) {
-                                               message = "Promise rejected " +
-                                                       ( !phase ? "during" : phase.replace( /Each$/, "" ) ) +
-                                                       " " + test.testName + ": " + ( error.message || error );
-                                               test.pushFailure( message, extractStacktrace( error, 0 ) );
-
-                                               // Else next test will carry the responsibility
-                                               saveGlobal();
-
-                                               // Unblock
-                                               QUnit.start();
-                                       }
-                               );
-                       }
-               }
-       },
-
-       valid: function() {
-               var filter = config.filter,
-                       regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec( filter ),
-                       module = config.module && config.module.toLowerCase(),
-                       fullName = ( this.module.name + ": " + this.testName );
-
-               function moduleChainNameMatch( testModule ) {
-                       var testModuleName = testModule.name ? testModule.name.toLowerCase() : null;
-                       if ( testModuleName === module ) {
-                               return true;
-                       } else if ( testModule.parentModule ) {
-                               return moduleChainNameMatch( testModule.parentModule );
-                       } else {
-                               return false;
-                       }
-               }
-
-               function moduleChainIdMatch( testModule ) {
-                       return inArray( testModule.moduleId, config.moduleId ) > -1 ||
-                               testModule.parentModule && moduleChainIdMatch( testModule.parentModule );
-               }
-
-               // Internally-generated tests are always valid
-               if ( this.callback && this.callback.validTest ) {
-                       return true;
-               }
-
-               if ( config.moduleId && config.moduleId.length > 0 &&
-                       !moduleChainIdMatch( this.module ) ) {
-
-                       return false;
-               }
-
-               if ( config.testId && config.testId.length > 0 &&
-                       inArray( this.testId, config.testId ) < 0 ) {
-
-                       return false;
-               }
-
-               if ( module && !moduleChainNameMatch( this.module ) ) {
-                       return false;
-               }
-
-               if ( !filter ) {
-                       return true;
-               }
-
-               return regexFilter ?
-                       this.regexFilter( !!regexFilter[ 1 ], regexFilter[ 2 ], regexFilter[ 3 ], fullName ) :
-                       this.stringFilter( filter, fullName );
-       },
-
-       regexFilter: function( exclude, pattern, flags, fullName ) {
-               var regex = new RegExp( pattern, flags );
-               var match = regex.test( fullName );
-
-               return match !== exclude;
-       },
-
-       stringFilter: function( filter, fullName ) {
-               filter = filter.toLowerCase();
-               fullName = fullName.toLowerCase();
-
-               var include = filter.charAt( 0 ) !== "!";
-               if ( !include ) {
-                       filter = filter.slice( 1 );
-               }
-
-               // If the filter matches, we need to honour include
-               if ( fullName.indexOf( filter ) !== -1 ) {
-                       return include;
-               }
-
-               // Otherwise, do the opposite
-               return !include;
-       }
-};
-
-// Resets the test setup. Useful for tests that modify the DOM.
-/*
-DEPRECATED: Use multiple tests instead of resetting inside a test.
-Use testStart or testDone for custom cleanup.
-This method will throw an error in 2.0, and will be removed in 2.1
-*/
-QUnit.reset = function() {
-
-       // Return on non-browser environments
-       // This is necessary to not break on node tests
-       if ( !defined.document ) {
-               return;
-       }
-
-       var fixture = defined.document && document.getElementById &&
-                       document.getElementById( "qunit-fixture" );
-
-       if ( fixture ) {
-               fixture.innerHTML = config.fixture;
-       }
-};
-
-QUnit.pushFailure = function() {
-       if ( !QUnit.config.current ) {
-               throw new Error( "pushFailure() assertion outside test context, in " +
-                       sourceFromStacktrace( 2 ) );
-       }
-
-       // Gets current test obj
-       var currentTest = QUnit.config.current;
-
-       return currentTest.pushFailure.apply( currentTest, arguments );
-};
-
-// Based on Java's String.hashCode, a simple but not
-// rigorously collision resistant hashing function
-function generateHash( module, testName ) {
-       var hex,
-               i = 0,
-               hash = 0,
-               str = module + "\x1C" + testName,
-               len = str.length;
-
-       for ( ; i < len; i++ ) {
-               hash  = ( ( hash << 5 ) - hash ) + str.charCodeAt( i );
-               hash |= 0;
-       }
-
-       // Convert the possibly negative integer hash code into an 8 character hex string, which isn't
-       // strictly necessary but increases user understanding that the id is a SHA-like hash
-       hex = ( 0x100000000 + hash ).toString( 16 );
-       if ( hex.length < 8 ) {
-               hex = "0000000" + hex;
-       }
-
-       return hex.slice( -8 );
-}
-
-function synchronize( callback, priority, seed ) {
-       var last = !priority,
-               index;
-
-       if ( QUnit.objectType( callback ) === "array" ) {
-               while ( callback.length ) {
-                       synchronize( callback.shift() );
-               }
-               return;
-       }
-
-       if ( priority ) {
-               config.queue.splice( priorityCount++, 0, callback );
-       } else if ( seed ) {
-               if ( !unitSampler ) {
-                       unitSampler = unitSamplerGenerator( seed );
-               }
-
-               // Insert into a random position after all priority items
-               index = Math.floor( unitSampler() * ( config.queue.length - priorityCount + 1 ) );
-               config.queue.splice( priorityCount + index, 0, callback );
-       } else {
-               config.queue.push( callback );
-       }
-
-       if ( config.autorun && !config.blocking ) {
-               process( last );
-       }
-}
-
-function unitSamplerGenerator( seed ) {
-
-       // 32-bit xorshift, requires only a nonzero seed
-       // http://excamera.com/sphinx/article-xorshift.html
-       var sample = parseInt( generateHash( seed ), 16 ) || -1;
-       return function() {
-               sample ^= sample << 13;
-               sample ^= sample >>> 17;
-               sample ^= sample << 5;
-
-               // ECMAScript has no unsigned number type
-               if ( sample < 0 ) {
-                       sample += 0x100000000;
-               }
-
-               return sample / 0x100000000;
-       };
-}
-
-function saveGlobal() {
-       config.pollution = [];
-
-       if ( config.noglobals ) {
-               for ( var key in global ) {
-                       if ( hasOwn.call( global, key ) ) {
-
-                               // In Opera sometimes DOM element ids show up here, ignore them
-                               if ( /^qunit-test-output/.test( key ) ) {
-                                       continue;
-                               }
-                               config.pollution.push( key );
-                       }
-               }
-       }
-}
-
-function checkPollution() {
-       var newGlobals,
-               deletedGlobals,
-               old = config.pollution;
-
-       saveGlobal();
-
-       newGlobals = diff( config.pollution, old );
-       if ( newGlobals.length > 0 ) {
-               QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) );
-       }
-
-       deletedGlobals = diff( old, config.pollution );
-       if ( deletedGlobals.length > 0 ) {
-               QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) );
-       }
-}
-
-// Will be exposed as QUnit.asyncTest
-function asyncTest( testName, expected, callback ) {
-       if ( arguments.length === 2 ) {
-               callback = expected;
-               expected = null;
-       }
-
-       QUnit.test( testName, expected, callback, true );
-}
-
-// Will be exposed as QUnit.test
-function test( testName, expected, callback, async ) {
-       if ( focused )  { return; }
-
-       var newTest;
-
-       if ( arguments.length === 2 ) {
-               callback = expected;
-               expected = null;
-       }
-
-       newTest = new Test( {
-               testName: testName,
-               expected: expected,
-               async: async,
-               callback: callback
-       } );
-
-       newTest.queue();
-}
-
-// Will be exposed as QUnit.skip
-function skip( testName ) {
-       if ( focused )  { return; }
-
-       var test = new Test( {
-               testName: testName,
-               skip: true
-       } );
-
-       test.queue();
-}
-
-// Will be exposed as QUnit.only
-function only( testName, expected, callback, async ) {
-       var newTest;
-
-       if ( focused )  { return; }
-
-       QUnit.config.queue.length = 0;
-       focused = true;
-
-       if ( arguments.length === 2 ) {
-               callback = expected;
-               expected = null;
-       }
-
-       newTest = new Test( {
-               testName: testName,
-               expected: expected,
-               async: async,
-               callback: callback
-       } );
-
-       newTest.queue();
-}
-
-function Assert( testContext ) {
-       this.test = testContext;
-}
-
-// Assert helpers
-QUnit.assert = Assert.prototype = {
-
-       // Specify the number of expected assertions to guarantee that failed test
-       // (no assertions are run at all) don't slip through.
-       expect: function( asserts ) {
-               if ( arguments.length === 1 ) {
-                       this.test.expected = asserts;
-               } else {
-                       return this.test.expected;
-               }
-       },
-
-       // Increment this Test's semaphore counter, then return a function that
-       // decrements that counter a maximum of once.
-       async: function( count ) {
-               var test = this.test,
-                       popped = false,
-                       acceptCallCount = count;
-
-               if ( typeof acceptCallCount === "undefined" ) {
-                       acceptCallCount = 1;
-               }
-
-               test.semaphore += 1;
-               test.usedAsync = true;
-               pauseProcessing();
-
-               return function done() {
-
-                       if ( popped ) {
-                               test.pushFailure( "Too many calls to the `assert.async` callback",
-                                       sourceFromStacktrace( 2 ) );
-                               return;
-                       }
-                       acceptCallCount -= 1;
-                       if ( acceptCallCount > 0 ) {
-                               return;
-                       }
-
-                       test.semaphore -= 1;
-                       popped = true;
-                       resumeProcessing();
-               };
-       },
-
-       // Exports test.push() to the user API
-       // Alias of pushResult.
-       push: function( result, actual, expected, message, negative ) {
-               var currentAssert = this instanceof Assert ? this : QUnit.config.current.assert;
-               return currentAssert.pushResult( {
-                       result: result,
-                       actual: actual,
-                       expected: expected,
-                       message: message,
-                       negative: negative
-               } );
-       },
-
-       pushResult: function( resultInfo ) {
-
-               // Destructure of resultInfo = { result, actual, expected, message, negative }
-               var assert = this,
-                       currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current;
-
-               // Backwards compatibility fix.
-               // Allows the direct use of global exported assertions and QUnit.assert.*
-               // Although, it's use is not recommended as it can leak assertions
-               // to other tests from async tests, because we only get a reference to the current test,
-               // not exactly the test where assertion were intended to be called.
-               if ( !currentTest ) {
-                       throw new Error( "assertion outside test context, in " + sourceFromStacktrace( 2 ) );
-               }
-
-               if ( currentTest.usedAsync === true && currentTest.semaphore === 0 ) {
-                       currentTest.pushFailure( "Assertion after the final `assert.async` was resolved",
-                               sourceFromStacktrace( 2 ) );
-
-                       // Allow this assertion to continue running anyway...
-               }
-
-               if ( !( assert instanceof Assert ) ) {
-                       assert = currentTest.assert;
-               }
-
-               return assert.test.pushResult( resultInfo );
-       },
-
-       ok: function( result, message ) {
-               message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " +
-                       QUnit.dump.parse( result ) );
-               this.pushResult( {
-                       result: !!result,
-                       actual: result,
-                       expected: true,
-                       message: message
-               } );
-       },
-
-       notOk: function( result, message ) {
-               message = message || ( !result ? "okay" : "failed, expected argument to be falsy, was: " +
-                       QUnit.dump.parse( result ) );
-               this.pushResult( {
-                       result: !result,
-                       actual: result,
-                       expected: false,
-                       message: message
-               } );
-       },
-
-       equal: function( actual, expected, message ) {
-               /*jshint eqeqeq:false */
-               this.pushResult( {
-                       result: expected == actual,
-                       actual: actual,
-                       expected: expected,
-                       message: message
-               } );
-       },
-
-       notEqual: function( actual, expected, message ) {
-               /*jshint eqeqeq:false */
-               this.pushResult( {
-                       result: expected != actual,
-                       actual: actual,
-                       expected: expected,
-                       message: message,
-                       negative: true
-               } );
-       },
-
-       propEqual: function( actual, expected, message ) {
-               actual = objectValues( actual );
-               expected = objectValues( expected );
-               this.pushResult( {
-                       result: QUnit.equiv( actual, expected ),
-                       actual: actual,
-                       expected: expected,
-                       message: message
-               } );
-       },
-
-       notPropEqual: function( actual, expected, message ) {
-               actual = objectValues( actual );
-               expected = objectValues( expected );
-               this.pushResult( {
-                       result: !QUnit.equiv( actual, expected ),
-                       actual: actual,
-                       expected: expected,
-                       message: message,
-                       negative: true
-               } );
-       },
-
-       deepEqual: function( actual, expected, message ) {
-               this.pushResult( {
-                       result: QUnit.equiv( actual, expected ),
-                       actual: actual,
-                       expected: expected,
-                       message: message
-               } );
-       },
-
-       notDeepEqual: function( actual, expected, message ) {
-               this.pushResult( {
-                       result: !QUnit.equiv( actual, expected ),
-                       actual: actual,
-                       expected: expected,
-                       message: message,
-                       negative: true
-               } );
-       },
-
-       strictEqual: function( actual, expected, message ) {
-               this.pushResult( {
-                       result: expected === actual,
-                       actual: actual,
-                       expected: expected,
-                       message: message
-               } );
-       },
-
-       notStrictEqual: function( actual, expected, message ) {
-               this.pushResult( {
-                       result: expected !== actual,
-                       actual: actual,
-                       expected: expected,
-                       message: message,
-                       negative: true
-               } );
-       },
-
-       "throws": function( block, expected, message ) {
-               var actual, expectedType,
-                       expectedOutput = expected,
-                       ok = false,
-                       currentTest = ( this instanceof Assert && this.test ) || QUnit.config.current;
-
-               // 'expected' is optional unless doing string comparison
-               if ( message == null && typeof expected === "string" ) {
-                       message = expected;
-                       expected = null;
-               }
-
-               currentTest.ignoreGlobalErrors = true;
-               try {
-                       block.call( currentTest.testEnvironment );
-               } catch ( e ) {
-                       actual = e;
-               }
-               currentTest.ignoreGlobalErrors = false;
-
-               if ( actual ) {
-                       expectedType = QUnit.objectType( expected );
-
-                       // We don't want to validate thrown error
-                       if ( !expected ) {
-                               ok = true;
-                               expectedOutput = null;
-
-                       // Expected is a regexp
-                       } else if ( expectedType === "regexp" ) {
-                               ok = expected.test( errorString( actual ) );
-
-                       // Expected is a string
-                       } else if ( expectedType === "string" ) {
-                               ok = expected === errorString( actual );
-
-                       // Expected is a constructor, maybe an Error constructor
-                       } else if ( expectedType === "function" && actual instanceof expected ) {
-                               ok = true;
-
-                       // Expected is an Error object
-                       } else if ( expectedType === "object" ) {
-                               ok = actual instanceof expected.constructor &&
-                                       actual.name === expected.name &&
-                                       actual.message === expected.message;
-
-                       // Expected is a validation function which returns true if validation passed
-                       } else if ( expectedType === "function" && expected.call( {}, actual ) === true ) {
-                               expectedOutput = null;
-                               ok = true;
-                       }
-               }
-
-               currentTest.assert.pushResult( {
-                       result: ok,
-                       actual: actual,
-                       expected: expectedOutput,
-                       message: message
-               } );
-       }
-};
-
-// Provide an alternative to assert.throws(), for environments that consider throws a reserved word
-// Known to us are: Closure Compiler, Narwhal
-( function() {
-       /*jshint sub:true */
-       Assert.prototype.raises = Assert.prototype [ "throws" ]; //jscs:ignore requireDotNotation
-}() );
-
-function errorString( error ) {
-       var name, message,
-               resultErrorString = error.toString();
-       if ( resultErrorString.substring( 0, 7 ) === "[object" ) {
-               name = error.name ? error.name.toString() : "Error";
-               message = error.message ? error.message.toString() : "";
-               if ( name && message ) {
-                       return name + ": " + message;
-               } else if ( name ) {
-                       return name;
-               } else if ( message ) {
-                       return message;
-               } else {
-                       return "Error";
-               }
-       } else {
-               return resultErrorString;
-       }
-}
-
-// Test for equality any JavaScript type.
-// Author: Philippe Rathé <prathe@gmail.com>
-QUnit.equiv = ( function() {
-
-       // Stack to decide between skip/abort functions
-       var callers = [];
-
-       // Stack to avoiding loops from circular referencing
-       var parents = [];
-       var parentsB = [];
-
-       var getProto = Object.getPrototypeOf || function( obj ) {
-
-               /*jshint proto: true */
-               return obj.__proto__;
-       };
-
-       function useStrictEquality( b, a ) {
-
-               // To catch short annotation VS 'new' annotation of a declaration. e.g.:
-               // `var i = 1;`
-               // `var j = new Number(1);`
-               if ( typeof a === "object" ) {
-                       a = a.valueOf();
-               }
-               if ( typeof b === "object" ) {
-                       b = b.valueOf();
-               }
-
-               return a === b;
-       }
-
-       function compareConstructors( a, b ) {
-               var protoA = getProto( a );
-               var protoB = getProto( b );
-
-               // Comparing constructors is more strict than using `instanceof`
-               if ( a.constructor === b.constructor ) {
-                       return true;
-               }
-
-               // Ref #851
-               // If the obj prototype descends from a null constructor, treat it
-               // as a null prototype.
-               if ( protoA && protoA.constructor === null ) {
-                       protoA = null;
-               }
-               if ( protoB && protoB.constructor === null ) {
-                       protoB = null;
-               }
-
-               // Allow objects with no prototype to be equivalent to
-               // objects with Object as their constructor.
-               if ( ( protoA === null && protoB === Object.prototype ) ||
-                               ( protoB === null && protoA === Object.prototype ) ) {
-                       return true;
-               }
-
-               return false;
-       }
-
-       function getRegExpFlags( regexp ) {
-               return "flags" in regexp ? regexp.flags : regexp.toString().match( /[gimuy]*$/ )[ 0 ];
-       }
-
-       var callbacks = {
-               "string": useStrictEquality,
-               "boolean": useStrictEquality,
-               "number": useStrictEquality,
-               "null": useStrictEquality,
-               "undefined": useStrictEquality,
-               "symbol": useStrictEquality,
-               "date": useStrictEquality,
-
-               "nan": function() {
-                       return true;
-               },
-
-               "regexp": function( b, a ) {
-                       return a.source === b.source &&
-
-                               // Include flags in the comparison
-                               getRegExpFlags( a ) === getRegExpFlags( b );
-               },
-
-               // - skip when the property is a method of an instance (OOP)
-               // - abort otherwise,
-               // initial === would have catch identical references anyway
-               "function": function() {
-                       var caller = callers[ callers.length - 1 ];
-                       return caller !== Object && typeof caller !== "undefined";
-               },
-
-               "array": function( b, a ) {
-                       var i, j, len, loop, aCircular, bCircular;
-
-                       len = a.length;
-                       if ( len !== b.length ) {
-
-                               // Safe and faster
-                               return false;
-                       }
-
-                       // Track reference to avoid circular references
-                       parents.push( a );
-                       parentsB.push( b );
-                       for ( i = 0; i < len; i++ ) {
-                               loop = false;
-                               for ( j = 0; j < parents.length; j++ ) {
-                                       aCircular = parents[ j ] === a[ i ];
-                                       bCircular = parentsB[ j ] === b[ i ];
-                                       if ( aCircular || bCircular ) {
-                                               if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
-                                                       loop = true;
-                                               } else {
-                                                       parents.pop();
-                                                       parentsB.pop();
-                                                       return false;
-                                               }
-                                       }
-                               }
-                               if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
-                                       parents.pop();
-                                       parentsB.pop();
-                                       return false;
-                               }
-                       }
-                       parents.pop();
-                       parentsB.pop();
-                       return true;
-               },
-
-               "set": function( b, a ) {
-                       var innerEq,
-                               outerEq = true;
-
-                       if ( a.size !== b.size ) {
-                               return false;
-                       }
-
-                       a.forEach( function( aVal ) {
-                               innerEq = false;
-
-                               b.forEach( function( bVal ) {
-                                       if ( innerEquiv( bVal, aVal ) ) {
-                                               innerEq = true;
-                                       }
-                               } );
-
-                               if ( !innerEq ) {
-                                       outerEq = false;
-                               }
-                       } );
-
-                       return outerEq;
-               },
-
-               "map": function( b, a ) {
-                       var innerEq,
-                               outerEq = true;
-
-                       if ( a.size !== b.size ) {
-                               return false;
-                       }
-
-                       a.forEach( function( aVal, aKey ) {
-                               innerEq = false;
-
-                               b.forEach( function( bVal, bKey ) {
-                                       if ( innerEquiv( [ bVal, bKey ], [ aVal, aKey ] ) ) {
-                                               innerEq = true;
-                                       }
-                               } );
-
-                               if ( !innerEq ) {
-                                       outerEq = false;
-                               }
-                       } );
-
-                       return outerEq;
-               },
-
-               "object": function( b, a ) {
-                       var i, j, loop, aCircular, bCircular;
-
-                       // Default to true
-                       var eq = true;
-                       var aProperties = [];
-                       var bProperties = [];
-
-                       if ( compareConstructors( a, b ) === false ) {
-                               return false;
-                       }
-
-                       // Stack constructor before traversing properties
-                       callers.push( a.constructor );
-
-                       // Track reference to avoid circular references
-                       parents.push( a );
-                       parentsB.push( b );
-
-                       // Be strict: don't ensure hasOwnProperty and go deep
-                       for ( i in a ) {
-                               loop = false;
-                               for ( j = 0; j < parents.length; j++ ) {
-                                       aCircular = parents[ j ] === a[ i ];
-                                       bCircular = parentsB[ j ] === b[ i ];
-                                       if ( aCircular || bCircular ) {
-                                               if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
-                                                       loop = true;
-                                               } else {
-                                                       eq = false;
-                                                       break;
-                                               }
-                                       }
-                               }
-                               aProperties.push( i );
-                               if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
-                                       eq = false;
-                                       break;
-                               }
-                       }
-
-                       parents.pop();
-                       parentsB.pop();
-
-                       // Unstack, we are done
-                       callers.pop();
-
-                       for ( i in b ) {
-
-                               // Collect b's properties
-                               bProperties.push( i );
-                       }
-
-                       // Ensures identical properties name
-                       return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
-               }
-       };
-
-       function typeEquiv( a, b ) {
-               var type = QUnit.objectType( a );
-               return QUnit.objectType( b ) === type && callbacks[ type ]( b, a );
-       }
-
-       // The real equiv function
-       function innerEquiv( a, b ) {
-
-               // We're done when there's nothing more to compare
-               if ( arguments.length < 2 ) {
-                       return true;
-               }
-
-               // Require type-specific equality
-               return ( a === b || typeEquiv( a, b ) ) &&
-
-                       // ...across all consecutive argument pairs
-                       ( arguments.length === 2 || innerEquiv.apply( this, [].slice.call( arguments, 1 ) ) );
-       }
-
-       return innerEquiv;
-}() );
-
-// Based on jsDump by Ariel Flesler
-// http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html
-QUnit.dump = ( function() {
-       function quote( str ) {
-               return "\"" + str.toString().replace( /\\/g, "\\\\" ).replace( /"/g, "\\\"" ) + "\"";
-       }
-       function literal( o ) {
-               return o + "";
-       }
-       function join( pre, arr, post ) {
-               var s = dump.separator(),
-                       base = dump.indent(),
-                       inner = dump.indent( 1 );
-               if ( arr.join ) {
-                       arr = arr.join( "," + s + inner );
-               }
-               if ( !arr ) {
-                       return pre + post;
-               }
-               return [ pre, inner + arr, base + post ].join( s );
-       }
-       function array( arr, stack ) {
-               var i = arr.length,
-                       ret = new Array( i );
-
-               if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
-                       return "[object Array]";
-               }
-
-               this.up();
-               while ( i-- ) {
-                       ret[ i ] = this.parse( arr[ i ], undefined, stack );
-               }
-               this.down();
-               return join( "[", ret, "]" );
-       }
-
-       var reName = /^function (\w+)/,
-               dump = {
-
-                       // The objType is used mostly internally, you can fix a (custom) type in advance
-                       parse: function( obj, objType, stack ) {
-                               stack = stack || [];
-                               var res, parser, parserType,
-                                       inStack = inArray( obj, stack );
-
-                               if ( inStack !== -1 ) {
-                                       return "recursion(" + ( inStack - stack.length ) + ")";
-                               }
-
-                               objType = objType || this.typeOf( obj  );
-                               parser = this.parsers[ objType ];
-                               parserType = typeof parser;
-
-                               if ( parserType === "function" ) {
-                                       stack.push( obj );
-                                       res = parser.call( this, obj, stack );
-                                       stack.pop();
-                                       return res;
-                               }
-                               return ( parserType === "string" ) ? parser : this.parsers.error;
-                       },
-                       typeOf: function( obj ) {
-                               var type;
-                               if ( obj === null ) {
-                                       type = "null";
-                               } else if ( typeof obj === "undefined" ) {
-                                       type = "undefined";
-                               } else if ( QUnit.is( "regexp", obj ) ) {
-                                       type = "regexp";
-                               } else if ( QUnit.is( "date", obj ) ) {
-                                       type = "date";
-                               } else if ( QUnit.is( "function", obj ) ) {
-                                       type = "function";
-                               } else if ( obj.setInterval !== undefined &&
-                                               obj.document !== undefined &&
-                                               obj.nodeType === undefined ) {
-                                       type = "window";
-                               } else if ( obj.nodeType === 9 ) {
-                                       type = "document";
-                               } else if ( obj.nodeType ) {
-                                       type = "node";
-                               } else if (
-
-                                       // Native arrays
-                                       toString.call( obj ) === "[object Array]" ||
-
-                                       // NodeList objects
-                                       ( typeof obj.length === "number" && obj.item !== undefined &&
-                                       ( obj.length ? obj.item( 0 ) === obj[ 0 ] : ( obj.item( 0 ) === null &&
-                                       obj[ 0 ] === undefined ) ) )
-                               ) {
-                                       type = "array";
-                               } else if ( obj.constructor === Error.prototype.constructor ) {
-                                       type = "error";
-                               } else {
-                                       type = typeof obj;
-                               }
-                               return type;
-                       },
-
-                       separator: function() {
-                               return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&#160;" : " ";
-                       },
-
-                       // Extra can be a number, shortcut for increasing-calling-decreasing
-                       indent: function( extra ) {
-                               if ( !this.multiline ) {
-                                       return "";
-                               }
-                               var chr = this.indentChar;
-                               if ( this.HTML ) {
-                                       chr = chr.replace( /\t/g, "   " ).replace( / /g, "&#160;" );
-                               }
-                               return new Array( this.depth + ( extra || 0 ) ).join( chr );
-                       },
-                       up: function( a ) {
-                               this.depth += a || 1;
-                       },
-                       down: function( a ) {
-                               this.depth -= a || 1;
-                       },
-                       setParser: function( name, parser ) {
-                               this.parsers[ name ] = parser;
-                       },
-
-                       // The next 3 are exposed so you can use them
-                       quote: quote,
-                       literal: literal,
-                       join: join,
-                       depth: 1,
-                       maxDepth: QUnit.config.maxDepth,
-
-                       // This is the list of parsers, to modify them, use dump.setParser
-                       parsers: {
-                               window: "[Window]",
-                               document: "[Document]",
-                               error: function( error ) {
-                                       return "Error(\"" + error.message + "\")";
-                               },
-                               unknown: "[Unknown]",
-                               "null": "null",
-                               "undefined": "undefined",
-                               "function": function( fn ) {
-                                       var ret = "function",
-
-                                               // Functions never have name in IE
-                                               name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ];
-
-                                       if ( name ) {
-                                               ret += " " + name;
-                                       }
-                                       ret += "(";
-
-                                       ret = [ ret, dump.parse( fn, "functionArgs" ), "){" ].join( "" );
-                                       return join( ret, dump.parse( fn, "functionCode" ), "}" );
-                               },
-                               array: array,
-                               nodelist: array,
-                               "arguments": array,
-                               object: function( map, stack ) {
-                                       var keys, key, val, i, nonEnumerableProperties,
-                                               ret = [];
-
-                                       if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
-                                               return "[object Object]";
-                                       }
-
-                                       dump.up();
-                                       keys = [];
-                                       for ( key in map ) {
-                                               keys.push( key );
-                                       }
-
-                                       // Some properties are not always enumerable on Error objects.
-                                       nonEnumerableProperties = [ "message", "name" ];
-                                       for ( i in nonEnumerableProperties ) {
-                                               key = nonEnumerableProperties[ i ];
-                                               if ( key in map && inArray( key, keys ) < 0 ) {
-                                                       keys.push( key );
-                                               }
-                                       }
-                                       keys.sort();
-                                       for ( i = 0; i < keys.length; i++ ) {
-                                               key = keys[ i ];
-                                               val = map[ key ];
-                                               ret.push( dump.parse( key, "key" ) + ": " +
-                                                       dump.parse( val, undefined, stack ) );
-                                       }
-                                       dump.down();
-                                       return join( "{", ret, "}" );
-                               },
-                               node: function( node ) {
-                                       var len, i, val,
-                                               open = dump.HTML ? "&lt;" : "<",
-                                               close = dump.HTML ? "&gt;" : ">",
-                                               tag = node.nodeName.toLowerCase(),
-                                               ret = open + tag,
-                                               attrs = node.attributes;
-
-                                       if ( attrs ) {
-                                               for ( i = 0, len = attrs.length; i < len; i++ ) {
-                                                       val = attrs[ i ].nodeValue;
-
-                                                       // IE6 includes all attributes in .attributes, even ones not explicitly
-                                                       // set. Those have values like undefined, null, 0, false, "" or
-                                                       // "inherit".
-                                                       if ( val && val !== "inherit" ) {
-                                                               ret += " " + attrs[ i ].nodeName + "=" +
-                                                                       dump.parse( val, "attribute" );
-                                                       }
-                                               }
-                                       }
-                                       ret += close;
-
-                                       // Show content of TextNode or CDATASection
-                                       if ( node.nodeType === 3 || node.nodeType === 4 ) {
-                                               ret += node.nodeValue;
-                                       }
-
-                                       return ret + open + "/" + tag + close;
-                               },
-
-                               // Function calls it internally, it's the arguments part of the function
-                               functionArgs: function( fn ) {
-                                       var args,
-                                               l = fn.length;
-
-                                       if ( !l ) {
-                                               return "";
-                                       }
-
-                                       args = new Array( l );
-                                       while ( l-- ) {
-
-                                               // 97 is 'a'
-                                               args[ l ] = String.fromCharCode( 97 + l );
-                                       }
-                                       return " " + args.join( ", " ) + " ";
-                               },
-
-                               // Object calls it internally, the key part of an item in a map
-                               key: quote,
-
-                               // Function calls it internally, it's the content of the function
-                               functionCode: "[code]",
-
-                               // Node calls it internally, it's a html attribute value
-                               attribute: quote,
-                               string: quote,
-                               date: quote,
-                               regexp: literal,
-                               number: literal,
-                               "boolean": literal
-                       },
-
-                       // If true, entities are escaped ( <, >, \t, space and \n )
-                       HTML: false,
-
-                       // Indentation unit
-                       indentChar: "  ",
-
-                       // If true, items in a collection, are separated by a \n, else just a space.
-                       multiline: true
-               };
-
-       return dump;
-}() );
-
-// Back compat
-QUnit.jsDump = QUnit.dump;
-
-// Deprecated
-// Extend assert methods to QUnit for Backwards compatibility
-( function() {
-       var i,
-               assertions = Assert.prototype;
-
-       function applyCurrent( current ) {
-               return function() {
-                       var assert = new Assert( QUnit.config.current );
-                       current.apply( assert, arguments );
-               };
-       }
-
-       for ( i in assertions ) {
-               QUnit[ i ] = applyCurrent( assertions[ i ] );
-       }
-}() );
-
-// For browser, export only select globals
-if ( defined.document ) {
-
-       ( function() {
-               var i, l,
-                       keys = [
-                               "test",
-                               "module",
-                               "expect",
-                               "asyncTest",
-                               "start",
-                               "stop",
-                               "ok",
-                               "notOk",
-                               "equal",
-                               "notEqual",
-                               "propEqual",
-                               "notPropEqual",
-                               "deepEqual",
-                               "notDeepEqual",
-                               "strictEqual",
-                               "notStrictEqual",
-                               "throws",
-                               "raises"
-                       ];
-
-               for ( i = 0, l = keys.length; i < l; i++ ) {
-                       window[ keys[ i ] ] = QUnit[ keys[ i ] ];
-               }
-       }() );
-
-       window.QUnit = QUnit;
-}
-
-// For nodejs
-if ( typeof module !== "undefined" && module && module.exports ) {
-       module.exports = QUnit;
-
-       // For consistency with CommonJS environments' exports
-       module.exports.QUnit = QUnit;
-}
-
-// For CommonJS with exports, but without module.exports, like Rhino
-if ( typeof exports !== "undefined" && exports ) {
-       exports.QUnit = QUnit;
-}
-
-if ( typeof define === "function" && define.amd ) {
-       define( function() {
-               return QUnit;
-       } );
-       QUnit.config.autostart = false;
-}
-
-// Get a reference to the global object, like window in browsers
-}( ( function() {
-       return this;
-}() ) ) );
-
-( function() {
-
-// Only interact with URLs via window.location
-var location = typeof window !== "undefined" && window.location;
-if ( !location ) {
-       return;
-}
-
-var urlParams = getUrlParams();
-
-QUnit.urlParams = urlParams;
-
-// Match module/test by inclusion in an array
-QUnit.config.moduleId = [].concat( urlParams.moduleId || [] );
-QUnit.config.testId = [].concat( urlParams.testId || [] );
-
-// Exact case-insensitive match of the module name
-QUnit.config.module = urlParams.module;
-
-// Regular expression or case-insenstive substring match against "moduleName: testName"
-QUnit.config.filter = urlParams.filter;
-
-// Test order randomization
-if ( urlParams.seed === true ) {
-
-       // Generate a random seed if the option is specified without a value
-       QUnit.config.seed = Math.random().toString( 36 ).slice( 2 );
-} else if ( urlParams.seed ) {
-       QUnit.config.seed = urlParams.seed;
-}
-
-// Add URL-parameter-mapped config values with UI form rendering data
-QUnit.config.urlConfig.push(
-       {
-               id: "hidepassed",
-               label: "Hide passed tests",
-               tooltip: "Only show tests and assertions that fail. Stored as query-strings."
-       },
-       {
-               id: "noglobals",
-               label: "Check for Globals",
-               tooltip: "Enabling this will test if any test introduces new properties on the " +
-                       "global object (`window` in Browsers). Stored as query-strings."
-       },
-       {
-               id: "notrycatch",
-               label: "No try-catch",
-               tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " +
-                       "exceptions in IE reasonable. Stored as query-strings."
-       }
-);
-
-QUnit.begin( function() {
-       var i, option,
-               urlConfig = QUnit.config.urlConfig;
-
-       for ( i = 0; i < urlConfig.length; i++ ) {
-
-               // Options can be either strings or objects with nonempty "id" properties
-               option = QUnit.config.urlConfig[ i ];
-               if ( typeof option !== "string" ) {
-                       option = option.id;
-               }
-
-               if ( QUnit.config[ option ] === undefined ) {
-                       QUnit.config[ option ] = urlParams[ option ];
-               }
-       }
-} );
-
-function getUrlParams() {
-       var i, param, name, value;
-       var urlParams = {};
-       var params = location.search.slice( 1 ).split( "&" );
-       var length = params.length;
-
-       for ( i = 0; i < length; i++ ) {
-               if ( params[ i ] ) {
-                       param = params[ i ].split( "=" );
-                       name = decodeURIComponent( param[ 0 ] );
-
-                       // Allow just a key to turn on a flag, e.g., test.html?noglobals
-                       value = param.length === 1 ||
-                               decodeURIComponent( param.slice( 1 ).join( "=" ) ) ;
-                       if ( urlParams[ name ] ) {
-                               urlParams[ name ] = [].concat( urlParams[ name ], value );
-                       } else {
-                               urlParams[ name ] = value;
-                       }
-               }
-       }
-
-       return urlParams;
-}
-
-// Don't load the HTML Reporter on non-browser environments
-if ( typeof window === "undefined" || !window.document ) {
-       return;
-}
-
-// Deprecated QUnit.init - Ref #530
-// Re-initialize the configuration options
-QUnit.init = function() {
-       var config = QUnit.config;
-
-       config.stats = { all: 0, bad: 0 };
-       config.moduleStats = { all: 0, bad: 0 };
-       config.started = 0;
-       config.updateRate = 1000;
-       config.blocking = false;
-       config.autostart = true;
-       config.autorun = false;
-       config.filter = "";
-       config.queue = [];
-
-       appendInterface();
-};
-
-var config = QUnit.config,
-       document = window.document,
-       collapseNext = false,
-       hasOwn = Object.prototype.hasOwnProperty,
-       unfilteredUrl = setUrl( { filter: undefined, module: undefined,
-               moduleId: undefined, testId: undefined } ),
-       defined = {
-               sessionStorage: ( function() {
-                       var x = "qunit-test-string";
-                       try {
-                               sessionStorage.setItem( x, x );
-                               sessionStorage.removeItem( x );
-                               return true;
-                       } catch ( e ) {
-                               return false;
-                       }
-               }() )
-       },
-       modulesList = [];
-
-/**
-* Escape text for attribute or text content.
-*/
-function escapeText( s ) {
-       if ( !s ) {
-               return "";
-       }
-       s = s + "";
-
-       // Both single quotes and double quotes (for attributes)
-       return s.replace( /['"<>&]/g, function( s ) {
-               switch ( s ) {
-               case "'":
-                       return "&#039;";
-               case "\"":
-                       return "&quot;";
-               case "<":
-                       return "&lt;";
-               case ">":
-                       return "&gt;";
-               case "&":
-                       return "&amp;";
-               }
-       } );
-}
-
-/**
- * @param {HTMLElement} elem
- * @param {string} type
- * @param {Function} fn
- */
-function addEvent( elem, type, fn ) {
-       if ( elem.addEventListener ) {
-
-               // Standards-based browsers
-               elem.addEventListener( type, fn, false );
-       } else if ( elem.attachEvent ) {
-
-               // Support: IE <9
-               elem.attachEvent( "on" + type, function() {
-                       var event = window.event;
-                       if ( !event.target ) {
-                               event.target = event.srcElement || document;
-                       }
-
-                       fn.call( elem, event );
-               } );
-       }
-}
-
-/**
- * @param {Array|NodeList} elems
- * @param {string} type
- * @param {Function} fn
- */
-function addEvents( elems, type, fn ) {
-       var i = elems.length;
-       while ( i-- ) {
-               addEvent( elems[ i ], type, fn );
-       }
-}
-
-function hasClass( elem, name ) {
-       return ( " " + elem.className + " " ).indexOf( " " + name + " " ) >= 0;
-}
-
-function addClass( elem, name ) {
-       if ( !hasClass( elem, name ) ) {
-               elem.className += ( elem.className ? " " : "" ) + name;
-       }
-}
-
-function toggleClass( elem, name, force ) {
-       if ( force || typeof force === "undefined" && !hasClass( elem, name ) ) {
-               addClass( elem, name );
-       } else {
-               removeClass( elem, name );
-       }
-}
-
-function removeClass( elem, name ) {
-       var set = " " + elem.className + " ";
-
-       // Class name may appear multiple times
-       while ( set.indexOf( " " + name + " " ) >= 0 ) {
-               set = set.replace( " " + name + " ", " " );
-       }
-
-       // Trim for prettiness
-       elem.className = typeof set.trim === "function" ? set.trim() : set.replace( /^\s+|\s+$/g, "" );
-}
-
-function id( name ) {
-       return document.getElementById && document.getElementById( name );
-}
-
-function getUrlConfigHtml() {
-       var i, j, val,
-               escaped, escapedTooltip,
-               selection = false,
-               urlConfig = config.urlConfig,
-               urlConfigHtml = "";
-
-       for ( i = 0; i < urlConfig.length; i++ ) {
-
-               // Options can be either strings or objects with nonempty "id" properties
-               val = config.urlConfig[ i ];
-               if ( typeof val === "string" ) {
-                       val = {
-                               id: val,
-                               label: val
-                       };
-               }
-
-               escaped = escapeText( val.id );
-               escapedTooltip = escapeText( val.tooltip );
-
-               if ( !val.value || typeof val.value === "string" ) {
-                       urlConfigHtml += "<input id='qunit-urlconfig-" + escaped +
-                               "' name='" + escaped + "' type='checkbox'" +
-                               ( val.value ? " value='" + escapeText( val.value ) + "'" : "" ) +
-                               ( config[ val.id ] ? " checked='checked'" : "" ) +
-                               " title='" + escapedTooltip + "' /><label for='qunit-urlconfig-" + escaped +
-                               "' title='" + escapedTooltip + "'>" + val.label + "</label>";
-               } else {
-                       urlConfigHtml += "<label for='qunit-urlconfig-" + escaped +
-                               "' title='" + escapedTooltip + "'>" + val.label +
-                               ": </label><select id='qunit-urlconfig-" + escaped +
-                               "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
-
-                       if ( QUnit.is( "array", val.value ) ) {
-                               for ( j = 0; j < val.value.length; j++ ) {
-                                       escaped = escapeText( val.value[ j ] );
-                                       urlConfigHtml += "<option value='" + escaped + "'" +
-                                               ( config[ val.id ] === val.value[ j ] ?
-                                                       ( selection = true ) && " selected='selected'" : "" ) +
-                                               ">" + escaped + "</option>";
-                               }
-                       } else {
-                               for ( j in val.value ) {
-                                       if ( hasOwn.call( val.value, j ) ) {
-                                               urlConfigHtml += "<option value='" + escapeText( j ) + "'" +
-                                                       ( config[ val.id ] === j ?
-                                                               ( selection = true ) && " selected='selected'" : "" ) +
-                                                       ">" + escapeText( val.value[ j ] ) + "</option>";
-                                       }
-                               }
-                       }
-                       if ( config[ val.id ] && !selection ) {
-                               escaped = escapeText( config[ val.id ] );
-                               urlConfigHtml += "<option value='" + escaped +
-                                       "' selected='selected' disabled='disabled'>" + escaped + "</option>";
-                       }
-                       urlConfigHtml += "</select>";
-               }
-       }
-
-       return urlConfigHtml;
-}
-
-// Handle "click" events on toolbar checkboxes and "change" for select menus.
-// Updates the URL with the new state of `config.urlConfig` values.
-function toolbarChanged() {
-       var updatedUrl, value, tests,
-               field = this,
-               params = {};
-
-       // Detect if field is a select menu or a checkbox
-       if ( "selectedIndex" in field ) {
-               value = field.options[ field.selectedIndex ].value || undefined;
-       } else {
-               value = field.checked ? ( field.defaultValue || true ) : undefined;
-       }
-
-       params[ field.name ] = value;
-       updatedUrl = setUrl( params );
-
-       // Check if we can apply the change without a page refresh
-       if ( "hidepassed" === field.name && "replaceState" in window.history ) {
-               QUnit.urlParams[ field.name ] = value;
-               config[ field.name ] = value || false;
-               tests = id( "qunit-tests" );
-               if ( tests ) {
-                       toggleClass( tests, "hidepass", value || false );
-               }
-               window.history.replaceState( null, "", updatedUrl );
-       } else {
-               window.location = updatedUrl;
-       }
-}
-
-function setUrl( params ) {
-       var key, arrValue, i,
-               querystring = "?",
-               location = window.location;
-
-       params = QUnit.extend( QUnit.extend( {}, QUnit.urlParams ), params );
-
-       for ( key in params ) {
-
-               // Skip inherited or undefined properties
-               if ( hasOwn.call( params, key ) && params[ key ] !== undefined ) {
-
-                       // Output a parameter for each value of this key (but usually just one)
-                       arrValue = [].concat( params[ key ] );
-                       for ( i = 0; i < arrValue.length; i++ ) {
-                               querystring += encodeURIComponent( key );
-                               if ( arrValue[ i ] !== true ) {
-                                       querystring += "=" + encodeURIComponent( arrValue[ i ] );
-                               }
-                               querystring += "&";
-                       }
-               }
-       }
-       return location.protocol + "//" + location.host +
-               location.pathname + querystring.slice( 0, -1 );
-}
-
-function applyUrlParams() {
-       var selectedModule,
-               modulesList = id( "qunit-modulefilter" ),
-               filter = id( "qunit-filter-input" ).value;
-
-       selectedModule = modulesList ?
-               decodeURIComponent( modulesList.options[ modulesList.selectedIndex ].value ) :
-               undefined;
-
-       window.location = setUrl( {
-               module: ( selectedModule === "" ) ? undefined : selectedModule,
-               filter: ( filter === "" ) ? undefined : filter,
-
-               // Remove moduleId and testId filters
-               moduleId: undefined,
-               testId: undefined
-       } );
-}
-
-function toolbarUrlConfigContainer() {
-       var urlConfigContainer = document.createElement( "span" );
-
-       urlConfigContainer.innerHTML = getUrlConfigHtml();
-       addClass( urlConfigContainer, "qunit-url-config" );
-
-       // For oldIE support:
-       // * Add handlers to the individual elements instead of the container
-       // * Use "click" instead of "change" for checkboxes
-       addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", toolbarChanged );
-       addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", toolbarChanged );
-
-       return urlConfigContainer;
-}
-
-function toolbarLooseFilter() {
-       var filter = document.createElement( "form" ),
-               label = document.createElement( "label" ),
-               input = document.createElement( "input" ),
-               button = document.createElement( "button" );
-
-       addClass( filter, "qunit-filter" );
-
-       label.innerHTML = "Filter: ";
-
-       input.type = "text";
-       input.value = config.filter || "";
-       input.name = "filter";
-       input.id = "qunit-filter-input";
-
-       button.innerHTML = "Go";
-
-       label.appendChild( input );
-
-       filter.appendChild( label );
-       filter.appendChild( button );
-       addEvent( filter, "submit", function( ev ) {
-               applyUrlParams();
-
-               if ( ev && ev.preventDefault ) {
-                       ev.preventDefault();
-               }
-
-               return false;
-       } );
-
-       return filter;
-}
-
-function toolbarModuleFilterHtml() {
-       var i,
-               moduleFilterHtml = "";
-
-       if ( !modulesList.length ) {
-               return false;
-       }
-
-       moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label>" +
-               "<select id='qunit-modulefilter' name='modulefilter'><option value='' " +
-               ( QUnit.urlParams.module === undefined ? "selected='selected'" : "" ) +
-               ">< All Modules ></option>";
-
-       for ( i = 0; i < modulesList.length; i++ ) {
-               moduleFilterHtml += "<option value='" +
-                       escapeText( encodeURIComponent( modulesList[ i ] ) ) + "' " +
-                       ( QUnit.urlParams.module === modulesList[ i ] ? "selected='selected'" : "" ) +
-                       ">" + escapeText( modulesList[ i ] ) + "</option>";
-       }
-       moduleFilterHtml += "</select>";
-
-       return moduleFilterHtml;
-}
-
-function toolbarModuleFilter() {
-       var toolbar = id( "qunit-testrunner-toolbar" ),
-               moduleFilter = document.createElement( "span" ),
-               moduleFilterHtml = toolbarModuleFilterHtml();
-
-       if ( !toolbar || !moduleFilterHtml ) {
-               return false;
-       }
-
-       moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
-       moduleFilter.innerHTML = moduleFilterHtml;
-
-       addEvent( moduleFilter.lastChild, "change", applyUrlParams );
-
-       toolbar.appendChild( moduleFilter );
-}
-
-function appendToolbar() {
-       var toolbar = id( "qunit-testrunner-toolbar" );
-
-       if ( toolbar ) {
-               toolbar.appendChild( toolbarUrlConfigContainer() );
-               toolbar.appendChild( toolbarLooseFilter() );
-               toolbarModuleFilter();
-       }
-}
-
-function appendHeader() {
-       var header = id( "qunit-header" );
-
-       if ( header ) {
-               header.innerHTML = "<a href='" + escapeText( unfilteredUrl ) + "'>" + header.innerHTML +
-                       "</a> ";
-       }
-}
-
-function appendBanner() {
-       var banner = id( "qunit-banner" );
-
-       if ( banner ) {
-               banner.className = "";
-       }
-}
-
-function appendTestResults() {
-       var tests = id( "qunit-tests" ),
-               result = id( "qunit-testresult" );
-
-       if ( result ) {
-               result.parentNode.removeChild( result );
-       }
-
-       if ( tests ) {
-               tests.innerHTML = "";
-               result = document.createElement( "p" );
-               result.id = "qunit-testresult";
-               result.className = "result";
-               tests.parentNode.insertBefore( result, tests );
-               result.innerHTML = "Running...<br />&#160;";
-       }
-}
-
-function storeFixture() {
-       var fixture = id( "qunit-fixture" );
-       if ( fixture ) {
-               config.fixture = fixture.innerHTML;
-       }
-}
-
-function appendFilteredTest() {
-       var testId = QUnit.config.testId;
-       if ( !testId || testId.length <= 0 ) {
-               return "";
-       }
-       return "<div id='qunit-filteredTest'>Rerunning selected tests: " +
-               escapeText( testId.join( ", " ) ) +
-               " <a id='qunit-clearFilter' href='" +
-               escapeText( unfilteredUrl ) +
-               "'>Run all tests</a></div>";
-}
-
-function appendUserAgent() {
-       var userAgent = id( "qunit-userAgent" );
-
-       if ( userAgent ) {
-               userAgent.innerHTML = "";
-               userAgent.appendChild(
-                       document.createTextNode(
-                               "QUnit " + QUnit.version + "; " + navigator.userAgent
-                       )
-               );
-       }
-}
-
-function appendInterface() {
-       var qunit = id( "qunit" );
-
-       if ( qunit ) {
-               qunit.innerHTML =
-                       "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
-                       "<h2 id='qunit-banner'></h2>" +
-                       "<div id='qunit-testrunner-toolbar'></div>" +
-                       appendFilteredTest() +
-                       "<h2 id='qunit-userAgent'></h2>" +
-                       "<ol id='qunit-tests'></ol>";
-       }
-
-       appendHeader();
-       appendBanner();
-       appendTestResults();
-       appendUserAgent();
-       appendToolbar();
-}
-
-function appendTestsList( modules ) {
-       var i, l, x, z, test, moduleObj;
-
-       for ( i = 0, l = modules.length; i < l; i++ ) {
-               moduleObj = modules[ i ];
-
-               for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) {
-                       test = moduleObj.tests[ x ];
-
-                       appendTest( test.name, test.testId, moduleObj.name );
-               }
-       }
-}
-
-function appendTest( name, testId, moduleName ) {
-       var title, rerunTrigger, testBlock, assertList,
-               tests = id( "qunit-tests" );
-
-       if ( !tests ) {
-               return;
-       }
-
-       title = document.createElement( "strong" );
-       title.innerHTML = getNameHtml( name, moduleName );
-
-       rerunTrigger = document.createElement( "a" );
-       rerunTrigger.innerHTML = "Rerun";
-       rerunTrigger.href = setUrl( { testId: testId } );
-
-       testBlock = document.createElement( "li" );
-       testBlock.appendChild( title );
-       testBlock.appendChild( rerunTrigger );
-       testBlock.id = "qunit-test-output-" + testId;
-
-       assertList = document.createElement( "ol" );
-       assertList.className = "qunit-assert-list";
-
-       testBlock.appendChild( assertList );
-
-       tests.appendChild( testBlock );
-}
-
-// HTML Reporter initialization and load
-QUnit.begin( function( details ) {
-       var i, moduleObj, tests;
-
-       // Sort modules by name for the picker
-       for ( i = 0; i < details.modules.length; i++ ) {
-               moduleObj = details.modules[ i ];
-               if ( moduleObj.name ) {
-                       modulesList.push( moduleObj.name );
-               }
-       }
-       modulesList.sort( function( a, b ) {
-               return a.localeCompare( b );
-       } );
-
-       // Capture fixture HTML from the page
-       storeFixture();
-
-       // Initialize QUnit elements
-       appendInterface();
-       appendTestsList( details.modules );
-       tests = id( "qunit-tests" );
-       if ( tests && config.hidepassed ) {
-               addClass( tests, "hidepass" );
-       }
-} );
-
-QUnit.done( function( details ) {
-       var i, key,
-               banner = id( "qunit-banner" ),
-               tests = id( "qunit-tests" ),
-               html = [
-                       "Tests completed in ",
-                       details.runtime,
-                       " milliseconds.<br />",
-                       "<span class='passed'>",
-                       details.passed,
-                       "</span> assertions of <span class='total'>",
-                       details.total,
-                       "</span> passed, <span class='failed'>",
-                       details.failed,
-                       "</span> failed."
-               ].join( "" );
-
-       if ( banner ) {
-               banner.className = details.failed ? "qunit-fail" : "qunit-pass";
-       }
-
-       if ( tests ) {
-               id( "qunit-testresult" ).innerHTML = html;
-       }
-
-       if ( config.altertitle && document.title ) {
-
-               // Show ✖ for good, ✔ for bad suite result in title
-               // use escape sequences in case file gets loaded with non-utf-8-charset
-               document.title = [
-                       ( details.failed ? "\u2716" : "\u2714" ),
-                       document.title.replace( /^[\u2714\u2716] /i, "" )
-               ].join( " " );
-       }
-
-       // Clear own sessionStorage items if all tests passed
-       if ( config.reorder && defined.sessionStorage && details.failed === 0 ) {
-               for ( i = 0; i < sessionStorage.length; i++ ) {
-                       key = sessionStorage.key( i++ );
-                       if ( key.indexOf( "qunit-test-" ) === 0 ) {
-                               sessionStorage.removeItem( key );
-                       }
-               }
-       }
-
-       // Scroll back to top to show results
-       if ( config.scrolltop && window.scrollTo ) {
-               window.scrollTo( 0, 0 );
-       }
-} );
-
-function getNameHtml( name, module ) {
-       var nameHtml = "";
-
-       if ( module ) {
-               nameHtml = "<span class='module-name'>" + escapeText( module ) + "</span>: ";
-       }
-
-       nameHtml += "<span class='test-name'>" + escapeText( name ) + "</span>";
-
-       return nameHtml;
-}
-
-QUnit.testStart( function( details ) {
-       var running, testBlock, bad;
-
-       testBlock = id( "qunit-test-output-" + details.testId );
-       if ( testBlock ) {
-               testBlock.className = "running";
-       } else {
-
-               // Report later registered tests
-               appendTest( details.name, details.testId, details.module );
-       }
-
-       running = id( "qunit-testresult" );
-       if ( running ) {
-               bad = QUnit.config.reorder && defined.sessionStorage &&
-                       +sessionStorage.getItem( "qunit-test-" + details.module + "-" + details.name );
-
-               running.innerHTML = ( bad ?
-                       "Rerunning previously failed test: <br />" :
-                       "Running: <br />" ) +
-                       getNameHtml( details.name, details.module );
-       }
-
-} );
-
-function stripHtml( string ) {
-
-       // Strip tags, html entity and whitespaces
-       return string.replace( /<\/?[^>]+(>|$)/g, "" ).replace( /\&quot;/g, "" ).replace( /\s+/g, "" );
-}
-
-QUnit.log( function( details ) {
-       var assertList, assertLi,
-               message, expected, actual, diff,
-               showDiff = false,
-               testItem = id( "qunit-test-output-" + details.testId );
-
-       if ( !testItem ) {
-               return;
-       }
-
-       message = escapeText( details.message ) || ( details.result ? "okay" : "failed" );
-       message = "<span class='test-message'>" + message + "</span>";
-       message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
-
-       // The pushFailure doesn't provide details.expected
-       // when it calls, it's implicit to also not show expected and diff stuff
-       // Also, we need to check details.expected existence, as it can exist and be undefined
-       if ( !details.result && hasOwn.call( details, "expected" ) ) {
-               if ( details.negative ) {
-                       expected = "NOT " + QUnit.dump.parse( details.expected );
-               } else {
-                       expected = QUnit.dump.parse( details.expected );
-               }
-
-               actual = QUnit.dump.parse( details.actual );
-               message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" +
-                       escapeText( expected ) +
-                       "</pre></td></tr>";
-
-               if ( actual !== expected ) {
-
-                       message += "<tr class='test-actual'><th>Result: </th><td><pre>" +
-                               escapeText( actual ) + "</pre></td></tr>";
-
-                       // Don't show diff if actual or expected are booleans
-                       if ( !( /^(true|false)$/.test( actual ) ) &&
-                                       !( /^(true|false)$/.test( expected ) ) ) {
-                               diff = QUnit.diff( expected, actual );
-                               showDiff = stripHtml( diff ).length !==
-                                       stripHtml( expected ).length +
-                                       stripHtml( actual ).length;
-                       }
-
-                       // Don't show diff if expected and actual are totally different
-                       if ( showDiff ) {
-                               message += "<tr class='test-diff'><th>Diff: </th><td><pre>" +
-                                       diff + "</pre></td></tr>";
-                       }
-               } else if ( expected.indexOf( "[object Array]" ) !== -1 ||
-                               expected.indexOf( "[object Object]" ) !== -1 ) {
-                       message += "<tr class='test-message'><th>Message: </th><td>" +
-                               "Diff suppressed as the depth of object is more than current max depth (" +
-                               QUnit.config.maxDepth + ").<p>Hint: Use <code>QUnit.dump.maxDepth</code> to " +
-                               " run with a higher max depth or <a href='" +
-                               escapeText( setUrl( { maxDepth: -1 } ) ) + "'>" +
-                               "Rerun</a> without max depth.</p></td></tr>";
-               } else {
-                       message += "<tr class='test-message'><th>Message: </th><td>" +
-                               "Diff suppressed as the expected and actual results have an equivalent" +
-                               " serialization</td></tr>";
-               }
-
-               if ( details.source ) {
-                       message += "<tr class='test-source'><th>Source: </th><td><pre>" +
-                               escapeText( details.source ) + "</pre></td></tr>";
-               }
-
-               message += "</table>";
-
-       // This occurs when pushFailure is set and we have an extracted stack trace
-       } else if ( !details.result && details.source ) {
-               message += "<table>" +
-                       "<tr class='test-source'><th>Source: </th><td><pre>" +
-                       escapeText( details.source ) + "</pre></td></tr>" +
-                       "</table>";
-       }
-
-       assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
-
-       assertLi = document.createElement( "li" );
-       assertLi.className = details.result ? "pass" : "fail";
-       assertLi.innerHTML = message;
-       assertList.appendChild( assertLi );
-} );
-
-QUnit.testDone( function( details ) {
-       var testTitle, time, testItem, assertList,
-               good, bad, testCounts, skipped, sourceName,
-               tests = id( "qunit-tests" );
-
-       if ( !tests ) {
-               return;
-       }
-
-       testItem = id( "qunit-test-output-" + details.testId );
-
-       assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
-
-       good = details.passed;
-       bad = details.failed;
-
-       // Store result when possible
-       if ( config.reorder && defined.sessionStorage ) {
-               if ( bad ) {
-                       sessionStorage.setItem( "qunit-test-" + details.module + "-" + details.name, bad );
-               } else {
-                       sessionStorage.removeItem( "qunit-test-" + details.module + "-" + details.name );
-               }
-       }
-
-       if ( bad === 0 ) {
-
-               // Collapse the passing tests
-               addClass( assertList, "qunit-collapsed" );
-       } else if ( bad && config.collapse && !collapseNext ) {
-
-               // Skip collapsing the first failing test
-               collapseNext = true;
-       } else {
-
-               // Collapse remaining tests
-               addClass( assertList, "qunit-collapsed" );
-       }
-
-       // The testItem.firstChild is the test name
-       testTitle = testItem.firstChild;
-
-       testCounts = bad ?
-               "<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " :
-               "";
-
-       testTitle.innerHTML += " <b class='counts'>(" + testCounts +
-               details.assertions.length + ")</b>";
-
-       if ( details.skipped ) {
-               testItem.className = "skipped";
-               skipped = document.createElement( "em" );
-               skipped.className = "qunit-skipped-label";
-               skipped.innerHTML = "skipped";
-               testItem.insertBefore( skipped, testTitle );
-       } else {
-               addEvent( testTitle, "click", function() {
-                       toggleClass( assertList, "qunit-collapsed" );
-               } );
-
-               testItem.className = bad ? "fail" : "pass";
-
-               time = document.createElement( "span" );
-               time.className = "runtime";
-               time.innerHTML = details.runtime + " ms";
-               testItem.insertBefore( time, assertList );
-       }
-
-       // Show the source of the test when showing assertions
-       if ( details.source ) {
-               sourceName = document.createElement( "p" );
-               sourceName.innerHTML = "<strong>Source: </strong>" + details.source;
-               addClass( sourceName, "qunit-source" );
-               if ( bad === 0 ) {
-                       addClass( sourceName, "qunit-collapsed" );
-               }
-               addEvent( testTitle, "click", function() {
-                       toggleClass( sourceName, "qunit-collapsed" );
-               } );
-               testItem.appendChild( sourceName );
-       }
-} );
-
-// Avoid readyState issue with phantomjs
-// Ref: #818
-var notPhantom = ( function( p ) {
-       return !( p && p.version && p.version.major > 0 );
-} )( window.phantom );
-
-if ( notPhantom && document.readyState === "complete" ) {
-       QUnit.load();
-} else {
-       addEvent( window, "load", QUnit.load );
-}
-
-/*
- * This file is a modified version of google-diff-match-patch's JavaScript implementation
- * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js),
- * modifications are licensed as more fully set forth in LICENSE.txt.
- *
- * The original source of google-diff-match-patch is attributable and licensed as follows:
- *
- * Copyright 2006 Google Inc.
- * https://code.google.com/p/google-diff-match-patch/
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * More Info:
- *  https://code.google.com/p/google-diff-match-patch/
- *
- * Usage: QUnit.diff(expected, actual)
- *
- */
-QUnit.diff = ( function() {
-       function DiffMatchPatch() {
-       }
-
-       //  DIFF FUNCTIONS
-
-       /**
-        * The data structure representing a diff is an array of tuples:
-        * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
-        * which means: delete 'Hello', add 'Goodbye' and keep ' world.'
-        */
-       var DIFF_DELETE = -1,
-               DIFF_INSERT = 1,
-               DIFF_EQUAL = 0;
-
-       /**
-        * Find the differences between two texts.  Simplifies the problem by stripping
-        * any common prefix or suffix off the texts before diffing.
-        * @param {string} text1 Old string to be diffed.
-        * @param {string} text2 New string to be diffed.
-        * @param {boolean=} optChecklines Optional speedup flag. If present and false,
-        *     then don't run a line-level diff first to identify the changed areas.
-        *     Defaults to true, which does a faster, slightly less optimal diff.
-        * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
-        */
-       DiffMatchPatch.prototype.DiffMain = function( text1, text2, optChecklines ) {
-               var deadline, checklines, commonlength,
-                       commonprefix, commonsuffix, diffs;
-
-               // The diff must be complete in up to 1 second.
-               deadline = ( new Date() ).getTime() + 1000;
-
-               // Check for null inputs.
-               if ( text1 === null || text2 === null ) {
-                       throw new Error( "Null input. (DiffMain)" );
-               }
-
-               // Check for equality (speedup).
-               if ( text1 === text2 ) {
-                       if ( text1 ) {
-                               return [
-                                       [ DIFF_EQUAL, text1 ]
-                               ];
-                       }
-                       return [];
-               }
-
-               if ( typeof optChecklines === "undefined" ) {
-                       optChecklines = true;
-               }
-
-               checklines = optChecklines;
-
-               // Trim off common prefix (speedup).
-               commonlength = this.diffCommonPrefix( text1, text2 );
-               commonprefix = text1.substring( 0, commonlength );
-               text1 = text1.substring( commonlength );
-               text2 = text2.substring( commonlength );
-
-               // Trim off common suffix (speedup).
-               commonlength = this.diffCommonSuffix( text1, text2 );
-               commonsuffix = text1.substring( text1.length - commonlength );
-               text1 = text1.substring( 0, text1.length - commonlength );
-               text2 = text2.substring( 0, text2.length - commonlength );
-
-               // Compute the diff on the middle block.
-               diffs = this.diffCompute( text1, text2, checklines, deadline );
-
-               // Restore the prefix and suffix.
-               if ( commonprefix ) {
-                       diffs.unshift( [ DIFF_EQUAL, commonprefix ] );
-               }
-               if ( commonsuffix ) {
-                       diffs.push( [ DIFF_EQUAL, commonsuffix ] );
-               }
-               this.diffCleanupMerge( diffs );
-               return diffs;
-       };
-
-       /**
-        * Reduce the number of edits by eliminating operationally trivial equalities.
-        * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
-        */
-       DiffMatchPatch.prototype.diffCleanupEfficiency = function( diffs ) {
-               var changes, equalities, equalitiesLength, lastequality,
-                       pointer, preIns, preDel, postIns, postDel;
-               changes = false;
-               equalities = []; // Stack of indices where equalities are found.
-               equalitiesLength = 0; // Keeping our own length var is faster in JS.
-               /** @type {?string} */
-               lastequality = null;
-
-               // Always equal to diffs[equalities[equalitiesLength - 1]][1]
-               pointer = 0; // Index of current position.
-
-               // Is there an insertion operation before the last equality.
-               preIns = false;
-
-               // Is there a deletion operation before the last equality.
-               preDel = false;
-
-               // Is there an insertion operation after the last equality.
-               postIns = false;
-
-               // Is there a deletion operation after the last equality.
-               postDel = false;
-               while ( pointer < diffs.length ) {
-
-                       // Equality found.
-                       if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) {
-                               if ( diffs[ pointer ][ 1 ].length < 4 && ( postIns || postDel ) ) {
-
-                                       // Candidate found.
-                                       equalities[ equalitiesLength++ ] = pointer;
-                                       preIns = postIns;
-                                       preDel = postDel;
-                                       lastequality = diffs[ pointer ][ 1 ];
-                               } else {
-
-                                       // Not a candidate, and can never become one.
-                                       equalitiesLength = 0;
-                                       lastequality = null;
-                               }
-                               postIns = postDel = false;
-
-                       // An insertion or deletion.
-                       } else {
-
-                               if ( diffs[ pointer ][ 0 ] === DIFF_DELETE ) {
-                                       postDel = true;
-                               } else {
-                                       postIns = true;
-                               }
-
-                               /*
-                                * Five types to be split:
-                                * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
-                                * <ins>A</ins>X<ins>C</ins><del>D</del>
-                                * <ins>A</ins><del>B</del>X<ins>C</ins>
-                                * <ins>A</del>X<ins>C</ins><del>D</del>
-                                * <ins>A</ins><del>B</del>X<del>C</del>
-                                */
-                               if ( lastequality && ( ( preIns && preDel && postIns && postDel ) ||
-                                               ( ( lastequality.length < 2 ) &&
-                                               ( preIns + preDel + postIns + postDel ) === 3 ) ) ) {
-
-                                       // Duplicate record.
-                                       diffs.splice(
-                                               equalities[ equalitiesLength - 1 ],
-                                               0,
-                                               [ DIFF_DELETE, lastequality ]
-                                       );
-
-                                       // Change second copy to insert.
-                                       diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT;
-                                       equalitiesLength--; // Throw away the equality we just deleted;
-                                       lastequality = null;
-                                       if ( preIns && preDel ) {
-
-                                               // No changes made which could affect previous entry, keep going.
-                                               postIns = postDel = true;
-                                               equalitiesLength = 0;
-                                       } else {
-                                               equalitiesLength--; // Throw away the previous equality.
-                                               pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1;
-                                               postIns = postDel = false;
-                                       }
-                                       changes = true;
-                               }
-                       }
-                       pointer++;
-               }
-
-               if ( changes ) {
-                       this.diffCleanupMerge( diffs );
-               }
-       };
-
-       /**
-        * Convert a diff array into a pretty HTML report.
-        * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
-        * @param {integer} string to be beautified.
-        * @return {string} HTML representation.
-        */
-       DiffMatchPatch.prototype.diffPrettyHtml = function( diffs ) {
-               var op, data, x,
-                       html = [];
-               for ( x = 0; x < diffs.length; x++ ) {
-                       op = diffs[ x ][ 0 ]; // Operation (insert, delete, equal)
-                       data = diffs[ x ][ 1 ]; // Text of change.
-                       switch ( op ) {
-                       case DIFF_INSERT:
-                               html[ x ] = "<ins>" + escapeText( data ) + "</ins>";
-                               break;
-                       case DIFF_DELETE:
-                               html[ x ] = "<del>" + escapeText( data ) + "</del>";
-                               break;
-                       case DIFF_EQUAL:
-                               html[ x ] = "<span>" + escapeText( data ) + "</span>";
-                               break;
-                       }
-               }
-               return html.join( "" );
-       };
-
-       /**
-        * Determine the common prefix of two strings.
-        * @param {string} text1 First string.
-        * @param {string} text2 Second string.
-        * @return {number} The number of characters common to the start of each
-        *     string.
-        */
-       DiffMatchPatch.prototype.diffCommonPrefix = function( text1, text2 ) {
-               var pointermid, pointermax, pointermin, pointerstart;
-
-               // Quick check for common null cases.
-               if ( !text1 || !text2 || text1.charAt( 0 ) !== text2.charAt( 0 ) ) {
-                       return 0;
-               }
-
-               // Binary search.
-               // Performance analysis: https://neil.fraser.name/news/2007/10/09/
-               pointermin = 0;
-               pointermax = Math.min( text1.length, text2.length );
-               pointermid = pointermax;
-               pointerstart = 0;
-               while ( pointermin < pointermid ) {
-                       if ( text1.substring( pointerstart, pointermid ) ===
-                                       text2.substring( pointerstart, pointermid ) ) {
-                               pointermin = pointermid;
-                               pointerstart = pointermin;
-                       } else {
-                               pointermax = pointermid;
-                       }
-                       pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin );
-               }
-               return pointermid;
-       };
-
-       /**
-        * Determine the common suffix of two strings.
-        * @param {string} text1 First string.
-        * @param {string} text2 Second string.
-        * @return {number} The number of characters common to the end of each string.
-        */
-       DiffMatchPatch.prototype.diffCommonSuffix = function( text1, text2 ) {
-               var pointermid, pointermax, pointermin, pointerend;
-
-               // Quick check for common null cases.
-               if ( !text1 ||
-                               !text2 ||
-                               text1.charAt( text1.length - 1 ) !== text2.charAt( text2.length - 1 ) ) {
-                       return 0;
-               }
-
-               // Binary search.
-               // Performance analysis: https://neil.fraser.name/news/2007/10/09/
-               pointermin = 0;
-               pointermax = Math.min( text1.length, text2.length );
-               pointermid = pointermax;
-               pointerend = 0;
-               while ( pointermin < pointermid ) {
-                       if ( text1.substring( text1.length - pointermid, text1.length - pointerend ) ===
-                                       text2.substring( text2.length - pointermid, text2.length - pointerend ) ) {
-                               pointermin = pointermid;
-                               pointerend = pointermin;
-                       } else {
-                               pointermax = pointermid;
-                       }
-                       pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin );
-               }
-               return pointermid;
-       };
-
-       /**
-        * Find the differences between two texts.  Assumes that the texts do not
-        * have any common prefix or suffix.
-        * @param {string} text1 Old string to be diffed.
-        * @param {string} text2 New string to be diffed.
-        * @param {boolean} checklines Speedup flag.  If false, then don't run a
-        *     line-level diff first to identify the changed areas.
-        *     If true, then run a faster, slightly less optimal diff.
-        * @param {number} deadline Time when the diff should be complete by.
-        * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffCompute = function( text1, text2, checklines, deadline ) {
-               var diffs, longtext, shorttext, i, hm,
-                       text1A, text2A, text1B, text2B,
-                       midCommon, diffsA, diffsB;
-
-               if ( !text1 ) {
-
-                       // Just add some text (speedup).
-                       return [
-                               [ DIFF_INSERT, text2 ]
-                       ];
-               }
-
-               if ( !text2 ) {
-
-                       // Just delete some text (speedup).
-                       return [
-                               [ DIFF_DELETE, text1 ]
-                       ];
-               }
-
-               longtext = text1.length > text2.length ? text1 : text2;
-               shorttext = text1.length > text2.length ? text2 : text1;
-               i = longtext.indexOf( shorttext );
-               if ( i !== -1 ) {
-
-                       // Shorter text is inside the longer text (speedup).
-                       diffs = [
-                               [ DIFF_INSERT, longtext.substring( 0, i ) ],
-                               [ DIFF_EQUAL, shorttext ],
-                               [ DIFF_INSERT, longtext.substring( i + shorttext.length ) ]
-                       ];
-
-                       // Swap insertions for deletions if diff is reversed.
-                       if ( text1.length > text2.length ) {
-                               diffs[ 0 ][ 0 ] = diffs[ 2 ][ 0 ] = DIFF_DELETE;
-                       }
-                       return diffs;
-               }
-
-               if ( shorttext.length === 1 ) {
-
-                       // Single character string.
-                       // After the previous speedup, the character can't be an equality.
-                       return [
-                               [ DIFF_DELETE, text1 ],
-                               [ DIFF_INSERT, text2 ]
-                       ];
-               }
-
-               // Check to see if the problem can be split in two.
-               hm = this.diffHalfMatch( text1, text2 );
-               if ( hm ) {
-
-                       // A half-match was found, sort out the return data.
-                       text1A = hm[ 0 ];
-                       text1B = hm[ 1 ];
-                       text2A = hm[ 2 ];
-                       text2B = hm[ 3 ];
-                       midCommon = hm[ 4 ];
-
-                       // Send both pairs off for separate processing.
-                       diffsA = this.DiffMain( text1A, text2A, checklines, deadline );
-                       diffsB = this.DiffMain( text1B, text2B, checklines, deadline );
-
-                       // Merge the results.
-                       return diffsA.concat( [
-                               [ DIFF_EQUAL, midCommon ]
-                       ], diffsB );
-               }
-
-               if ( checklines && text1.length > 100 && text2.length > 100 ) {
-                       return this.diffLineMode( text1, text2, deadline );
-               }
-
-               return this.diffBisect( text1, text2, deadline );
-       };
-
-       /**
-        * Do the two texts share a substring which is at least half the length of the
-        * longer text?
-        * This speedup can produce non-minimal diffs.
-        * @param {string} text1 First string.
-        * @param {string} text2 Second string.
-        * @return {Array.<string>} Five element Array, containing the prefix of
-        *     text1, the suffix of text1, the prefix of text2, the suffix of
-        *     text2 and the common middle.  Or null if there was no match.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffHalfMatch = function( text1, text2 ) {
-               var longtext, shorttext, dmp,
-                       text1A, text2B, text2A, text1B, midCommon,
-                       hm1, hm2, hm;
-
-               longtext = text1.length > text2.length ? text1 : text2;
-               shorttext = text1.length > text2.length ? text2 : text1;
-               if ( longtext.length < 4 || shorttext.length * 2 < longtext.length ) {
-                       return null; // Pointless.
-               }
-               dmp = this; // 'this' becomes 'window' in a closure.
-
-               /**
-                * Does a substring of shorttext exist within longtext such that the substring
-                * is at least half the length of longtext?
-                * Closure, but does not reference any external variables.
-                * @param {string} longtext Longer string.
-                * @param {string} shorttext Shorter string.
-                * @param {number} i Start index of quarter length substring within longtext.
-                * @return {Array.<string>} Five element Array, containing the prefix of
-                *     longtext, the suffix of longtext, the prefix of shorttext, the suffix
-                *     of shorttext and the common middle.  Or null if there was no match.
-                * @private
-                */
-               function diffHalfMatchI( longtext, shorttext, i ) {
-                       var seed, j, bestCommon, prefixLength, suffixLength,
-                               bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB;
-
-                       // Start with a 1/4 length substring at position i as a seed.
-                       seed = longtext.substring( i, i + Math.floor( longtext.length / 4 ) );
-                       j = -1;
-                       bestCommon = "";
-                       while ( ( j = shorttext.indexOf( seed, j + 1 ) ) !== -1 ) {
-                               prefixLength = dmp.diffCommonPrefix( longtext.substring( i ),
-                                       shorttext.substring( j ) );
-                               suffixLength = dmp.diffCommonSuffix( longtext.substring( 0, i ),
-                                       shorttext.substring( 0, j ) );
-                               if ( bestCommon.length < suffixLength + prefixLength ) {
-                                       bestCommon = shorttext.substring( j - suffixLength, j ) +
-                                               shorttext.substring( j, j + prefixLength );
-                                       bestLongtextA = longtext.substring( 0, i - suffixLength );
-                                       bestLongtextB = longtext.substring( i + prefixLength );
-                                       bestShorttextA = shorttext.substring( 0, j - suffixLength );
-                                       bestShorttextB = shorttext.substring( j + prefixLength );
-                               }
-                       }
-                       if ( bestCommon.length * 2 >= longtext.length ) {
-                               return [ bestLongtextA, bestLongtextB,
-                                       bestShorttextA, bestShorttextB, bestCommon
-                               ];
-                       } else {
-                               return null;
-                       }
-               }
-
-               // First check if the second quarter is the seed for a half-match.
-               hm1 = diffHalfMatchI( longtext, shorttext,
-                       Math.ceil( longtext.length / 4 ) );
-
-               // Check again based on the third quarter.
-               hm2 = diffHalfMatchI( longtext, shorttext,
-                       Math.ceil( longtext.length / 2 ) );
-               if ( !hm1 && !hm2 ) {
-                       return null;
-               } else if ( !hm2 ) {
-                       hm = hm1;
-               } else if ( !hm1 ) {
-                       hm = hm2;
-               } else {
-
-                       // Both matched.  Select the longest.
-                       hm = hm1[ 4 ].length > hm2[ 4 ].length ? hm1 : hm2;
-               }
-
-               // A half-match was found, sort out the return data.
-               text1A, text1B, text2A, text2B;
-               if ( text1.length > text2.length ) {
-                       text1A = hm[ 0 ];
-                       text1B = hm[ 1 ];
-                       text2A = hm[ 2 ];
-                       text2B = hm[ 3 ];
-               } else {
-                       text2A = hm[ 0 ];
-                       text2B = hm[ 1 ];
-                       text1A = hm[ 2 ];
-                       text1B = hm[ 3 ];
-               }
-               midCommon = hm[ 4 ];
-               return [ text1A, text1B, text2A, text2B, midCommon ];
-       };
-
-       /**
-        * Do a quick line-level diff on both strings, then rediff the parts for
-        * greater accuracy.
-        * This speedup can produce non-minimal diffs.
-        * @param {string} text1 Old string to be diffed.
-        * @param {string} text2 New string to be diffed.
-        * @param {number} deadline Time when the diff should be complete by.
-        * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffLineMode = function( text1, text2, deadline ) {
-               var a, diffs, linearray, pointer, countInsert,
-                       countDelete, textInsert, textDelete, j;
-
-               // Scan the text on a line-by-line basis first.
-               a = this.diffLinesToChars( text1, text2 );
-               text1 = a.chars1;
-               text2 = a.chars2;
-               linearray = a.lineArray;
-
-               diffs = this.DiffMain( text1, text2, false, deadline );
-
-               // Convert the diff back to original text.
-               this.diffCharsToLines( diffs, linearray );
-
-               // Eliminate freak matches (e.g. blank lines)
-               this.diffCleanupSemantic( diffs );
-
-               // Rediff any replacement blocks, this time character-by-character.
-               // Add a dummy entry at the end.
-               diffs.push( [ DIFF_EQUAL, "" ] );
-               pointer = 0;
-               countDelete = 0;
-               countInsert = 0;
-               textDelete = "";
-               textInsert = "";
-               while ( pointer < diffs.length ) {
-                       switch ( diffs[ pointer ][ 0 ] ) {
-                       case DIFF_INSERT:
-                               countInsert++;
-                               textInsert += diffs[ pointer ][ 1 ];
-                               break;
-                       case DIFF_DELETE:
-                               countDelete++;
-                               textDelete += diffs[ pointer ][ 1 ];
-                               break;
-                       case DIFF_EQUAL:
-
-                               // Upon reaching an equality, check for prior redundancies.
-                               if ( countDelete >= 1 && countInsert >= 1 ) {
-
-                                       // Delete the offending records and add the merged ones.
-                                       diffs.splice( pointer - countDelete - countInsert,
-                                               countDelete + countInsert );
-                                       pointer = pointer - countDelete - countInsert;
-                                       a = this.DiffMain( textDelete, textInsert, false, deadline );
-                                       for ( j = a.length - 1; j >= 0; j-- ) {
-                                               diffs.splice( pointer, 0, a[ j ] );
-                                       }
-                                       pointer = pointer + a.length;
-                               }
-                               countInsert = 0;
-                               countDelete = 0;
-                               textDelete = "";
-                               textInsert = "";
-                               break;
-                       }
-                       pointer++;
-               }
-               diffs.pop(); // Remove the dummy entry at the end.
-
-               return diffs;
-       };
-
-       /**
-        * Find the 'middle snake' of a diff, split the problem in two
-        * and return the recursively constructed diff.
-        * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
-        * @param {string} text1 Old string to be diffed.
-        * @param {string} text2 New string to be diffed.
-        * @param {number} deadline Time at which to bail if not yet complete.
-        * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffBisect = function( text1, text2, deadline ) {
-               var text1Length, text2Length, maxD, vOffset, vLength,
-                       v1, v2, x, delta, front, k1start, k1end, k2start,
-                       k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2;
-
-               // Cache the text lengths to prevent multiple calls.
-               text1Length = text1.length;
-               text2Length = text2.length;
-               maxD = Math.ceil( ( text1Length + text2Length ) / 2 );
-               vOffset = maxD;
-               vLength = 2 * maxD;
-               v1 = new Array( vLength );
-               v2 = new Array( vLength );
-
-               // Setting all elements to -1 is faster in Chrome & Firefox than mixing
-               // integers and undefined.
-               for ( x = 0; x < vLength; x++ ) {
-                       v1[ x ] = -1;
-                       v2[ x ] = -1;
-               }
-               v1[ vOffset + 1 ] = 0;
-               v2[ vOffset + 1 ] = 0;
-               delta = text1Length - text2Length;
-
-               // If the total number of characters is odd, then the front path will collide
-               // with the reverse path.
-               front = ( delta % 2 !== 0 );
-
-               // Offsets for start and end of k loop.
-               // Prevents mapping of space beyond the grid.
-               k1start = 0;
-               k1end = 0;
-               k2start = 0;
-               k2end = 0;
-               for ( d = 0; d < maxD; d++ ) {
-
-                       // Bail out if deadline is reached.
-                       if ( ( new Date() ).getTime() > deadline ) {
-                               break;
-                       }
-
-                       // Walk the front path one step.
-                       for ( k1 = -d + k1start; k1 <= d - k1end; k1 += 2 ) {
-                               k1Offset = vOffset + k1;
-                               if ( k1 === -d || ( k1 !== d && v1[ k1Offset - 1 ] < v1[ k1Offset + 1 ] ) ) {
-                                       x1 = v1[ k1Offset + 1 ];
-                               } else {
-                                       x1 = v1[ k1Offset - 1 ] + 1;
-                               }
-                               y1 = x1 - k1;
-                               while ( x1 < text1Length && y1 < text2Length &&
-                                       text1.charAt( x1 ) === text2.charAt( y1 ) ) {
-                                       x1++;
-                                       y1++;
-                               }
-                               v1[ k1Offset ] = x1;
-                               if ( x1 > text1Length ) {
-
-                                       // Ran off the right of the graph.
-                                       k1end += 2;
-                               } else if ( y1 > text2Length ) {
-
-                                       // Ran off the bottom of the graph.
-                                       k1start += 2;
-                               } else if ( front ) {
-                                       k2Offset = vOffset + delta - k1;
-                                       if ( k2Offset >= 0 && k2Offset < vLength && v2[ k2Offset ] !== -1 ) {
-
-                                               // Mirror x2 onto top-left coordinate system.
-                                               x2 = text1Length - v2[ k2Offset ];
-                                               if ( x1 >= x2 ) {
-
-                                                       // Overlap detected.
-                                                       return this.diffBisectSplit( text1, text2, x1, y1, deadline );
-                                               }
-                                       }
-                               }
-                       }
-
-                       // Walk the reverse path one step.
-                       for ( k2 = -d + k2start; k2 <= d - k2end; k2 += 2 ) {
-                               k2Offset = vOffset + k2;
-                               if ( k2 === -d || ( k2 !== d && v2[ k2Offset - 1 ] < v2[ k2Offset + 1 ] ) ) {
-                                       x2 = v2[ k2Offset + 1 ];
-                               } else {
-                                       x2 = v2[ k2Offset - 1 ] + 1;
-                               }
-                               y2 = x2 - k2;
-                               while ( x2 < text1Length && y2 < text2Length &&
-                                       text1.charAt( text1Length - x2 - 1 ) ===
-                                       text2.charAt( text2Length - y2 - 1 ) ) {
-                                       x2++;
-                                       y2++;
-                               }
-                               v2[ k2Offset ] = x2;
-                               if ( x2 > text1Length ) {
-
-                                       // Ran off the left of the graph.
-                                       k2end += 2;
-                               } else if ( y2 > text2Length ) {
-
-                                       // Ran off the top of the graph.
-                                       k2start += 2;
-                               } else if ( !front ) {
-                                       k1Offset = vOffset + delta - k2;
-                                       if ( k1Offset >= 0 && k1Offset < vLength && v1[ k1Offset ] !== -1 ) {
-                                               x1 = v1[ k1Offset ];
-                                               y1 = vOffset + x1 - k1Offset;
-
-                                               // Mirror x2 onto top-left coordinate system.
-                                               x2 = text1Length - x2;
-                                               if ( x1 >= x2 ) {
-
-                                                       // Overlap detected.
-                                                       return this.diffBisectSplit( text1, text2, x1, y1, deadline );
-                                               }
-                                       }
-                               }
-                       }
-               }
-
-               // Diff took too long and hit the deadline or
-               // number of diffs equals number of characters, no commonality at all.
-               return [
-                       [ DIFF_DELETE, text1 ],
-                       [ DIFF_INSERT, text2 ]
-               ];
-       };
-
-       /**
-        * Given the location of the 'middle snake', split the diff in two parts
-        * and recurse.
-        * @param {string} text1 Old string to be diffed.
-        * @param {string} text2 New string to be diffed.
-        * @param {number} x Index of split point in text1.
-        * @param {number} y Index of split point in text2.
-        * @param {number} deadline Time at which to bail if not yet complete.
-        * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffBisectSplit = function( text1, text2, x, y, deadline ) {
-               var text1a, text1b, text2a, text2b, diffs, diffsb;
-               text1a = text1.substring( 0, x );
-               text2a = text2.substring( 0, y );
-               text1b = text1.substring( x );
-               text2b = text2.substring( y );
-
-               // Compute both diffs serially.
-               diffs = this.DiffMain( text1a, text2a, false, deadline );
-               diffsb = this.DiffMain( text1b, text2b, false, deadline );
-
-               return diffs.concat( diffsb );
-       };
-
-       /**
-        * Reduce the number of edits by eliminating semantically trivial equalities.
-        * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
-        */
-       DiffMatchPatch.prototype.diffCleanupSemantic = function( diffs ) {
-               var changes, equalities, equalitiesLength, lastequality,
-                       pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1,
-                       lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2;
-               changes = false;
-               equalities = []; // Stack of indices where equalities are found.
-               equalitiesLength = 0; // Keeping our own length var is faster in JS.
-               /** @type {?string} */
-               lastequality = null;
-
-               // Always equal to diffs[equalities[equalitiesLength - 1]][1]
-               pointer = 0; // Index of current position.
-
-               // Number of characters that changed prior to the equality.
-               lengthInsertions1 = 0;
-               lengthDeletions1 = 0;
-
-               // Number of characters that changed after the equality.
-               lengthInsertions2 = 0;
-               lengthDeletions2 = 0;
-               while ( pointer < diffs.length ) {
-                       if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) { // Equality found.
-                               equalities[ equalitiesLength++ ] = pointer;
-                               lengthInsertions1 = lengthInsertions2;
-                               lengthDeletions1 = lengthDeletions2;
-                               lengthInsertions2 = 0;
-                               lengthDeletions2 = 0;
-                               lastequality = diffs[ pointer ][ 1 ];
-                       } else { // An insertion or deletion.
-                               if ( diffs[ pointer ][ 0 ] === DIFF_INSERT ) {
-                                       lengthInsertions2 += diffs[ pointer ][ 1 ].length;
-                               } else {
-                                       lengthDeletions2 += diffs[ pointer ][ 1 ].length;
-                               }
-
-                               // Eliminate an equality that is smaller or equal to the edits on both
-                               // sides of it.
-                               if ( lastequality && ( lastequality.length <=
-                                               Math.max( lengthInsertions1, lengthDeletions1 ) ) &&
-                                               ( lastequality.length <= Math.max( lengthInsertions2,
-                                                       lengthDeletions2 ) ) ) {
-
-                                       // Duplicate record.
-                                       diffs.splice(
-                                               equalities[ equalitiesLength - 1 ],
-                                               0,
-                                               [ DIFF_DELETE, lastequality ]
-                                       );
-
-                                       // Change second copy to insert.
-                                       diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT;
-
-                                       // Throw away the equality we just deleted.
-                                       equalitiesLength--;
-
-                                       // Throw away the previous equality (it needs to be reevaluated).
-                                       equalitiesLength--;
-                                       pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1;
-
-                                       // Reset the counters.
-                                       lengthInsertions1 = 0;
-                                       lengthDeletions1 = 0;
-                                       lengthInsertions2 = 0;
-                                       lengthDeletions2 = 0;
-                                       lastequality = null;
-                                       changes = true;
-                               }
-                       }
-                       pointer++;
-               }
-
-               // Normalize the diff.
-               if ( changes ) {
-                       this.diffCleanupMerge( diffs );
-               }
-
-               // Find any overlaps between deletions and insertions.
-               // e.g: <del>abcxxx</del><ins>xxxdef</ins>
-               //   -> <del>abc</del>xxx<ins>def</ins>
-               // e.g: <del>xxxabc</del><ins>defxxx</ins>
-               //   -> <ins>def</ins>xxx<del>abc</del>
-               // Only extract an overlap if it is as big as the edit ahead or behind it.
-               pointer = 1;
-               while ( pointer < diffs.length ) {
-                       if ( diffs[ pointer - 1 ][ 0 ] === DIFF_DELETE &&
-                                       diffs[ pointer ][ 0 ] === DIFF_INSERT ) {
-                               deletion = diffs[ pointer - 1 ][ 1 ];
-                               insertion = diffs[ pointer ][ 1 ];
-                               overlapLength1 = this.diffCommonOverlap( deletion, insertion );
-                               overlapLength2 = this.diffCommonOverlap( insertion, deletion );
-                               if ( overlapLength1 >= overlapLength2 ) {
-                                       if ( overlapLength1 >= deletion.length / 2 ||
-                                                       overlapLength1 >= insertion.length / 2 ) {
-
-                                               // Overlap found.  Insert an equality and trim the surrounding edits.
-                                               diffs.splice(
-                                                       pointer,
-                                                       0,
-                                                       [ DIFF_EQUAL, insertion.substring( 0, overlapLength1 ) ]
-                                               );
-                                               diffs[ pointer - 1 ][ 1 ] =
-                                                       deletion.substring( 0, deletion.length - overlapLength1 );
-                                               diffs[ pointer + 1 ][ 1 ] = insertion.substring( overlapLength1 );
-                                               pointer++;
-                                       }
-                               } else {
-                                       if ( overlapLength2 >= deletion.length / 2 ||
-                                                       overlapLength2 >= insertion.length / 2 ) {
-
-                                               // Reverse overlap found.
-                                               // Insert an equality and swap and trim the surrounding edits.
-                                               diffs.splice(
-                                                       pointer,
-                                                       0,
-                                                       [ DIFF_EQUAL, deletion.substring( 0, overlapLength2 ) ]
-                                               );
-
-                                               diffs[ pointer - 1 ][ 0 ] = DIFF_INSERT;
-                                               diffs[ pointer - 1 ][ 1 ] =
-                                                       insertion.substring( 0, insertion.length - overlapLength2 );
-                                               diffs[ pointer + 1 ][ 0 ] = DIFF_DELETE;
-                                               diffs[ pointer + 1 ][ 1 ] =
-                                                       deletion.substring( overlapLength2 );
-                                               pointer++;
-                                       }
-                               }
-                               pointer++;
-                       }
-                       pointer++;
-               }
-       };
-
-       /**
-        * Determine if the suffix of one string is the prefix of another.
-        * @param {string} text1 First string.
-        * @param {string} text2 Second string.
-        * @return {number} The number of characters common to the end of the first
-        *     string and the start of the second string.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffCommonOverlap = function( text1, text2 ) {
-               var text1Length, text2Length, textLength,
-                       best, length, pattern, found;
-
-               // Cache the text lengths to prevent multiple calls.
-               text1Length = text1.length;
-               text2Length = text2.length;
-
-               // Eliminate the null case.
-               if ( text1Length === 0 || text2Length === 0 ) {
-                       return 0;
-               }
-
-               // Truncate the longer string.
-               if ( text1Length > text2Length ) {
-                       text1 = text1.substring( text1Length - text2Length );
-               } else if ( text1Length < text2Length ) {
-                       text2 = text2.substring( 0, text1Length );
-               }
-               textLength = Math.min( text1Length, text2Length );
-
-               // Quick check for the worst case.
-               if ( text1 === text2 ) {
-                       return textLength;
-               }
-
-               // Start by looking for a single character match
-               // and increase length until no match is found.
-               // Performance analysis: https://neil.fraser.name/news/2010/11/04/
-               best = 0;
-               length = 1;
-               while ( true ) {
-                       pattern = text1.substring( textLength - length );
-                       found = text2.indexOf( pattern );
-                       if ( found === -1 ) {
-                               return best;
-                       }
-                       length += found;
-                       if ( found === 0 || text1.substring( textLength - length ) ===
-                                       text2.substring( 0, length ) ) {
-                               best = length;
-                               length++;
-                       }
-               }
-       };
-
-       /**
-        * Split two texts into an array of strings.  Reduce the texts to a string of
-        * hashes where each Unicode character represents one line.
-        * @param {string} text1 First string.
-        * @param {string} text2 Second string.
-        * @return {{chars1: string, chars2: string, lineArray: !Array.<string>}}
-        *     An object containing the encoded text1, the encoded text2 and
-        *     the array of unique strings.
-        *     The zeroth element of the array of unique strings is intentionally blank.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffLinesToChars = function( text1, text2 ) {
-               var lineArray, lineHash, chars1, chars2;
-               lineArray = []; // E.g. lineArray[4] === 'Hello\n'
-               lineHash = {};  // E.g. lineHash['Hello\n'] === 4
-
-               // '\x00' is a valid character, but various debuggers don't like it.
-               // So we'll insert a junk entry to avoid generating a null character.
-               lineArray[ 0 ] = "";
-
-               /**
-                * Split a text into an array of strings.  Reduce the texts to a string of
-                * hashes where each Unicode character represents one line.
-                * Modifies linearray and linehash through being a closure.
-                * @param {string} text String to encode.
-                * @return {string} Encoded string.
-                * @private
-                */
-               function diffLinesToCharsMunge( text ) {
-                       var chars, lineStart, lineEnd, lineArrayLength, line;
-                       chars = "";
-
-                       // Walk the text, pulling out a substring for each line.
-                       // text.split('\n') would would temporarily double our memory footprint.
-                       // Modifying text would create many large strings to garbage collect.
-                       lineStart = 0;
-                       lineEnd = -1;
-
-                       // Keeping our own length variable is faster than looking it up.
-                       lineArrayLength = lineArray.length;
-                       while ( lineEnd < text.length - 1 ) {
-                               lineEnd = text.indexOf( "\n", lineStart );
-                               if ( lineEnd === -1 ) {
-                                       lineEnd = text.length - 1;
-                               }
-                               line = text.substring( lineStart, lineEnd + 1 );
-                               lineStart = lineEnd + 1;
-
-                               if ( lineHash.hasOwnProperty ? lineHash.hasOwnProperty( line ) :
-                                                       ( lineHash[ line ] !== undefined ) ) {
-                                       chars += String.fromCharCode( lineHash[ line ] );
-                               } else {
-                                       chars += String.fromCharCode( lineArrayLength );
-                                       lineHash[ line ] = lineArrayLength;
-                                       lineArray[ lineArrayLength++ ] = line;
-                               }
-                       }
-                       return chars;
-               }
-
-               chars1 = diffLinesToCharsMunge( text1 );
-               chars2 = diffLinesToCharsMunge( text2 );
-               return {
-                       chars1: chars1,
-                       chars2: chars2,
-                       lineArray: lineArray
-               };
-       };
-
-       /**
-        * Rehydrate the text in a diff from a string of line hashes to real lines of
-        * text.
-        * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
-        * @param {!Array.<string>} lineArray Array of unique strings.
-        * @private
-        */
-       DiffMatchPatch.prototype.diffCharsToLines = function( diffs, lineArray ) {
-               var x, chars, text, y;
-               for ( x = 0; x < diffs.length; x++ ) {
-                       chars = diffs[ x ][ 1 ];
-                       text = [];
-                       for ( y = 0; y < chars.length; y++ ) {
-                               text[ y ] = lineArray[ chars.charCodeAt( y ) ];
-                       }
-                       diffs[ x ][ 1 ] = text.join( "" );
-               }
-       };
-
-       /**
-        * Reorder and merge like edit sections.  Merge equalities.
-        * Any edit section can move as long as it doesn't cross an equality.
-        * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
-        */
-       DiffMatchPatch.prototype.diffCleanupMerge = function( diffs ) {
-               var pointer, countDelete, countInsert, textInsert, textDelete,
-                       commonlength, changes, diffPointer, position;
-               diffs.push( [ DIFF_EQUAL, "" ] ); // Add a dummy entry at the end.
-               pointer = 0;
-               countDelete = 0;
-               countInsert = 0;
-               textDelete = "";
-               textInsert = "";
-               commonlength;
-               while ( pointer < diffs.length ) {
-                       switch ( diffs[ pointer ][ 0 ] ) {
-                       case DIFF_INSERT:
-                               countInsert++;
-                               textInsert += diffs[ pointer ][ 1 ];
-                               pointer++;
-                               break;
-                       case DIFF_DELETE:
-                               countDelete++;
-                               textDelete += diffs[ pointer ][ 1 ];
-                               pointer++;
-                               break;
-                       case DIFF_EQUAL:
-
-                               // Upon reaching an equality, check for prior redundancies.
-                               if ( countDelete + countInsert > 1 ) {
-                                       if ( countDelete !== 0 && countInsert !== 0 ) {
-
-                                               // Factor out any common prefixes.
-                                               commonlength = this.diffCommonPrefix( textInsert, textDelete );
-                                               if ( commonlength !== 0 ) {
-                                                       if ( ( pointer - countDelete - countInsert ) > 0 &&
-                                                                       diffs[ pointer - countDelete - countInsert - 1 ][ 0 ] ===
-                                                                       DIFF_EQUAL ) {
-                                                               diffs[ pointer - countDelete - countInsert - 1 ][ 1 ] +=
-                                                                       textInsert.substring( 0, commonlength );
-                                                       } else {
-                                                               diffs.splice( 0, 0, [ DIFF_EQUAL,
-                                                                       textInsert.substring( 0, commonlength )
-                                                               ] );
-                                                               pointer++;
-                                                       }
-                                                       textInsert = textInsert.substring( commonlength );
-                                                       textDelete = textDelete.substring( commonlength );
-                                               }
-
-                                               // Factor out any common suffixies.
-                                               commonlength = this.diffCommonSuffix( textInsert, textDelete );
-                                               if ( commonlength !== 0 ) {
-                                                       diffs[ pointer ][ 1 ] = textInsert.substring( textInsert.length -
-                                                                       commonlength ) + diffs[ pointer ][ 1 ];
-                                                       textInsert = textInsert.substring( 0, textInsert.length -
-                                                               commonlength );
-                                                       textDelete = textDelete.substring( 0, textDelete.length -
-                                                               commonlength );
-                                               }
-                                       }
-
-                                       // Delete the offending records and add the merged ones.
-                                       if ( countDelete === 0 ) {
-                                               diffs.splice( pointer - countInsert,
-                                                       countDelete + countInsert, [ DIFF_INSERT, textInsert ] );
-                                       } else if ( countInsert === 0 ) {
-                                               diffs.splice( pointer - countDelete,
-                                                       countDelete + countInsert, [ DIFF_DELETE, textDelete ] );
-                                       } else {
-                                               diffs.splice(
-                                                       pointer - countDelete - countInsert,
-                                                       countDelete + countInsert,
-                                                       [ DIFF_DELETE, textDelete ], [ DIFF_INSERT, textInsert ]
-                                               );
-                                       }
-                                       pointer = pointer - countDelete - countInsert +
-                                               ( countDelete ? 1 : 0 ) + ( countInsert ? 1 : 0 ) + 1;
-                               } else if ( pointer !== 0 && diffs[ pointer - 1 ][ 0 ] === DIFF_EQUAL ) {
-
-                                       // Merge this equality with the previous one.
-                                       diffs[ pointer - 1 ][ 1 ] += diffs[ pointer ][ 1 ];
-                                       diffs.splice( pointer, 1 );
-                               } else {
-                                       pointer++;
-                               }
-                               countInsert = 0;
-                               countDelete = 0;
-                               textDelete = "";
-                               textInsert = "";
-                               break;
-                       }
-               }
-               if ( diffs[ diffs.length - 1 ][ 1 ] === "" ) {
-                       diffs.pop(); // Remove the dummy entry at the end.
-               }
-
-               // Second pass: look for single edits surrounded on both sides by equalities
-               // which can be shifted sideways to eliminate an equality.
-               // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
-               changes = false;
-               pointer = 1;
-
-               // Intentionally ignore the first and last element (don't need checking).
-               while ( pointer < diffs.length - 1 ) {
-                       if ( diffs[ pointer - 1 ][ 0 ] === DIFF_EQUAL &&
-                                       diffs[ pointer + 1 ][ 0 ] === DIFF_EQUAL ) {
-
-                               diffPointer = diffs[ pointer ][ 1 ];
-                               position = diffPointer.substring(
-                                       diffPointer.length - diffs[ pointer - 1 ][ 1 ].length
-                               );
-
-                               // This is a single edit surrounded by equalities.
-                               if ( position === diffs[ pointer - 1 ][ 1 ] ) {
-
-                                       // Shift the edit over the previous equality.
-                                       diffs[ pointer ][ 1 ] = diffs[ pointer - 1 ][ 1 ] +
-                                               diffs[ pointer ][ 1 ].substring( 0, diffs[ pointer ][ 1 ].length -
-                                                       diffs[ pointer - 1 ][ 1 ].length );
-                                       diffs[ pointer + 1 ][ 1 ] =
-                                               diffs[ pointer - 1 ][ 1 ] + diffs[ pointer + 1 ][ 1 ];
-                                       diffs.splice( pointer - 1, 1 );
-                                       changes = true;
-                               } else if ( diffPointer.substring( 0, diffs[ pointer + 1 ][ 1 ].length ) ===
-                                               diffs[ pointer + 1 ][ 1 ] ) {
-
-                                       // Shift the edit over the next equality.
-                                       diffs[ pointer - 1 ][ 1 ] += diffs[ pointer + 1 ][ 1 ];
-                                       diffs[ pointer ][ 1 ] =
-                                               diffs[ pointer ][ 1 ].substring( diffs[ pointer + 1 ][ 1 ].length ) +
-                                               diffs[ pointer + 1 ][ 1 ];
-                                       diffs.splice( pointer + 1, 1 );
-                                       changes = true;
-                               }
-                       }
-                       pointer++;
-               }
-
-               // If shifts were made, the diff needs reordering and another shift sweep.
-               if ( changes ) {
-                       this.diffCleanupMerge( diffs );
-               }
-       };
-
-       return function( o, n ) {
-               var diff, output, text;
-               diff = new DiffMatchPatch();
-               output = diff.DiffMain( o, n );
-               diff.diffCleanupEfficiency( output );
-               text = diff.diffPrettyHtml( output );
-
-               return text;
-       };
-}() );
-
-}() );
+
+
+  var classCallCheck = function (instance, Constructor) {
+    if (!(instance instanceof Constructor)) {
+      throw new TypeError("Cannot call a class as a function");
+    }
+  };
+
+  var createClass = function () {
+    function defineProperties(target, props) {
+      for (var i = 0; i < props.length; i++) {
+        var descriptor = props[i];
+        descriptor.enumerable = descriptor.enumerable || false;
+        descriptor.configurable = true;
+        if ("value" in descriptor) descriptor.writable = true;
+        Object.defineProperty(target, descriptor.key, descriptor);
+      }
+    }
+
+    return function (Constructor, protoProps, staticProps) {
+      if (protoProps) defineProperties(Constructor.prototype, protoProps);
+      if (staticProps) defineProperties(Constructor, staticProps);
+      return Constructor;
+    };
+  }();
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  var toConsumableArray = function (arr) {
+    if (Array.isArray(arr)) {
+      for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
+
+      return arr2;
+    } else {
+      return Array.from(arr);
+    }
+  };
+
+  var toString = Object.prototype.toString;
+  var hasOwn = Object.prototype.hasOwnProperty;
+  var now = Date.now || function () {
+       return new Date().getTime();
+  };
+
+  var defined = {
+       document: window && window.document !== undefined,
+       setTimeout: setTimeout !== undefined
+  };
+
+  // Returns a new Array with the elements that are in a but not in b
+  function diff(a, b) {
+       var i,
+           j,
+           result = a.slice();
+
+       for (i = 0; i < result.length; i++) {
+               for (j = 0; j < b.length; j++) {
+                       if (result[i] === b[j]) {
+                               result.splice(i, 1);
+                               i--;
+                               break;
+                       }
+               }
+       }
+       return result;
+  }
+
+  /**
+   * Determines whether an element exists in a given array or not.
+   *
+   * @method inArray
+   * @param {Any} elem
+   * @param {Array} array
+   * @return {Boolean}
+   */
+  function inArray(elem, array) {
+       return array.indexOf(elem) !== -1;
+  }
+
+  /**
+   * Makes a clone of an object using only Array or Object as base,
+   * and copies over the own enumerable properties.
+   *
+   * @param {Object} obj
+   * @return {Object} New object with only the own properties (recursively).
+   */
+  function objectValues(obj) {
+       var key,
+           val,
+           vals = is("array", obj) ? [] : {};
+       for (key in obj) {
+               if (hasOwn.call(obj, key)) {
+                       val = obj[key];
+                       vals[key] = val === Object(val) ? objectValues(val) : val;
+               }
+       }
+       return vals;
+  }
+
+  function extend(a, b, undefOnly) {
+       for (var prop in b) {
+               if (hasOwn.call(b, prop)) {
+                       if (b[prop] === undefined) {
+                               delete a[prop];
+                       } else if (!(undefOnly && typeof a[prop] !== "undefined")) {
+                               a[prop] = b[prop];
+                       }
+               }
+       }
+
+       return a;
+  }
+
+  function objectType(obj) {
+       if (typeof obj === "undefined") {
+               return "undefined";
+       }
+
+       // Consider: typeof null === object
+       if (obj === null) {
+               return "null";
+       }
+
+       var match = toString.call(obj).match(/^\[object\s(.*)\]$/),
+           type = match && match[1];
+
+       switch (type) {
+               case "Number":
+                       if (isNaN(obj)) {
+                               return "nan";
+                       }
+                       return "number";
+               case "String":
+               case "Boolean":
+               case "Array":
+               case "Set":
+               case "Map":
+               case "Date":
+               case "RegExp":
+               case "Function":
+               case "Symbol":
+                       return type.toLowerCase();
+               default:
+                       return typeof obj === "undefined" ? "undefined" : _typeof(obj);
+       }
+  }
+
+  // Safe object type checking
+  function is(type, obj) {
+       return objectType(obj) === type;
+  }
+
+  // Based on Java's String.hashCode, a simple but not
+  // rigorously collision resistant hashing function
+  function generateHash(module, testName) {
+       var str = module + "\x1C" + testName;
+       var hash = 0;
+
+       for (var i = 0; i < str.length; i++) {
+               hash = (hash << 5) - hash + str.charCodeAt(i);
+               hash |= 0;
+       }
+
+       // Convert the possibly negative integer hash code into an 8 character hex string, which isn't
+       // strictly necessary but increases user understanding that the id is a SHA-like hash
+       var hex = (0x100000000 + hash).toString(16);
+       if (hex.length < 8) {
+               hex = "0000000" + hex;
+       }
+
+       return hex.slice(-8);
+  }
+
+  // Test for equality any JavaScript type.
+  // Authors: Philippe Rathé <prathe@gmail.com>, David Chan <david@troi.org>
+  var equiv = (function () {
+
+       // Value pairs queued for comparison. Used for breadth-first processing order, recursion
+       // detection and avoiding repeated comparison (see below for details).
+       // Elements are { a: val, b: val }.
+       var pairs = [];
+
+       var getProto = Object.getPrototypeOf || function (obj) {
+               return obj.__proto__;
+       };
+
+       function useStrictEquality(a, b) {
+
+               // This only gets called if a and b are not strict equal, and is used to compare on
+               // the primitive values inside object wrappers. For example:
+               // `var i = 1;`
+               // `var j = new Number(1);`
+               // Neither a nor b can be null, as a !== b and they have the same type.
+               if ((typeof a === "undefined" ? "undefined" : _typeof(a)) === "object") {
+                       a = a.valueOf();
+               }
+               if ((typeof b === "undefined" ? "undefined" : _typeof(b)) === "object") {
+                       b = b.valueOf();
+               }
+
+               return a === b;
+       }
+
+       function compareConstructors(a, b) {
+               var protoA = getProto(a);
+               var protoB = getProto(b);
+
+               // Comparing constructors is more strict than using `instanceof`
+               if (a.constructor === b.constructor) {
+                       return true;
+               }
+
+               // Ref #851
+               // If the obj prototype descends from a null constructor, treat it
+               // as a null prototype.
+               if (protoA && protoA.constructor === null) {
+                       protoA = null;
+               }
+               if (protoB && protoB.constructor === null) {
+                       protoB = null;
+               }
+
+               // Allow objects with no prototype to be equivalent to
+               // objects with Object as their constructor.
+               if (protoA === null && protoB === Object.prototype || protoB === null && protoA === Object.prototype) {
+                       return true;
+               }
+
+               return false;
+       }
+
+       function getRegExpFlags(regexp) {
+               return "flags" in regexp ? regexp.flags : regexp.toString().match(/[gimuy]*$/)[0];
+       }
+
+       function isContainer(val) {
+               return ["object", "array", "map", "set"].indexOf(objectType(val)) !== -1;
+       }
+
+       function breadthFirstCompareChild(a, b) {
+
+               // If a is a container not reference-equal to b, postpone the comparison to the
+               // end of the pairs queue -- unless (a, b) has been seen before, in which case skip
+               // over the pair.
+               if (a === b) {
+                       return true;
+               }
+               if (!isContainer(a)) {
+                       return typeEquiv(a, b);
+               }
+               if (pairs.every(function (pair) {
+                       return pair.a !== a || pair.b !== b;
+               })) {
+
+                       // Not yet started comparing this pair
+                       pairs.push({ a: a, b: b });
+               }
+               return true;
+       }
+
+       var callbacks = {
+               "string": useStrictEquality,
+               "boolean": useStrictEquality,
+               "number": useStrictEquality,
+               "null": useStrictEquality,
+               "undefined": useStrictEquality,
+               "symbol": useStrictEquality,
+               "date": useStrictEquality,
+
+               "nan": function nan() {
+                       return true;
+               },
+
+               "regexp": function regexp(a, b) {
+                       return a.source === b.source &&
+
+                       // Include flags in the comparison
+                       getRegExpFlags(a) === getRegExpFlags(b);
+               },
+
+               // abort (identical references / instance methods were skipped earlier)
+               "function": function _function() {
+                       return false;
+               },
+
+               "array": function array(a, b) {
+                       var i, len;
+
+                       len = a.length;
+                       if (len !== b.length) {
+
+                               // Safe and faster
+                               return false;
+                       }
+
+                       for (i = 0; i < len; i++) {
+
+                               // Compare non-containers; queue non-reference-equal containers
+                               if (!breadthFirstCompareChild(a[i], b[i])) {
+                                       return false;
+                               }
+                       }
+                       return true;
+               },
+
+               // Define sets a and b to be equivalent if for each element aVal in a, there
+               // is some element bVal in b such that aVal and bVal are equivalent. Element
+               // repetitions are not counted, so these are equivalent:
+               // a = new Set( [ {}, [], [] ] );
+               // b = new Set( [ {}, {}, [] ] );
+               "set": function set$$1(a, b) {
+                       var innerEq,
+                           outerEq = true;
+
+                       if (a.size !== b.size) {
+
+                               // This optimization has certain quirks because of the lack of
+                               // repetition counting. For instance, adding the same
+                               // (reference-identical) element to two equivalent sets can
+                               // make them non-equivalent.
+                               return false;
+                       }
+
+                       a.forEach(function (aVal) {
+
+                               // Short-circuit if the result is already known. (Using for...of
+                               // with a break clause would be cleaner here, but it would cause
+                               // a syntax error on older Javascript implementations even if
+                               // Set is unused)
+                               if (!outerEq) {
+                                       return;
+                               }
+
+                               innerEq = false;
+
+                               b.forEach(function (bVal) {
+                                       var parentPairs;
+
+                                       // Likewise, short-circuit if the result is already known
+                                       if (innerEq) {
+                                               return;
+                                       }
+
+                                       // Swap out the global pairs list, as the nested call to
+                                       // innerEquiv will clobber its contents
+                                       parentPairs = pairs;
+                                       if (innerEquiv(bVal, aVal)) {
+                                               innerEq = true;
+                                       }
+
+                                       // Replace the global pairs list
+                                       pairs = parentPairs;
+                               });
+
+                               if (!innerEq) {
+                                       outerEq = false;
+                               }
+                       });
+
+                       return outerEq;
+               },
+
+               // Define maps a and b to be equivalent if for each key-value pair (aKey, aVal)
+               // in a, there is some key-value pair (bKey, bVal) in b such that
+               // [ aKey, aVal ] and [ bKey, bVal ] are equivalent. Key repetitions are not
+               // counted, so these are equivalent:
+               // a = new Map( [ [ {}, 1 ], [ {}, 1 ], [ [], 1 ] ] );
+               // b = new Map( [ [ {}, 1 ], [ [], 1 ], [ [], 1 ] ] );
+               "map": function map(a, b) {
+                       var innerEq,
+                           outerEq = true;
+
+                       if (a.size !== b.size) {
+
+                               // This optimization has certain quirks because of the lack of
+                               // repetition counting. For instance, adding the same
+                               // (reference-identical) key-value pair to two equivalent maps
+                               // can make them non-equivalent.
+                               return false;
+                       }
+
+                       a.forEach(function (aVal, aKey) {
+
+                               // Short-circuit if the result is already known. (Using for...of
+                               // with a break clause would be cleaner here, but it would cause
+                               // a syntax error on older Javascript implementations even if
+                               // Map is unused)
+                               if (!outerEq) {
+                                       return;
+                               }
+
+                               innerEq = false;
+
+                               b.forEach(function (bVal, bKey) {
+                                       var parentPairs;
+
+                                       // Likewise, short-circuit if the result is already known
+                                       if (innerEq) {
+                                               return;
+                                       }
+
+                                       // Swap out the global pairs list, as the nested call to
+                                       // innerEquiv will clobber its contents
+                                       parentPairs = pairs;
+                                       if (innerEquiv([bVal, bKey], [aVal, aKey])) {
+                                               innerEq = true;
+                                       }
+
+                                       // Replace the global pairs list
+                                       pairs = parentPairs;
+                               });
+
+                               if (!innerEq) {
+                                       outerEq = false;
+                               }
+                       });
+
+                       return outerEq;
+               },
+
+               "object": function object(a, b) {
+                       var i,
+                           aProperties = [],
+                           bProperties = [];
+
+                       if (compareConstructors(a, b) === false) {
+                               return false;
+                       }
+
+                       // Be strict: don't ensure hasOwnProperty and go deep
+                       for (i in a) {
+
+                               // Collect a's properties
+                               aProperties.push(i);
+
+                               // Skip OOP methods that look the same
+                               if (a.constructor !== Object && typeof a.constructor !== "undefined" && typeof a[i] === "function" && typeof b[i] === "function" && a[i].toString() === b[i].toString()) {
+                                       continue;
+                               }
+
+                               // Compare non-containers; queue non-reference-equal containers
+                               if (!breadthFirstCompareChild(a[i], b[i])) {
+                                       return false;
+                               }
+                       }
+
+                       for (i in b) {
+
+                               // Collect b's properties
+                               bProperties.push(i);
+                       }
+
+                       // Ensures identical properties name
+                       return typeEquiv(aProperties.sort(), bProperties.sort());
+               }
+       };
+
+       function typeEquiv(a, b) {
+               var type = objectType(a);
+
+               // Callbacks for containers will append to the pairs queue to achieve breadth-first
+               // search order. The pairs queue is also used to avoid reprocessing any pair of
+               // containers that are reference-equal to a previously visited pair (a special case
+               // this being recursion detection).
+               //
+               // Because of this approach, once typeEquiv returns a false value, it should not be
+               // called again without clearing the pair queue else it may wrongly report a visited
+               // pair as being equivalent.
+               return objectType(b) === type && callbacks[type](a, b);
+       }
+
+       function innerEquiv(a, b) {
+               var i, pair;
+
+               // We're done when there's nothing more to compare
+               if (arguments.length < 2) {
+                       return true;
+               }
+
+               // Clear the global pair queue and add the top-level values being compared
+               pairs = [{ a: a, b: b }];
+
+               for (i = 0; i < pairs.length; i++) {
+                       pair = pairs[i];
+
+                       // Perform type-specific comparison on any pairs that are not strictly
+                       // equal. For container types, that comparison will postpone comparison
+                       // of any sub-container pair to the end of the pair queue. This gives
+                       // breadth-first search order. It also avoids the reprocessing of
+                       // reference-equal siblings, cousins etc, which can have a significant speed
+                       // impact when comparing a container of small objects each of which has a
+                       // reference to the same (singleton) large object.
+                       if (pair.a !== pair.b && !typeEquiv(pair.a, pair.b)) {
+                               return false;
+                       }
+               }
+
+               // ...across all consecutive argument pairs
+               return arguments.length === 2 || innerEquiv.apply(this, [].slice.call(arguments, 1));
+       }
+
+       return function () {
+               var result = innerEquiv.apply(undefined, arguments);
+
+               // Release any retained objects
+               pairs.length = 0;
+               return result;
+       };
+  })();
+
+  /**
+   * Config object: Maintain internal state
+   * Later exposed as QUnit.config
+   * `config` initialized at top of scope
+   */
+  var config = {
+
+       // The queue of tests to run
+       queue: [],
+
+       // Block until document ready
+       blocking: true,
+
+       // By default, run previously failed tests first
+       // very useful in combination with "Hide passed tests" checked
+       reorder: true,
+
+       // By default, modify document.title when suite is done
+       altertitle: true,
+
+       // HTML Reporter: collapse every test except the first failing test
+       // If false, all failing tests will be expanded
+       collapse: true,
+
+       // By default, scroll to top of the page when suite is done
+       scrolltop: true,
+
+       // Depth up-to which object will be dumped
+       maxDepth: 5,
+
+       // When enabled, all tests must call expect()
+       requireExpects: false,
+
+       // Placeholder for user-configurable form-exposed URL parameters
+       urlConfig: [],
+
+       // Set of all modules.
+       modules: [],
+
+       // The first unnamed module
+       currentModule: {
+               name: "",
+               tests: [],
+               childModules: [],
+               testsRun: 0,
+               unskippedTestsRun: 0,
+               hooks: {
+                       before: [],
+                       beforeEach: [],
+                       afterEach: [],
+                       after: []
+               }
+       },
+
+       callbacks: {},
+
+       // The storage module to use for reordering tests
+       storage: localSessionStorage
+  };
+
+  // take a predefined QUnit.config and extend the defaults
+  var globalConfig = window && window.QUnit && window.QUnit.config;
+
+  // only extend the global config if there is no QUnit overload
+  if (window && window.QUnit && !window.QUnit.version) {
+       extend(config, globalConfig);
+  }
+
+  // Push a loose unnamed module to the modules collection
+  config.modules.push(config.currentModule);
+
+  // Based on jsDump by Ariel Flesler
+  // http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html
+  var dump = (function () {
+       function quote(str) {
+               return "\"" + str.toString().replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\"";
+       }
+       function literal(o) {
+               return o + "";
+       }
+       function join(pre, arr, post) {
+               var s = dump.separator(),
+                   base = dump.indent(),
+                   inner = dump.indent(1);
+               if (arr.join) {
+                       arr = arr.join("," + s + inner);
+               }
+               if (!arr) {
+                       return pre + post;
+               }
+               return [pre, inner + arr, base + post].join(s);
+       }
+       function array(arr, stack) {
+               var i = arr.length,
+                   ret = new Array(i);
+
+               if (dump.maxDepth && dump.depth > dump.maxDepth) {
+                       return "[object Array]";
+               }
+
+               this.up();
+               while (i--) {
+                       ret[i] = this.parse(arr[i], undefined, stack);
+               }
+               this.down();
+               return join("[", ret, "]");
+       }
+
+       function isArray(obj) {
+               return (
+
+                       //Native Arrays
+                       toString.call(obj) === "[object Array]" ||
+
+                       // NodeList objects
+                       typeof obj.length === "number" && obj.item !== undefined && (obj.length ? obj.item(0) === obj[0] : obj.item(0) === null && obj[0] === undefined)
+               );
+       }
+
+       var reName = /^function (\w+)/,
+           dump = {
+
+               // The objType is used mostly internally, you can fix a (custom) type in advance
+               parse: function parse(obj, objType, stack) {
+                       stack = stack || [];
+                       var res,
+                           parser,
+                           parserType,
+                           objIndex = stack.indexOf(obj);
+
+                       if (objIndex !== -1) {
+                               return "recursion(" + (objIndex - stack.length) + ")";
+                       }
+
+                       objType = objType || this.typeOf(obj);
+                       parser = this.parsers[objType];
+                       parserType = typeof parser === "undefined" ? "undefined" : _typeof(parser);
+
+                       if (parserType === "function") {
+                               stack.push(obj);
+                               res = parser.call(this, obj, stack);
+                               stack.pop();
+                               return res;
+                       }
+                       return parserType === "string" ? parser : this.parsers.error;
+               },
+               typeOf: function typeOf(obj) {
+                       var type;
+
+                       if (obj === null) {
+                               type = "null";
+                       } else if (typeof obj === "undefined") {
+                               type = "undefined";
+                       } else if (is("regexp", obj)) {
+                               type = "regexp";
+                       } else if (is("date", obj)) {
+                               type = "date";
+                       } else if (is("function", obj)) {
+                               type = "function";
+                       } else if (obj.setInterval !== undefined && obj.document !== undefined && obj.nodeType === undefined) {
+                               type = "window";
+                       } else if (obj.nodeType === 9) {
+                               type = "document";
+                       } else if (obj.nodeType) {
+                               type = "node";
+                       } else if (isArray(obj)) {
+                               type = "array";
+                       } else if (obj.constructor === Error.prototype.constructor) {
+                               type = "error";
+                       } else {
+                               type = typeof obj === "undefined" ? "undefined" : _typeof(obj);
+                       }
+                       return type;
+               },
+
+               separator: function separator() {
+                       if (this.multiline) {
+                               return this.HTML ? "<br />" : "\n";
+                       } else {
+                               return this.HTML ? "&#160;" : " ";
+                       }
+               },
+
+               // Extra can be a number, shortcut for increasing-calling-decreasing
+               indent: function indent(extra) {
+                       if (!this.multiline) {
+                               return "";
+                       }
+                       var chr = this.indentChar;
+                       if (this.HTML) {
+                               chr = chr.replace(/\t/g, "   ").replace(/ /g, "&#160;");
+                       }
+                       return new Array(this.depth + (extra || 0)).join(chr);
+               },
+               up: function up(a) {
+                       this.depth += a || 1;
+               },
+               down: function down(a) {
+                       this.depth -= a || 1;
+               },
+               setParser: function setParser(name, parser) {
+                       this.parsers[name] = parser;
+               },
+
+               // The next 3 are exposed so you can use them
+               quote: quote,
+               literal: literal,
+               join: join,
+               depth: 1,
+               maxDepth: config.maxDepth,
+
+               // This is the list of parsers, to modify them, use dump.setParser
+               parsers: {
+                       window: "[Window]",
+                       document: "[Document]",
+                       error: function error(_error) {
+                               return "Error(\"" + _error.message + "\")";
+                       },
+                       unknown: "[Unknown]",
+                       "null": "null",
+                       "undefined": "undefined",
+                       "function": function _function(fn) {
+                               var ret = "function",
+
+
+                               // Functions never have name in IE
+                               name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];
+
+                               if (name) {
+                                       ret += " " + name;
+                               }
+                               ret += "(";
+
+                               ret = [ret, dump.parse(fn, "functionArgs"), "){"].join("");
+                               return join(ret, dump.parse(fn, "functionCode"), "}");
+                       },
+                       array: array,
+                       nodelist: array,
+                       "arguments": array,
+                       object: function object(map, stack) {
+                               var keys,
+                                   key,
+                                   val,
+                                   i,
+                                   nonEnumerableProperties,
+                                   ret = [];
+
+                               if (dump.maxDepth && dump.depth > dump.maxDepth) {
+                                       return "[object Object]";
+                               }
+
+                               dump.up();
+                               keys = [];
+                               for (key in map) {
+                                       keys.push(key);
+                               }
+
+                               // Some properties are not always enumerable on Error objects.
+                               nonEnumerableProperties = ["message", "name"];
+                               for (i in nonEnumerableProperties) {
+                                       key = nonEnumerableProperties[i];
+                                       if (key in map && !inArray(key, keys)) {
+                                               keys.push(key);
+                                       }
+                               }
+                               keys.sort();
+                               for (i = 0; i < keys.length; i++) {
+                                       key = keys[i];
+                                       val = map[key];
+                                       ret.push(dump.parse(key, "key") + ": " + dump.parse(val, undefined, stack));
+                               }
+                               dump.down();
+                               return join("{", ret, "}");
+                       },
+                       node: function node(_node) {
+                               var len,
+                                   i,
+                                   val,
+                                   open = dump.HTML ? "&lt;" : "<",
+                                   close = dump.HTML ? "&gt;" : ">",
+                                   tag = _node.nodeName.toLowerCase(),
+                                   ret = open + tag,
+                                   attrs = _node.attributes;
+
+                               if (attrs) {
+                                       for (i = 0, len = attrs.length; i < len; i++) {
+                                               val = attrs[i].nodeValue;
+
+                                               // IE6 includes all attributes in .attributes, even ones not explicitly
+                                               // set. Those have values like undefined, null, 0, false, "" or
+                                               // "inherit".
+                                               if (val && val !== "inherit") {
+                                                       ret += " " + attrs[i].nodeName + "=" + dump.parse(val, "attribute");
+                                               }
+                                       }
+                               }
+                               ret += close;
+
+                               // Show content of TextNode or CDATASection
+                               if (_node.nodeType === 3 || _node.nodeType === 4) {
+                                       ret += _node.nodeValue;
+                               }
+
+                               return ret + open + "/" + tag + close;
+                       },
+
+                       // Function calls it internally, it's the arguments part of the function
+                       functionArgs: function functionArgs(fn) {
+                               var args,
+                                   l = fn.length;
+
+                               if (!l) {
+                                       return "";
+                               }
+
+                               args = new Array(l);
+                               while (l--) {
+
+                                       // 97 is 'a'
+                                       args[l] = String.fromCharCode(97 + l);
+                               }
+                               return " " + args.join(", ") + " ";
+                       },
+
+                       // Object calls it internally, the key part of an item in a map
+                       key: quote,
+
+                       // Function calls it internally, it's the content of the function
+                       functionCode: "[code]",
+
+                       // Node calls it internally, it's a html attribute value
+                       attribute: quote,
+                       string: quote,
+                       date: quote,
+                       regexp: literal,
+                       number: literal,
+                       "boolean": literal,
+                       symbol: function symbol(sym) {
+                               return sym.toString();
+                       }
+               },
+
+               // If true, entities are escaped ( <, >, \t, space and \n )
+               HTML: false,
+
+               // Indentation unit
+               indentChar: "  ",
+
+               // If true, items in a collection, are separated by a \n, else just a space.
+               multiline: true
+       };
+
+       return dump;
+  })();
+
+  var LISTENERS = Object.create(null);
+  var SUPPORTED_EVENTS = ["runStart", "suiteStart", "testStart", "assertion", "testEnd", "suiteEnd", "runEnd"];
+
+  /**
+   * Emits an event with the specified data to all currently registered listeners.
+   * Callbacks will fire in the order in which they are registered (FIFO). This
+   * function is not exposed publicly; it is used by QUnit internals to emit
+   * logging events.
+   *
+   * @private
+   * @method emit
+   * @param {String} eventName
+   * @param {Object} data
+   * @return {Void}
+   */
+  function emit(eventName, data) {
+       if (objectType(eventName) !== "string") {
+               throw new TypeError("eventName must be a string when emitting an event");
+       }
+
+       // Clone the callbacks in case one of them registers a new callback
+       var originalCallbacks = LISTENERS[eventName];
+       var callbacks = originalCallbacks ? [].concat(toConsumableArray(originalCallbacks)) : [];
+
+       for (var i = 0; i < callbacks.length; i++) {
+               callbacks[i](data);
+       }
+  }
+
+  /**
+   * Registers a callback as a listener to the specified event.
+   *
+   * @public
+   * @method on
+   * @param {String} eventName
+   * @param {Function} callback
+   * @return {Void}
+   */
+  function on(eventName, callback) {
+       if (objectType(eventName) !== "string") {
+               throw new TypeError("eventName must be a string when registering a listener");
+       } else if (!inArray(eventName, SUPPORTED_EVENTS)) {
+               var events = SUPPORTED_EVENTS.join(", ");
+               throw new Error("\"" + eventName + "\" is not a valid event; must be one of: " + events + ".");
+       } else if (objectType(callback) !== "function") {
+               throw new TypeError("callback must be a function when registering a listener");
+       }
+
+       if (!LISTENERS[eventName]) {
+               LISTENERS[eventName] = [];
+       }
+
+       // Don't register the same callback more than once
+       if (!inArray(callback, LISTENERS[eventName])) {
+               LISTENERS[eventName].push(callback);
+       }
+  }
+
+  // Register logging callbacks
+  function registerLoggingCallbacks(obj) {
+       var i,
+           l,
+           key,
+           callbackNames = ["begin", "done", "log", "testStart", "testDone", "moduleStart", "moduleDone"];
+
+       function registerLoggingCallback(key) {
+               var loggingCallback = function loggingCallback(callback) {
+                       if (objectType(callback) !== "function") {
+                               throw new Error("QUnit logging methods require a callback function as their first parameters.");
+                       }
+
+                       config.callbacks[key].push(callback);
+               };
+
+               return loggingCallback;
+       }
+
+       for (i = 0, l = callbackNames.length; i < l; i++) {
+               key = callbackNames[i];
+
+               // Initialize key collection of logging callback
+               if (objectType(config.callbacks[key]) === "undefined") {
+                       config.callbacks[key] = [];
+               }
+
+               obj[key] = registerLoggingCallback(key);
+       }
+  }
+
+  function runLoggingCallbacks(key, args) {
+       var i, l, callbacks;
+
+       callbacks = config.callbacks[key];
+       for (i = 0, l = callbacks.length; i < l; i++) {
+               callbacks[i](args);
+       }
+  }
+
+  // Doesn't support IE9, it will return undefined on these browsers
+  // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
+  var fileName = (sourceFromStacktrace(0) || "").replace(/(:\d+)+\)?/, "").replace(/.+\//, "");
+
+  function extractStacktrace(e, offset) {
+       offset = offset === undefined ? 4 : offset;
+
+       var stack, include, i;
+
+       if (e && e.stack) {
+               stack = e.stack.split("\n");
+               if (/^error$/i.test(stack[0])) {
+                       stack.shift();
+               }
+               if (fileName) {
+                       include = [];
+                       for (i = offset; i < stack.length; i++) {
+                               if (stack[i].indexOf(fileName) !== -1) {
+                                       break;
+                               }
+                               include.push(stack[i]);
+                       }
+                       if (include.length) {
+                               return include.join("\n");
+                       }
+               }
+               return stack[offset];
+       }
+  }
+
+  function sourceFromStacktrace(offset) {
+       var error = new Error();
+
+       // Support: Safari <=7 only, IE <=10 - 11 only
+       // Not all browsers generate the `stack` property for `new Error()`, see also #636
+       if (!error.stack) {
+               try {
+                       throw error;
+               } catch (err) {
+                       error = err;
+               }
+       }
+
+       return extractStacktrace(error, offset);
+  }
+
+  var priorityCount = 0;
+  var unitSampler = void 0;
+
+  /**
+   * Advances the ProcessingQueue to the next item if it is ready.
+   * @param {Boolean} last
+   */
+  function advance() {
+       var start = now();
+       config.depth = (config.depth || 0) + 1;
+
+       while (config.queue.length && !config.blocking) {
+               var elapsedTime = now() - start;
+
+               if (!defined.setTimeout || config.updateRate <= 0 || elapsedTime < config.updateRate) {
+                       if (priorityCount > 0) {
+                               priorityCount--;
+                       }
+
+                       config.queue.shift()();
+               } else {
+                       setTimeout(advance, 13);
+                       break;
+               }
+       }
+
+       config.depth--;
+
+       if (!config.blocking && !config.queue.length && config.depth === 0) {
+               done();
+       }
+  }
+
+  function addToQueueImmediate(callback) {
+       if (objectType(callback) === "array") {
+               while (callback.length) {
+                       addToQueueImmediate(callback.pop());
+               }
+
+               return;
+       }
+
+       config.queue.unshift(callback);
+       priorityCount++;
+  }
+
+  /**
+   * Adds a function to the ProcessingQueue for execution.
+   * @param {Function|Array} callback
+   * @param {Boolean} priority
+   * @param {String} seed
+   */
+  function addToQueue(callback, prioritize, seed) {
+       if (prioritize) {
+               config.queue.splice(priorityCount++, 0, callback);
+       } else if (seed) {
+               if (!unitSampler) {
+                       unitSampler = unitSamplerGenerator(seed);
+               }
+
+               // Insert into a random position after all prioritized items
+               var index = Math.floor(unitSampler() * (config.queue.length - priorityCount + 1));
+               config.queue.splice(priorityCount + index, 0, callback);
+       } else {
+               config.queue.push(callback);
+       }
+  }
+
+  /**
+   * Creates a seeded "sample" generator which is used for randomizing tests.
+   */
+  function unitSamplerGenerator(seed) {
+
+       // 32-bit xorshift, requires only a nonzero seed
+       // http://excamera.com/sphinx/article-xorshift.html
+       var sample = parseInt(generateHash(seed), 16) || -1;
+       return function () {
+               sample ^= sample << 13;
+               sample ^= sample >>> 17;
+               sample ^= sample << 5;
+
+               // ECMAScript has no unsigned number type
+               if (sample < 0) {
+                       sample += 0x100000000;
+               }
+
+               return sample / 0x100000000;
+       };
+  }
+
+  /**
+   * This function is called when the ProcessingQueue is done processing all
+   * items. It handles emitting the final run events.
+   */
+  function done() {
+       var storage = config.storage;
+
+       ProcessingQueue.finished = true;
+
+       var runtime = now() - config.started;
+       var passed = config.stats.all - config.stats.bad;
+
+       emit("runEnd", globalSuite.end(true));
+       runLoggingCallbacks("done", {
+               passed: passed,
+               failed: config.stats.bad,
+               total: config.stats.all,
+               runtime: runtime
+       });
+
+       // Clear own storage items if all tests passed
+       if (storage && config.stats.bad === 0) {
+               for (var i = storage.length - 1; i >= 0; i--) {
+                       var key = storage.key(i);
+
+                       if (key.indexOf("qunit-test-") === 0) {
+                               storage.removeItem(key);
+                       }
+               }
+       }
+  }
+
+  var ProcessingQueue = {
+       finished: false,
+       add: addToQueue,
+       addImmediate: addToQueueImmediate,
+       advance: advance
+  };
+
+  var TestReport = function () {
+       function TestReport(name, suite, options) {
+               classCallCheck(this, TestReport);
+
+               this.name = name;
+               this.suiteName = suite.name;
+               this.fullName = suite.fullName.concat(name);
+               this.runtime = 0;
+               this.assertions = [];
+
+               this.skipped = !!options.skip;
+               this.todo = !!options.todo;
+
+               this.valid = options.valid;
+
+               this._startTime = 0;
+               this._endTime = 0;
+
+               suite.pushTest(this);
+       }
+
+       createClass(TestReport, [{
+               key: "start",
+               value: function start(recordTime) {
+                       if (recordTime) {
+                               this._startTime = Date.now();
+                       }
+
+                       return {
+                               name: this.name,
+                               suiteName: this.suiteName,
+                               fullName: this.fullName.slice()
+                       };
+               }
+       }, {
+               key: "end",
+               value: function end(recordTime) {
+                       if (recordTime) {
+                               this._endTime = Date.now();
+                       }
+
+                       return extend(this.start(), {
+                               runtime: this.getRuntime(),
+                               status: this.getStatus(),
+                               errors: this.getFailedAssertions(),
+                               assertions: this.getAssertions()
+                       });
+               }
+       }, {
+               key: "pushAssertion",
+               value: function pushAssertion(assertion) {
+                       this.assertions.push(assertion);
+               }
+       }, {
+               key: "getRuntime",
+               value: function getRuntime() {
+                       return this._endTime - this._startTime;
+               }
+       }, {
+               key: "getStatus",
+               value: function getStatus() {
+                       if (this.skipped) {
+                               return "skipped";
+                       }
+
+                       var testPassed = this.getFailedAssertions().length > 0 ? this.todo : !this.todo;
+
+                       if (!testPassed) {
+                               return "failed";
+                       } else if (this.todo) {
+                               return "todo";
+                       } else {
+                               return "passed";
+                       }
+               }
+       }, {
+               key: "getFailedAssertions",
+               value: function getFailedAssertions() {
+                       return this.assertions.filter(function (assertion) {
+                               return !assertion.passed;
+                       });
+               }
+       }, {
+               key: "getAssertions",
+               value: function getAssertions() {
+                       return this.assertions.slice();
+               }
+
+               // Remove actual and expected values from assertions. This is to prevent
+               // leaking memory throughout a test suite.
+
+       }, {
+               key: "slimAssertions",
+               value: function slimAssertions() {
+                       this.assertions = this.assertions.map(function (assertion) {
+                               delete assertion.actual;
+                               delete assertion.expected;
+                               return assertion;
+                       });
+               }
+       }]);
+       return TestReport;
+  }();
+
+  var focused$1 = false;
+
+  function Test(settings) {
+       var i, l;
+
+       ++Test.count;
+
+       this.expected = null;
+       this.assertions = [];
+       this.semaphore = 0;
+       this.module = config.currentModule;
+       this.stack = sourceFromStacktrace(3);
+       this.steps = [];
+       this.timeout = undefined;
+
+       // If a module is skipped, all its tests and the tests of the child suites
+       // should be treated as skipped even if they are defined as `only` or `todo`.
+       // As for `todo` module, all its tests will be treated as `todo` except for
+       // tests defined as `skip` which will be left intact.
+       //
+       // So, if a test is defined as `todo` and is inside a skipped module, we should
+       // then treat that test as if was defined as `skip`.
+       if (this.module.skip) {
+               settings.skip = true;
+               settings.todo = false;
+
+               // Skipped tests should be left intact
+       } else if (this.module.todo && !settings.skip) {
+               settings.todo = true;
+       }
+
+       extend(this, settings);
+
+       this.testReport = new TestReport(settings.testName, this.module.suiteReport, {
+               todo: settings.todo,
+               skip: settings.skip,
+               valid: this.valid()
+       });
+
+       // Register unique strings
+       for (i = 0, l = this.module.tests; i < l.length; i++) {
+               if (this.module.tests[i].name === this.testName) {
+                       this.testName += " ";
+               }
+       }
+
+       this.testId = generateHash(this.module.name, this.testName);
+
+       this.module.tests.push({
+               name: this.testName,
+               testId: this.testId,
+               skip: !!settings.skip
+       });
+
+       if (settings.skip) {
+
+               // Skipped tests will fully ignore any sent callback
+               this.callback = function () {};
+               this.async = false;
+               this.expected = 0;
+       } else {
+               this.assert = new Assert(this);
+       }
+  }
+
+  Test.count = 0;
+
+  function getNotStartedModules(startModule) {
+       var module = startModule,
+           modules = [];
+
+       while (module && module.testsRun === 0) {
+               modules.push(module);
+               module = module.parentModule;
+       }
+
+       return modules;
+  }
+
+  Test.prototype = {
+       before: function before() {
+               var i,
+                   startModule,
+                   module = this.module,
+                   notStartedModules = getNotStartedModules(module);
+
+               for (i = notStartedModules.length - 1; i >= 0; i--) {
+                       startModule = notStartedModules[i];
+                       startModule.stats = { all: 0, bad: 0, started: now() };
+                       emit("suiteStart", startModule.suiteReport.start(true));
+                       runLoggingCallbacks("moduleStart", {
+                               name: startModule.name,
+                               tests: startModule.tests
+                       });
+               }
+
+               config.current = this;
+
+               this.testEnvironment = extend({}, module.testEnvironment);
+
+               this.started = now();
+               emit("testStart", this.testReport.start(true));
+               runLoggingCallbacks("testStart", {
+                       name: this.testName,
+                       module: module.name,
+                       testId: this.testId,
+                       previousFailure: this.previousFailure
+               });
+
+               if (!config.pollution) {
+                       saveGlobal();
+               }
+       },
+
+       run: function run() {
+               var promise;
+
+               config.current = this;
+
+               this.callbackStarted = now();
+
+               if (config.notrycatch) {
+                       runTest(this);
+                       return;
+               }
+
+               try {
+                       runTest(this);
+               } catch (e) {
+                       this.pushFailure("Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + (e.message || e), extractStacktrace(e, 0));
+
+                       // Else next test will carry the responsibility
+                       saveGlobal();
+
+                       // Restart the tests if they're blocking
+                       if (config.blocking) {
+                               internalRecover(this);
+                       }
+               }
+
+               function runTest(test) {
+                       promise = test.callback.call(test.testEnvironment, test.assert);
+                       test.resolvePromise(promise);
+
+                       // If the test has a "lock" on it, but the timeout is 0, then we push a
+                       // failure as the test should be synchronous.
+                       if (test.timeout === 0 && test.semaphore !== 0) {
+                               pushFailure("Test did not finish synchronously even though assert.timeout( 0 ) was used.", sourceFromStacktrace(2));
+                       }
+               }
+       },
+
+       after: function after() {
+               checkPollution();
+       },
+
+       queueHook: function queueHook(hook, hookName, hookOwner) {
+               var _this = this;
+
+               var callHook = function callHook() {
+                       var promise = hook.call(_this.testEnvironment, _this.assert);
+                       _this.resolvePromise(promise, hookName);
+               };
+
+               var runHook = function runHook() {
+                       if (hookName === "before") {
+                               if (hookOwner.unskippedTestsRun !== 0) {
+                                       return;
+                               }
+
+                               _this.preserveEnvironment = true;
+                       }
+
+                       if (hookName === "after" && hookOwner.unskippedTestsRun !== numberOfUnskippedTests(hookOwner) - 1 && config.queue.length > 2) {
+                               return;
+                       }
+
+                       config.current = _this;
+                       if (config.notrycatch) {
+                               callHook();
+                               return;
+                       }
+                       try {
+                               callHook();
+                       } catch (error) {
+                               _this.pushFailure(hookName + " failed on " + _this.testName + ": " + (error.message || error), extractStacktrace(error, 0));
+                       }
+               };
+
+               return runHook;
+       },
+
+
+       // Currently only used for module level hooks, can be used to add global level ones
+       hooks: function hooks(handler) {
+               var hooks = [];
+
+               function processHooks(test, module) {
+                       if (module.parentModule) {
+                               processHooks(test, module.parentModule);
+                       }
+
+                       if (module.hooks[handler].length) {
+                               for (var i = 0; i < module.hooks[handler].length; i++) {
+                                       hooks.push(test.queueHook(module.hooks[handler][i], handler, module));
+                               }
+                       }
+               }
+
+               // Hooks are ignored on skipped tests
+               if (!this.skip) {
+                       processHooks(this, this.module);
+               }
+
+               return hooks;
+       },
+
+
+       finish: function finish() {
+               config.current = this;
+               if (config.requireExpects && this.expected === null) {
+                       this.pushFailure("Expected number of assertions to be defined, but expect() was " + "not called.", this.stack);
+               } else if (this.expected !== null && this.expected !== this.assertions.length) {
+                       this.pushFailure("Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack);
+               } else if (this.expected === null && !this.assertions.length) {
+                       this.pushFailure("Expected at least one assertion, but none were run - call " + "expect(0) to accept zero assertions.", this.stack);
+               }
+
+               var i,
+                   module = this.module,
+                   moduleName = module.name,
+                   testName = this.testName,
+                   skipped = !!this.skip,
+                   todo = !!this.todo,
+                   bad = 0,
+                   storage = config.storage;
+
+               this.runtime = now() - this.started;
+
+               config.stats.all += this.assertions.length;
+               module.stats.all += this.assertions.length;
+
+               for (i = 0; i < this.assertions.length; i++) {
+                       if (!this.assertions[i].result) {
+                               bad++;
+                               config.stats.bad++;
+                               module.stats.bad++;
+                       }
+               }
+
+               notifyTestsRan(module, skipped);
+
+               // Store result when possible
+               if (storage) {
+                       if (bad) {
+                               storage.setItem("qunit-test-" + moduleName + "-" + testName, bad);
+                       } else {
+                               storage.removeItem("qunit-test-" + moduleName + "-" + testName);
+                       }
+               }
+
+               // After emitting the js-reporters event we cleanup the assertion data to
+               // avoid leaking it. It is not used by the legacy testDone callbacks.
+               emit("testEnd", this.testReport.end(true));
+               this.testReport.slimAssertions();
+
+               runLoggingCallbacks("testDone", {
+                       name: testName,
+                       module: moduleName,
+                       skipped: skipped,
+                       todo: todo,
+                       failed: bad,
+                       passed: this.assertions.length - bad,
+                       total: this.assertions.length,
+                       runtime: skipped ? 0 : this.runtime,
+
+                       // HTML Reporter use
+                       assertions: this.assertions,
+                       testId: this.testId,
+
+                       // Source of Test
+                       source: this.stack
+               });
+
+               if (module.testsRun === numberOfTests(module)) {
+                       logSuiteEnd(module);
+
+                       // Check if the parent modules, iteratively, are done. If that the case,
+                       // we emit the `suiteEnd` event and trigger `moduleDone` callback.
+                       var parent = module.parentModule;
+                       while (parent && parent.testsRun === numberOfTests(parent)) {
+                               logSuiteEnd(parent);
+                               parent = parent.parentModule;
+                       }
+               }
+
+               config.current = undefined;
+
+               function logSuiteEnd(module) {
+                       emit("suiteEnd", module.suiteReport.end(true));
+                       runLoggingCallbacks("moduleDone", {
+                               name: module.name,
+                               tests: module.tests,
+                               failed: module.stats.bad,
+                               passed: module.stats.all - module.stats.bad,
+                               total: module.stats.all,
+                               runtime: now() - module.stats.started
+                       });
+               }
+       },
+
+       preserveTestEnvironment: function preserveTestEnvironment() {
+               if (this.preserveEnvironment) {
+                       this.module.testEnvironment = this.testEnvironment;
+                       this.testEnvironment = extend({}, this.module.testEnvironment);
+               }
+       },
+
+       queue: function queue() {
+               var test = this;
+
+               if (!this.valid()) {
+                       return;
+               }
+
+               function runTest() {
+
+                       // Each of these can by async
+                       ProcessingQueue.addImmediate([function () {
+                               test.before();
+                       }, test.hooks("before"), function () {
+                               test.preserveTestEnvironment();
+                       }, test.hooks("beforeEach"), function () {
+                               test.run();
+                       }, test.hooks("afterEach").reverse(), test.hooks("after").reverse(), function () {
+                               test.after();
+                       }, function () {
+                               test.finish();
+                       }]);
+               }
+
+               var previousFailCount = config.storage && +config.storage.getItem("qunit-test-" + this.module.name + "-" + this.testName);
+
+               // Prioritize previously failed tests, detected from storage
+               var prioritize = config.reorder && !!previousFailCount;
+
+               this.previousFailure = !!previousFailCount;
+
+               ProcessingQueue.add(runTest, prioritize, config.seed);
+
+               // If the queue has already finished, we manually process the new test
+               if (ProcessingQueue.finished) {
+                       ProcessingQueue.advance();
+               }
+       },
+
+
+       pushResult: function pushResult(resultInfo) {
+               if (this !== config.current) {
+                       throw new Error("Assertion occured after test had finished.");
+               }
+
+               // Destructure of resultInfo = { result, actual, expected, message, negative }
+               var source,
+                   details = {
+                       module: this.module.name,
+                       name: this.testName,
+                       result: resultInfo.result,
+                       message: resultInfo.message,
+                       actual: resultInfo.actual,
+                       expected: resultInfo.expected,
+                       testId: this.testId,
+                       negative: resultInfo.negative || false,
+                       runtime: now() - this.started,
+                       todo: !!this.todo
+               };
+
+               if (!resultInfo.result) {
+                       source = resultInfo.source || sourceFromStacktrace();
+
+                       if (source) {
+                               details.source = source;
+                       }
+               }
+
+               this.logAssertion(details);
+
+               this.assertions.push({
+                       result: !!resultInfo.result,
+                       message: resultInfo.message
+               });
+       },
+
+       pushFailure: function pushFailure(message, source, actual) {
+               if (!(this instanceof Test)) {
+                       throw new Error("pushFailure() assertion outside test context, was " + sourceFromStacktrace(2));
+               }
+
+               this.pushResult({
+                       result: false,
+                       message: message || "error",
+                       actual: actual || null,
+                       expected: null,
+                       source: source
+               });
+       },
+
+       /**
+    * Log assertion details using both the old QUnit.log interface and
+    * QUnit.on( "assertion" ) interface.
+    *
+    * @private
+    */
+       logAssertion: function logAssertion(details) {
+               runLoggingCallbacks("log", details);
+
+               var assertion = {
+                       passed: details.result,
+                       actual: details.actual,
+                       expected: details.expected,
+                       message: details.message,
+                       stack: details.source,
+                       todo: details.todo
+               };
+               this.testReport.pushAssertion(assertion);
+               emit("assertion", assertion);
+       },
+
+
+       resolvePromise: function resolvePromise(promise, phase) {
+               var then,
+                   resume,
+                   message,
+                   test = this;
+               if (promise != null) {
+                       then = promise.then;
+                       if (objectType(then) === "function") {
+                               resume = internalStop(test);
+                               then.call(promise, function () {
+                                       resume();
+                               }, function (error) {
+                                       message = "Promise rejected " + (!phase ? "during" : phase.replace(/Each$/, "")) + " \"" + test.testName + "\": " + (error && error.message || error);
+                                       test.pushFailure(message, extractStacktrace(error, 0));
+
+                                       // Else next test will carry the responsibility
+                                       saveGlobal();
+
+                                       // Unblock
+                                       resume();
+                               });
+                       }
+               }
+       },
+
+       valid: function valid() {
+               var filter = config.filter,
+                   regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec(filter),
+                   module = config.module && config.module.toLowerCase(),
+                   fullName = this.module.name + ": " + this.testName;
+
+               function moduleChainNameMatch(testModule) {
+                       var testModuleName = testModule.name ? testModule.name.toLowerCase() : null;
+                       if (testModuleName === module) {
+                               return true;
+                       } else if (testModule.parentModule) {
+                               return moduleChainNameMatch(testModule.parentModule);
+                       } else {
+                               return false;
+                       }
+               }
+
+               function moduleChainIdMatch(testModule) {
+                       return inArray(testModule.moduleId, config.moduleId) || testModule.parentModule && moduleChainIdMatch(testModule.parentModule);
+               }
+
+               // Internally-generated tests are always valid
+               if (this.callback && this.callback.validTest) {
+                       return true;
+               }
+
+               if (config.moduleId && config.moduleId.length > 0 && !moduleChainIdMatch(this.module)) {
+
+                       return false;
+               }
+
+               if (config.testId && config.testId.length > 0 && !inArray(this.testId, config.testId)) {
+
+                       return false;
+               }
+
+               if (module && !moduleChainNameMatch(this.module)) {
+                       return false;
+               }
+
+               if (!filter) {
+                       return true;
+               }
+
+               return regexFilter ? this.regexFilter(!!regexFilter[1], regexFilter[2], regexFilter[3], fullName) : this.stringFilter(filter, fullName);
+       },
+
+       regexFilter: function regexFilter(exclude, pattern, flags, fullName) {
+               var regex = new RegExp(pattern, flags);
+               var match = regex.test(fullName);
+
+               return match !== exclude;
+       },
+
+       stringFilter: function stringFilter(filter, fullName) {
+               filter = filter.toLowerCase();
+               fullName = fullName.toLowerCase();
+
+               var include = filter.charAt(0) !== "!";
+               if (!include) {
+                       filter = filter.slice(1);
+               }
+
+               // If the filter matches, we need to honour include
+               if (fullName.indexOf(filter) !== -1) {
+                       return include;
+               }
+
+               // Otherwise, do the opposite
+               return !include;
+       }
+  };
+
+  function pushFailure() {
+       if (!config.current) {
+               throw new Error("pushFailure() assertion outside test context, in " + sourceFromStacktrace(2));
+       }
+
+       // Gets current test obj
+       var currentTest = config.current;
+
+       return currentTest.pushFailure.apply(currentTest, arguments);
+  }
+
+  function saveGlobal() {
+       config.pollution = [];
+
+       if (config.noglobals) {
+               for (var key in global$1) {
+                       if (hasOwn.call(global$1, key)) {
+
+                               // In Opera sometimes DOM element ids show up here, ignore them
+                               if (/^qunit-test-output/.test(key)) {
+                                       continue;
+                               }
+                               config.pollution.push(key);
+                       }
+               }
+       }
+  }
+
+  function checkPollution() {
+       var newGlobals,
+           deletedGlobals,
+           old = config.pollution;
+
+       saveGlobal();
+
+       newGlobals = diff(config.pollution, old);
+       if (newGlobals.length > 0) {
+               pushFailure("Introduced global variable(s): " + newGlobals.join(", "));
+       }
+
+       deletedGlobals = diff(old, config.pollution);
+       if (deletedGlobals.length > 0) {
+               pushFailure("Deleted global variable(s): " + deletedGlobals.join(", "));
+       }
+  }
+
+  // Will be exposed as QUnit.test
+  function test(testName, callback) {
+       if (focused$1) {
+               return;
+       }
+
+       var newTest = new Test({
+               testName: testName,
+               callback: callback
+       });
+
+       newTest.queue();
+  }
+
+  function todo(testName, callback) {
+       if (focused$1) {
+               return;
+       }
+
+       var newTest = new Test({
+               testName: testName,
+               callback: callback,
+               todo: true
+       });
+
+       newTest.queue();
+  }
+
+  // Will be exposed as QUnit.skip
+  function skip(testName) {
+       if (focused$1) {
+               return;
+       }
+
+       var test = new Test({
+               testName: testName,
+               skip: true
+       });
+
+       test.queue();
+  }
+
+  // Will be exposed as QUnit.only
+  function only(testName, callback) {
+       if (focused$1) {
+               return;
+       }
+
+       config.queue.length = 0;
+       focused$1 = true;
+
+       var newTest = new Test({
+               testName: testName,
+               callback: callback
+       });
+
+       newTest.queue();
+  }
+
+  // Put a hold on processing and return a function that will release it.
+  function internalStop(test) {
+       test.semaphore += 1;
+       config.blocking = true;
+
+       // Set a recovery timeout, if so configured.
+       if (defined.setTimeout) {
+               var timeoutDuration = void 0;
+
+               if (typeof test.timeout === "number") {
+                       timeoutDuration = test.timeout;
+               } else if (typeof config.testTimeout === "number") {
+                       timeoutDuration = config.testTimeout;
+               }
+
+               if (typeof timeoutDuration === "number" && timeoutDuration > 0) {
+                       clearTimeout(config.timeout);
+                       config.timeout = setTimeout(function () {
+                               pushFailure("Test took longer than " + timeoutDuration + "ms; test timed out.", sourceFromStacktrace(2));
+                               internalRecover(test);
+                       }, timeoutDuration);
+               }
+       }
+
+       var released = false;
+       return function resume() {
+               if (released) {
+                       return;
+               }
+
+               released = true;
+               test.semaphore -= 1;
+               internalStart(test);
+       };
+  }
+
+  // Forcefully release all processing holds.
+  function internalRecover(test) {
+       test.semaphore = 0;
+       internalStart(test);
+  }
+
+  // Release a processing hold, scheduling a resumption attempt if no holds remain.
+  function internalStart(test) {
+
+       // If semaphore is non-numeric, throw error
+       if (isNaN(test.semaphore)) {
+               test.semaphore = 0;
+
+               pushFailure("Invalid value on test.semaphore", sourceFromStacktrace(2));
+               return;
+       }
+
+       // Don't start until equal number of stop-calls
+       if (test.semaphore > 0) {
+               return;
+       }
+
+       // Throw an Error if start is called more often than stop
+       if (test.semaphore < 0) {
+               test.semaphore = 0;
+
+               pushFailure("Tried to restart test while already started (test's semaphore was 0 already)", sourceFromStacktrace(2));
+               return;
+       }
+
+       // Add a slight delay to allow more assertions etc.
+       if (defined.setTimeout) {
+               if (config.timeout) {
+                       clearTimeout(config.timeout);
+               }
+               config.timeout = setTimeout(function () {
+                       if (test.semaphore > 0) {
+                               return;
+                       }
+
+                       if (config.timeout) {
+                               clearTimeout(config.timeout);
+                       }
+
+                       begin();
+               }, 13);
+       } else {
+               begin();
+       }
+  }
+
+  function collectTests(module) {
+       var tests = [].concat(module.tests);
+       var modules = [].concat(toConsumableArray(module.childModules));
+
+       // Do a breadth-first traversal of the child modules
+       while (modules.length) {
+               var nextModule = modules.shift();
+               tests.push.apply(tests, nextModule.tests);
+               modules.push.apply(modules, toConsumableArray(nextModule.childModules));
+       }
+
+       return tests;
+  }
+
+  function numberOfTests(module) {
+       return collectTests(module).length;
+  }
+
+  function numberOfUnskippedTests(module) {
+       return collectTests(module).filter(function (test) {
+               return !test.skip;
+       }).length;
+  }
+
+  function notifyTestsRan(module, skipped) {
+       module.testsRun++;
+       if (!skipped) {
+               module.unskippedTestsRun++;
+       }
+       while (module = module.parentModule) {
+               module.testsRun++;
+               if (!skipped) {
+                       module.unskippedTestsRun++;
+               }
+       }
+  }
+
+  /**
+   * Returns a function that proxies to the given method name on the globals
+   * console object. The proxy will also detect if the console doesn't exist and
+   * will appropriately no-op. This allows support for IE9, which doesn't have a
+   * console if the developer tools are not open.
+   */
+  function consoleProxy(method) {
+       return function () {
+               if (console) {
+                       console[method].apply(console, arguments);
+               }
+       };
+  }
+
+  var Logger = {
+       warn: consoleProxy("warn")
+  };
+
+  var Assert = function () {
+       function Assert(testContext) {
+               classCallCheck(this, Assert);
+
+               this.test = testContext;
+       }
+
+       // Assert helpers
+
+       createClass(Assert, [{
+               key: "timeout",
+               value: function timeout(duration) {
+                       if (typeof duration !== "number") {
+                               throw new Error("You must pass a number as the duration to assert.timeout");
+                       }
+
+                       this.test.timeout = duration;
+               }
+
+               // Documents a "step", which is a string value, in a test as a passing assertion
+
+       }, {
+               key: "step",
+               value: function step(message) {
+                       var result = !!message;
+
+                       this.test.steps.push(message);
+
+                       return this.pushResult({
+                               result: result,
+                               message: message || "You must provide a message to assert.step"
+                       });
+               }
+
+               // Verifies the steps in a test match a given array of string values
+
+       }, {
+               key: "verifySteps",
+               value: function verifySteps(steps, message) {
+                       this.deepEqual(this.test.steps, steps, message);
+               }
+
+               // Specify the number of expected assertions to guarantee that failed test
+               // (no assertions are run at all) don't slip through.
+
+       }, {
+               key: "expect",
+               value: function expect(asserts) {
+                       if (arguments.length === 1) {
+                               this.test.expected = asserts;
+                       } else {
+                               return this.test.expected;
+                       }
+               }
+
+               // Put a hold on processing and return a function that will release it a maximum of once.
+
+       }, {
+               key: "async",
+               value: function async(count) {
+                       var test$$1 = this.test;
+
+                       var popped = false,
+                           acceptCallCount = count;
+
+                       if (typeof acceptCallCount === "undefined") {
+                               acceptCallCount = 1;
+                       }
+
+                       var resume = internalStop(test$$1);
+
+                       return function done() {
+                               if (config.current !== test$$1) {
+                                       throw Error("assert.async callback called after test finished.");
+                               }
+
+                               if (popped) {
+                                       test$$1.pushFailure("Too many calls to the `assert.async` callback", sourceFromStacktrace(2));
+                                       return;
+                               }
+
+                               acceptCallCount -= 1;
+                               if (acceptCallCount > 0) {
+                                       return;
+                               }
+
+                               popped = true;
+                               resume();
+                       };
+               }
+
+               // Exports test.push() to the user API
+               // Alias of pushResult.
+
+       }, {
+               key: "push",
+               value: function push(result, actual, expected, message, negative) {
+                       Logger.warn("assert.push is deprecated and will be removed in QUnit 3.0." + " Please use assert.pushResult instead (https://api.qunitjs.com/assert/pushResult).");
+
+                       var currentAssert = this instanceof Assert ? this : config.current.assert;
+                       return currentAssert.pushResult({
+                               result: result,
+                               actual: actual,
+                               expected: expected,
+                               message: message,
+                               negative: negative
+                       });
+               }
+       }, {
+               key: "pushResult",
+               value: function pushResult(resultInfo) {
+
+                       // Destructure of resultInfo = { result, actual, expected, message, negative }
+                       var assert = this;
+                       var currentTest = assert instanceof Assert && assert.test || config.current;
+
+                       // Backwards compatibility fix.
+                       // Allows the direct use of global exported assertions and QUnit.assert.*
+                       // Although, it's use is not recommended as it can leak assertions
+                       // to other tests from async tests, because we only get a reference to the current test,
+                       // not exactly the test where assertion were intended to be called.
+                       if (!currentTest) {
+                               throw new Error("assertion outside test context, in " + sourceFromStacktrace(2));
+                       }
+
+                       if (!(assert instanceof Assert)) {
+                               assert = currentTest.assert;
+                       }
+
+                       return assert.test.pushResult(resultInfo);
+               }
+       }, {
+               key: "ok",
+               value: function ok(result, message) {
+                       if (!message) {
+                               message = result ? "okay" : "failed, expected argument to be truthy, was: " + dump.parse(result);
+                       }
+
+                       this.pushResult({
+                               result: !!result,
+                               actual: result,
+                               expected: true,
+                               message: message
+                       });
+               }
+       }, {
+               key: "notOk",
+               value: function notOk(result, message) {
+                       if (!message) {
+                               message = !result ? "okay" : "failed, expected argument to be falsy, was: " + dump.parse(result);
+                       }
+
+                       this.pushResult({
+                               result: !result,
+                               actual: result,
+                               expected: false,
+                               message: message
+                       });
+               }
+       }, {
+               key: "equal",
+               value: function equal(actual, expected, message) {
+
+                       // eslint-disable-next-line eqeqeq
+                       var result = expected == actual;
+
+                       this.pushResult({
+                               result: result,
+                               actual: actual,
+                               expected: expected,
+                               message: message
+                       });
+               }
+       }, {
+               key: "notEqual",
+               value: function notEqual(actual, expected, message) {
+
+                       // eslint-disable-next-line eqeqeq
+                       var result = expected != actual;
+
+                       this.pushResult({
+                               result: result,
+                               actual: actual,
+                               expected: expected,
+                               message: message,
+                               negative: true
+                       });
+               }
+       }, {
+               key: "propEqual",
+               value: function propEqual(actual, expected, message) {
+                       actual = objectValues(actual);
+                       expected = objectValues(expected);
+
+                       this.pushResult({
+                               result: equiv(actual, expected),
+                               actual: actual,
+                               expected: expected,
+                               message: message
+                       });
+               }
+       }, {
+               key: "notPropEqual",
+               value: function notPropEqual(actual, expected, message) {
+                       actual = objectValues(actual);
+                       expected = objectValues(expected);
+
+                       this.pushResult({
+                               result: !equiv(actual, expected),
+                               actual: actual,
+                               expected: expected,
+                               message: message,
+                               negative: true
+                       });
+               }
+       }, {
+               key: "deepEqual",
+               value: function deepEqual(actual, expected, message) {
+                       this.pushResult({
+                               result: equiv(actual, expected),
+                               actual: actual,
+                               expected: expected,
+                               message: message
+                       });
+               }
+       }, {
+               key: "notDeepEqual",
+               value: function notDeepEqual(actual, expected, message) {
+                       this.pushResult({
+                               result: !equiv(actual, expected),
+                               actual: actual,
+                               expected: expected,
+                               message: message,
+                               negative: true
+                       });
+               }
+       }, {
+               key: "strictEqual",
+               value: function strictEqual(actual, expected, message) {
+                       this.pushResult({
+                               result: expected === actual,
+                               actual: actual,
+                               expected: expected,
+                               message: message
+                       });
+               }
+       }, {
+               key: "notStrictEqual",
+               value: function notStrictEqual(actual, expected, message) {
+                       this.pushResult({
+                               result: expected !== actual,
+                               actual: actual,
+                               expected: expected,
+                               message: message,
+                               negative: true
+                       });
+               }
+       }, {
+               key: "throws",
+               value: function throws(block, expected, message) {
+                       var actual = void 0,
+                           result = false;
+
+                       var currentTest = this instanceof Assert && this.test || config.current;
+
+                       // 'expected' is optional unless doing string comparison
+                       if (objectType(expected) === "string") {
+                               if (message == null) {
+                                       message = expected;
+                                       expected = null;
+                               } else {
+                                       throw new Error("throws/raises does not accept a string value for the expected argument.\n" + "Use a non-string object value (e.g. regExp) instead if it's necessary.");
+                               }
+                       }
+
+                       currentTest.ignoreGlobalErrors = true;
+                       try {
+                               block.call(currentTest.testEnvironment);
+                       } catch (e) {
+                               actual = e;
+                       }
+                       currentTest.ignoreGlobalErrors = false;
+
+                       if (actual) {
+                               var expectedType = objectType(expected);
+
+                               // We don't want to validate thrown error
+                               if (!expected) {
+                                       result = true;
+                                       expected = null;
+
+                                       // Expected is a regexp
+                               } else if (expectedType === "regexp") {
+                                       result = expected.test(errorString(actual));
+
+                                       // Expected is a constructor, maybe an Error constructor
+                               } else if (expectedType === "function" && actual instanceof expected) {
+                                       result = true;
+
+                                       // Expected is an Error object
+                               } else if (expectedType === "object") {
+                                       result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message;
+
+                                       // Expected is a validation function which returns true if validation passed
+                               } else if (expectedType === "function" && expected.call({}, actual) === true) {
+                                       expected = null;
+                                       result = true;
+                               }
+                       }
+
+                       currentTest.assert.pushResult({
+                               result: result,
+                               actual: actual,
+                               expected: expected,
+                               message: message
+                       });
+               }
+       }]);
+       return Assert;
+  }();
+
+  // Provide an alternative to assert.throws(), for environments that consider throws a reserved word
+  // Known to us are: Closure Compiler, Narwhal
+  // eslint-disable-next-line dot-notation
+
+
+  Assert.prototype.raises = Assert.prototype["throws"];
+
+  /**
+   * Converts an error into a simple string for comparisons.
+   *
+   * @param {Error} error
+   * @return {String}
+   */
+  function errorString(error) {
+       var resultErrorString = error.toString();
+
+       if (resultErrorString.substring(0, 7) === "[object") {
+               var name = error.name ? error.name.toString() : "Error";
+               var message = error.message ? error.message.toString() : "";
+
+               if (name && message) {
+                       return name + ": " + message;
+               } else if (name) {
+                       return name;
+               } else if (message) {
+                       return message;
+               } else {
+                       return "Error";
+               }
+       } else {
+               return resultErrorString;
+       }
+  }
+
+  /* global module, exports, define */
+  function exportQUnit(QUnit) {
+
+       if (defined.document) {
+
+               // QUnit may be defined when it is preconfigured but then only QUnit and QUnit.config may be defined.
+               if (window.QUnit && window.QUnit.version) {
+                       throw new Error("QUnit has already been defined.");
+               }
+
+               window.QUnit = QUnit;
+       }
+
+       // For nodejs
+       if (typeof module !== "undefined" && module && module.exports) {
+               module.exports = QUnit;
+
+               // For consistency with CommonJS environments' exports
+               module.exports.QUnit = QUnit;
+       }
+
+       // For CommonJS with exports, but without module.exports, like Rhino
+       if (typeof exports !== "undefined" && exports) {
+               exports.QUnit = QUnit;
+       }
+
+       if (typeof define === "function" && define.amd) {
+               define(function () {
+                       return QUnit;
+               });
+               QUnit.config.autostart = false;
+       }
+
+       // For Web/Service Workers
+       if (self$1 && self$1.WorkerGlobalScope && self$1 instanceof self$1.WorkerGlobalScope) {
+               self$1.QUnit = QUnit;
+       }
+  }
+
+  var SuiteReport = function () {
+       function SuiteReport(name, parentSuite) {
+               classCallCheck(this, SuiteReport);
+
+               this.name = name;
+               this.fullName = parentSuite ? parentSuite.fullName.concat(name) : [];
+
+               this.tests = [];
+               this.childSuites = [];
+
+               if (parentSuite) {
+                       parentSuite.pushChildSuite(this);
+               }
+       }
+
+       createClass(SuiteReport, [{
+               key: "start",
+               value: function start(recordTime) {
+                       if (recordTime) {
+                               this._startTime = Date.now();
+                       }
+
+                       return {
+                               name: this.name,
+                               fullName: this.fullName.slice(),
+                               tests: this.tests.map(function (test) {
+                                       return test.start();
+                               }),
+                               childSuites: this.childSuites.map(function (suite) {
+                                       return suite.start();
+                               }),
+                               testCounts: {
+                                       total: this.getTestCounts().total
+                               }
+                       };
+               }
+       }, {
+               key: "end",
+               value: function end(recordTime) {
+                       if (recordTime) {
+                               this._endTime = Date.now();
+                       }
+
+                       return {
+                               name: this.name,
+                               fullName: this.fullName.slice(),
+                               tests: this.tests.map(function (test) {
+                                       return test.end();
+                               }),
+                               childSuites: this.childSuites.map(function (suite) {
+                                       return suite.end();
+                               }),
+                               testCounts: this.getTestCounts(),
+                               runtime: this.getRuntime(),
+                               status: this.getStatus()
+                       };
+               }
+       }, {
+               key: "pushChildSuite",
+               value: function pushChildSuite(suite) {
+                       this.childSuites.push(suite);
+               }
+       }, {
+               key: "pushTest",
+               value: function pushTest(test) {
+                       this.tests.push(test);
+               }
+       }, {
+               key: "getRuntime",
+               value: function getRuntime() {
+                       return this._endTime - this._startTime;
+               }
+       }, {
+               key: "getTestCounts",
+               value: function getTestCounts() {
+                       var counts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { passed: 0, failed: 0, skipped: 0, todo: 0, total: 0 };
+
+                       counts = this.tests.reduce(function (counts, test) {
+                               if (test.valid) {
+                                       counts[test.getStatus()]++;
+                                       counts.total++;
+                               }
+
+                               return counts;
+                       }, counts);
+
+                       return this.childSuites.reduce(function (counts, suite) {
+                               return suite.getTestCounts(counts);
+                       }, counts);
+               }
+       }, {
+               key: "getStatus",
+               value: function getStatus() {
+                       var _getTestCounts = this.getTestCounts(),
+                           total = _getTestCounts.total,
+                           failed = _getTestCounts.failed,
+                           skipped = _getTestCounts.skipped,
+                           todo = _getTestCounts.todo;
+
+                       if (failed) {
+                               return "failed";
+                       } else {
+                               if (skipped === total) {
+                                       return "skipped";
+                               } else if (todo === total) {
+                                       return "todo";
+                               } else {
+                                       return "passed";
+                               }
+                       }
+               }
+       }]);
+       return SuiteReport;
+  }();
+
+  // Handle an unhandled exception. By convention, returns true if further
+  // error handling should be suppressed and false otherwise.
+  // In this case, we will only suppress further error handling if the
+  // "ignoreGlobalErrors" configuration option is enabled.
+  function onError(error) {
+       for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+               args[_key - 1] = arguments[_key];
+       }
+
+       if (config.current) {
+               if (config.current.ignoreGlobalErrors) {
+                       return true;
+               }
+               pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args));
+       } else {
+               test("global failure", extend(function () {
+                       pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args));
+               }, { validTest: true }));
+       }
+
+       return false;
+  }
+
+  var focused = false;
+  var QUnit = {};
+  var globalSuite = new SuiteReport();
+
+  // The initial "currentModule" represents the global (or top-level) module that
+  // is not explicitly defined by the user, therefore we add the "globalSuite" to
+  // it since each module has a suiteReport associated with it.
+  config.currentModule.suiteReport = globalSuite;
+
+  var moduleStack = [];
+  var globalStartCalled = false;
+  var runStarted = false;
+
+  // Figure out if we're running the tests from a server or not
+  QUnit.isLocal = !(defined.document && window.location.protocol !== "file:");
+
+  // Expose the current QUnit version
+  QUnit.version = "2.4.0";
+
+  function createModule(name, testEnvironment, modifiers) {
+       var parentModule = moduleStack.length ? moduleStack.slice(-1)[0] : null;
+       var moduleName = parentModule !== null ? [parentModule.name, name].join(" > ") : name;
+       var parentSuite = parentModule ? parentModule.suiteReport : globalSuite;
+
+       var skip$$1 = parentModule !== null && parentModule.skip || modifiers.skip;
+       var todo$$1 = parentModule !== null && parentModule.todo || modifiers.todo;
+
+       var module = {
+               name: moduleName,
+               parentModule: parentModule,
+               tests: [],
+               moduleId: generateHash(moduleName),
+               testsRun: 0,
+               unskippedTestsRun: 0,
+               childModules: [],
+               suiteReport: new SuiteReport(name, parentSuite),
+
+               // Pass along `skip` and `todo` properties from parent module, in case
+               // there is one, to childs. And use own otherwise.
+               // This property will be used to mark own tests and tests of child suites
+               // as either `skipped` or `todo`.
+               skip: skip$$1,
+               todo: skip$$1 ? false : todo$$1
+       };
+
+       var env = {};
+       if (parentModule) {
+               parentModule.childModules.push(module);
+               extend(env, parentModule.testEnvironment);
+       }
+       extend(env, testEnvironment);
+       module.testEnvironment = env;
+
+       config.modules.push(module);
+       return module;
+  }
+
+  function processModule(name, options, executeNow) {
+       var modifiers = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
+
+       var module = createModule(name, options, modifiers);
+
+       // Move any hooks to a 'hooks' object
+       var testEnvironment = module.testEnvironment;
+       var hooks = module.hooks = {};
+
+       setHookFromEnvironment(hooks, testEnvironment, "before");
+       setHookFromEnvironment(hooks, testEnvironment, "beforeEach");
+       setHookFromEnvironment(hooks, testEnvironment, "afterEach");
+       setHookFromEnvironment(hooks, testEnvironment, "after");
+
+       function setHookFromEnvironment(hooks, environment, name) {
+               var potentialHook = environment[name];
+               hooks[name] = typeof potentialHook === "function" ? [potentialHook] : [];
+               delete environment[name];
+       }
+
+       var moduleFns = {
+               before: setHookFunction(module, "before"),
+               beforeEach: setHookFunction(module, "beforeEach"),
+               afterEach: setHookFunction(module, "afterEach"),
+               after: setHookFunction(module, "after")
+       };
+
+       var currentModule = config.currentModule;
+       if (objectType(executeNow) === "function") {
+               moduleStack.push(module);
+               config.currentModule = module;
+               executeNow.call(module.testEnvironment, moduleFns);
+               moduleStack.pop();
+               module = module.parentModule || currentModule;
+       }
+
+       config.currentModule = module;
+  }
+
+  // TODO: extract this to a new file alongside its related functions
+  function module$1(name, options, executeNow) {
+       if (focused) {
+               return;
+       }
+
+       if (arguments.length === 2) {
+               if (objectType(options) === "function") {
+                       executeNow = options;
+                       options = undefined;
+               }
+       }
+
+       processModule(name, options, executeNow);
+  }
+
+  module$1.only = function () {
+       if (focused) {
+               return;
+       }
+
+       config.modules.length = 0;
+       config.queue.length = 0;
+
+       module$1.apply(undefined, arguments);
+
+       focused = true;
+  };
+
+  module$1.skip = function (name, options, executeNow) {
+       if (focused) {
+               return;
+       }
+
+       if (arguments.length === 2) {
+               if (objectType(options) === "function") {
+                       executeNow = options;
+                       options = undefined;
+               }
+       }
+
+       processModule(name, options, executeNow, { skip: true });
+  };
+
+  module$1.todo = function (name, options, executeNow) {
+       if (focused) {
+               return;
+       }
+
+       if (arguments.length === 2) {
+               if (objectType(options) === "function") {
+                       executeNow = options;
+                       options = undefined;
+               }
+       }
+
+       processModule(name, options, executeNow, { todo: true });
+  };
+
+  extend(QUnit, {
+       on: on,
+
+       module: module$1,
+
+       test: test,
+
+       todo: todo,
+
+       skip: skip,
+
+       only: only,
+
+       start: function start(count) {
+               var globalStartAlreadyCalled = globalStartCalled;
+
+               if (!config.current) {
+                       globalStartCalled = true;
+
+                       if (runStarted) {
+                               throw new Error("Called start() while test already started running");
+                       } else if (globalStartAlreadyCalled || count > 1) {
+                               throw new Error("Called start() outside of a test context too many times");
+                       } else if (config.autostart) {
+                               throw new Error("Called start() outside of a test context when " + "QUnit.config.autostart was true");
+                       } else if (!config.pageLoaded) {
+
+                               // The page isn't completely loaded yet, so we set autostart and then
+                               // load if we're in Node or wait for the browser's load event.
+                               config.autostart = true;
+
+                               // Starts from Node even if .load was not previously called. We still return
+                               // early otherwise we'll wind up "beginning" twice.
+                               if (!defined.document) {
+                                       QUnit.load();
+                               }
+
+                               return;
+                       }
+               } else {
+                       throw new Error("QUnit.start cannot be called inside a test context.");
+               }
+
+               scheduleBegin();
+       },
+
+       config: config,
+
+       is: is,
+
+       objectType: objectType,
+
+       extend: extend,
+
+       load: function load() {
+               config.pageLoaded = true;
+
+               // Initialize the configuration options
+               extend(config, {
+                       stats: { all: 0, bad: 0 },
+                       started: 0,
+                       updateRate: 1000,
+                       autostart: true,
+                       filter: ""
+               }, true);
+
+               if (!runStarted) {
+                       config.blocking = false;
+
+                       if (config.autostart) {
+                               scheduleBegin();
+                       }
+               }
+       },
+
+       stack: function stack(offset) {
+               offset = (offset || 0) + 2;
+               return sourceFromStacktrace(offset);
+       },
+
+       onError: onError
+  });
+
+  QUnit.pushFailure = pushFailure;
+  QUnit.assert = Assert.prototype;
+  QUnit.equiv = equiv;
+  QUnit.dump = dump;
+
+  registerLoggingCallbacks(QUnit);
+
+  function scheduleBegin() {
+
+       runStarted = true;
+
+       // Add a slight delay to allow definition of more modules and tests.
+       if (defined.setTimeout) {
+               setTimeout(function () {
+                       begin();
+               }, 13);
+       } else {
+               begin();
+       }
+  }
+
+  function begin() {
+       var i,
+           l,
+           modulesLog = [];
+
+       // If the test run hasn't officially begun yet
+       if (!config.started) {
+
+               // Record the time of the test run's beginning
+               config.started = now();
+
+               // Delete the loose unnamed module if unused.
+               if (config.modules[0].name === "" && config.modules[0].tests.length === 0) {
+                       config.modules.shift();
+               }
+
+               // Avoid unnecessary information by not logging modules' test environments
+               for (i = 0, l = config.modules.length; i < l; i++) {
+                       modulesLog.push({
+                               name: config.modules[i].name,
+                               tests: config.modules[i].tests
+                       });
+               }
+
+               // The test run is officially beginning now
+               emit("runStart", globalSuite.start(true));
+               runLoggingCallbacks("begin", {
+                       totalTests: Test.count,
+                       modules: modulesLog
+               });
+       }
+
+       config.blocking = false;
+       ProcessingQueue.advance();
+  }
+
+  function setHookFunction(module, hookName) {
+       return function setHook(callback) {
+               module.hooks[hookName].push(callback);
+       };
+  }
+
+  exportQUnit(QUnit);
+
+  (function () {
+
+       if (typeof window === "undefined" || typeof document === "undefined") {
+               return;
+       }
+
+       var config = QUnit.config,
+           hasOwn = Object.prototype.hasOwnProperty;
+
+       // Stores fixture HTML for resetting later
+       function storeFixture() {
+
+               // Avoid overwriting user-defined values
+               if (hasOwn.call(config, "fixture")) {
+                       return;
+               }
+
+               var fixture = document.getElementById("qunit-fixture");
+               if (fixture) {
+                       config.fixture = fixture.innerHTML;
+               }
+       }
+
+       QUnit.begin(storeFixture);
+
+       // Resets the fixture DOM element if available.
+       function resetFixture() {
+               if (config.fixture == null) {
+                       return;
+               }
+
+               var fixture = document.getElementById("qunit-fixture");
+               if (fixture) {
+                       fixture.innerHTML = config.fixture;
+               }
+       }
+
+       QUnit.testStart(resetFixture);
+  })();
+
+  (function () {
+
+       // Only interact with URLs via window.location
+       var location = typeof window !== "undefined" && window.location;
+       if (!location) {
+               return;
+       }
+
+       var urlParams = getUrlParams();
+
+       QUnit.urlParams = urlParams;
+
+       // Match module/test by inclusion in an array
+       QUnit.config.moduleId = [].concat(urlParams.moduleId || []);
+       QUnit.config.testId = [].concat(urlParams.testId || []);
+
+       // Exact case-insensitive match of the module name
+       QUnit.config.module = urlParams.module;
+
+       // Regular expression or case-insenstive substring match against "moduleName: testName"
+       QUnit.config.filter = urlParams.filter;
+
+       // Test order randomization
+       if (urlParams.seed === true) {
+
+               // Generate a random seed if the option is specified without a value
+               QUnit.config.seed = Math.random().toString(36).slice(2);
+       } else if (urlParams.seed) {
+               QUnit.config.seed = urlParams.seed;
+       }
+
+       // Add URL-parameter-mapped config values with UI form rendering data
+       QUnit.config.urlConfig.push({
+               id: "hidepassed",
+               label: "Hide passed tests",
+               tooltip: "Only show tests and assertions that fail. Stored as query-strings."
+       }, {
+               id: "noglobals",
+               label: "Check for Globals",
+               tooltip: "Enabling this will test if any test introduces new properties on the " + "global object (`window` in Browsers). Stored as query-strings."
+       }, {
+               id: "notrycatch",
+               label: "No try-catch",
+               tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + "exceptions in IE reasonable. Stored as query-strings."
+       });
+
+       QUnit.begin(function () {
+               var i,
+                   option,
+                   urlConfig = QUnit.config.urlConfig;
+
+               for (i = 0; i < urlConfig.length; i++) {
+
+                       // Options can be either strings or objects with nonempty "id" properties
+                       option = QUnit.config.urlConfig[i];
+                       if (typeof option !== "string") {
+                               option = option.id;
+                       }
+
+                       if (QUnit.config[option] === undefined) {
+                               QUnit.config[option] = urlParams[option];
+                       }
+               }
+       });
+
+       function getUrlParams() {
+               var i, param, name, value;
+               var urlParams = Object.create(null);
+               var params = location.search.slice(1).split("&");
+               var length = params.length;
+
+               for (i = 0; i < length; i++) {
+                       if (params[i]) {
+                               param = params[i].split("=");
+                               name = decodeQueryParam(param[0]);
+
+                               // Allow just a key to turn on a flag, e.g., test.html?noglobals
+                               value = param.length === 1 || decodeQueryParam(param.slice(1).join("="));
+                               if (name in urlParams) {
+                                       urlParams[name] = [].concat(urlParams[name], value);
+                               } else {
+                                       urlParams[name] = value;
+                               }
+                       }
+               }
+
+               return urlParams;
+       }
+
+       function decodeQueryParam(param) {
+               return decodeURIComponent(param.replace(/\+/g, "%20"));
+       }
+  })();
+
+  var stats = {
+       passedTests: 0,
+       failedTests: 0,
+       skippedTests: 0,
+       todoTests: 0
+  };
+
+  // Escape text for attribute or text content.
+  function escapeText(s) {
+       if (!s) {
+               return "";
+       }
+       s = s + "";
+
+       // Both single quotes and double quotes (for attributes)
+       return s.replace(/['"<>&]/g, function (s) {
+               switch (s) {
+                       case "'":
+                               return "&#039;";
+                       case "\"":
+                               return "&quot;";
+                       case "<":
+                               return "&lt;";
+                       case ">":
+                               return "&gt;";
+                       case "&":
+                               return "&amp;";
+               }
+       });
+  }
+
+  (function () {
+
+       // Don't load the HTML Reporter on non-browser environments
+       if (typeof window === "undefined" || !window.document) {
+               return;
+       }
+
+       var config = QUnit.config,
+           document$$1 = window.document,
+           collapseNext = false,
+           hasOwn = Object.prototype.hasOwnProperty,
+           unfilteredUrl = setUrl({ filter: undefined, module: undefined,
+               moduleId: undefined, testId: undefined }),
+           modulesList = [];
+
+       function addEvent(elem, type, fn) {
+               elem.addEventListener(type, fn, false);
+       }
+
+       function removeEvent(elem, type, fn) {
+               elem.removeEventListener(type, fn, false);
+       }
+
+       function addEvents(elems, type, fn) {
+               var i = elems.length;
+               while (i--) {
+                       addEvent(elems[i], type, fn);
+               }
+       }
+
+       function hasClass(elem, name) {
+               return (" " + elem.className + " ").indexOf(" " + name + " ") >= 0;
+       }
+
+       function addClass(elem, name) {
+               if (!hasClass(elem, name)) {
+                       elem.className += (elem.className ? " " : "") + name;
+               }
+       }
+
+       function toggleClass(elem, name, force) {
+               if (force || typeof force === "undefined" && !hasClass(elem, name)) {
+                       addClass(elem, name);
+               } else {
+                       removeClass(elem, name);
+               }
+       }
+
+       function removeClass(elem, name) {
+               var set = " " + elem.className + " ";
+
+               // Class name may appear multiple times
+               while (set.indexOf(" " + name + " ") >= 0) {
+                       set = set.replace(" " + name + " ", " ");
+               }
+
+               // Trim for prettiness
+               elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, "");
+       }
+
+       function id(name) {
+               return document$$1.getElementById && document$$1.getElementById(name);
+       }
+
+       function abortTests() {
+               var abortButton = id("qunit-abort-tests-button");
+               if (abortButton) {
+                       abortButton.disabled = true;
+                       abortButton.innerHTML = "Aborting...";
+               }
+               QUnit.config.queue.length = 0;
+               return false;
+       }
+
+       function interceptNavigation(ev) {
+               applyUrlParams();
+
+               if (ev && ev.preventDefault) {
+                       ev.preventDefault();
+               }
+
+               return false;
+       }
+
+       function getUrlConfigHtml() {
+               var i,
+                   j,
+                   val,
+                   escaped,
+                   escapedTooltip,
+                   selection = false,
+                   urlConfig = config.urlConfig,
+                   urlConfigHtml = "";
+
+               for (i = 0; i < urlConfig.length; i++) {
+
+                       // Options can be either strings or objects with nonempty "id" properties
+                       val = config.urlConfig[i];
+                       if (typeof val === "string") {
+                               val = {
+                                       id: val,
+                                       label: val
+                               };
+                       }
+
+                       escaped = escapeText(val.id);
+                       escapedTooltip = escapeText(val.tooltip);
+
+                       if (!val.value || typeof val.value === "string") {
+                               urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + "' title='" + escapedTooltip + "'><input id='qunit-urlconfig-" + escaped + "' name='" + escaped + "' type='checkbox'" + (val.value ? " value='" + escapeText(val.value) + "'" : "") + (config[val.id] ? " checked='checked'" : "") + " title='" + escapedTooltip + "' />" + escapeText(val.label) + "</label>";
+                       } else {
+                               urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + "' title='" + escapedTooltip + "'>" + val.label + ": </label><select id='qunit-urlconfig-" + escaped + "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
+
+                               if (QUnit.is("array", val.value)) {
+                                       for (j = 0; j < val.value.length; j++) {
+                                               escaped = escapeText(val.value[j]);
+                                               urlConfigHtml += "<option value='" + escaped + "'" + (config[val.id] === val.value[j] ? (selection = true) && " selected='selected'" : "") + ">" + escaped + "</option>";
+                                       }
+                               } else {
+                                       for (j in val.value) {
+                                               if (hasOwn.call(val.value, j)) {
+                                                       urlConfigHtml += "<option value='" + escapeText(j) + "'" + (config[val.id] === j ? (selection = true) && " selected='selected'" : "") + ">" + escapeText(val.value[j]) + "</option>";
+                                               }
+                                       }
+                               }
+                               if (config[val.id] && !selection) {
+                                       escaped = escapeText(config[val.id]);
+                                       urlConfigHtml += "<option value='" + escaped + "' selected='selected' disabled='disabled'>" + escaped + "</option>";
+                               }
+                               urlConfigHtml += "</select>";
+                       }
+               }
+
+               return urlConfigHtml;
+       }
+
+       // Handle "click" events on toolbar checkboxes and "change" for select menus.
+       // Updates the URL with the new state of `config.urlConfig` values.
+       function toolbarChanged() {
+               var updatedUrl,
+                   value,
+                   tests,
+                   field = this,
+                   params = {};
+
+               // Detect if field is a select menu or a checkbox
+               if ("selectedIndex" in field) {
+                       value = field.options[field.selectedIndex].value || undefined;
+               } else {
+                       value = field.checked ? field.defaultValue || true : undefined;
+               }
+
+               params[field.name] = value;
+               updatedUrl = setUrl(params);
+
+               // Check if we can apply the change without a page refresh
+               if ("hidepassed" === field.name && "replaceState" in window.history) {
+                       QUnit.urlParams[field.name] = value;
+                       config[field.name] = value || false;
+                       tests = id("qunit-tests");
+                       if (tests) {
+                               toggleClass(tests, "hidepass", value || false);
+                       }
+                       window.history.replaceState(null, "", updatedUrl);
+               } else {
+                       window.location = updatedUrl;
+               }
+       }
+
+       function setUrl(params) {
+               var key,
+                   arrValue,
+                   i,
+                   querystring = "?",
+                   location = window.location;
+
+               params = QUnit.extend(QUnit.extend({}, QUnit.urlParams), params);
+
+               for (key in params) {
+
+                       // Skip inherited or undefined properties
+                       if (hasOwn.call(params, key) && params[key] !== undefined) {
+
+                               // Output a parameter for each value of this key (but usually just one)
+                               arrValue = [].concat(params[key]);
+                               for (i = 0; i < arrValue.length; i++) {
+                                       querystring += encodeURIComponent(key);
+                                       if (arrValue[i] !== true) {
+                                               querystring += "=" + encodeURIComponent(arrValue[i]);
+                                       }
+                                       querystring += "&";
+                               }
+                       }
+               }
+               return location.protocol + "//" + location.host + location.pathname + querystring.slice(0, -1);
+       }
+
+       function applyUrlParams() {
+               var i,
+                   selectedModules = [],
+                   modulesList = id("qunit-modulefilter-dropdown-list").getElementsByTagName("input"),
+                   filter = id("qunit-filter-input").value;
+
+               for (i = 0; i < modulesList.length; i++) {
+                       if (modulesList[i].checked) {
+                               selectedModules.push(modulesList[i].value);
+                       }
+               }
+
+               window.location = setUrl({
+                       filter: filter === "" ? undefined : filter,
+                       moduleId: selectedModules.length === 0 ? undefined : selectedModules,
+
+                       // Remove module and testId filter
+                       module: undefined,
+                       testId: undefined
+               });
+       }
+
+       function toolbarUrlConfigContainer() {
+               var urlConfigContainer = document$$1.createElement("span");
+
+               urlConfigContainer.innerHTML = getUrlConfigHtml();
+               addClass(urlConfigContainer, "qunit-url-config");
+
+               addEvents(urlConfigContainer.getElementsByTagName("input"), "change", toolbarChanged);
+               addEvents(urlConfigContainer.getElementsByTagName("select"), "change", toolbarChanged);
+
+               return urlConfigContainer;
+       }
+
+       function abortTestsButton() {
+               var button = document$$1.createElement("button");
+               button.id = "qunit-abort-tests-button";
+               button.innerHTML = "Abort";
+               addEvent(button, "click", abortTests);
+               return button;
+       }
+
+       function toolbarLooseFilter() {
+               var filter = document$$1.createElement("form"),
+                   label = document$$1.createElement("label"),
+                   input = document$$1.createElement("input"),
+                   button = document$$1.createElement("button");
+
+               addClass(filter, "qunit-filter");
+
+               label.innerHTML = "Filter: ";
+
+               input.type = "text";
+               input.value = config.filter || "";
+               input.name = "filter";
+               input.id = "qunit-filter-input";
+
+               button.innerHTML = "Go";
+
+               label.appendChild(input);
+
+               filter.appendChild(label);
+               filter.appendChild(document$$1.createTextNode(" "));
+               filter.appendChild(button);
+               addEvent(filter, "submit", interceptNavigation);
+
+               return filter;
+       }
+
+       function moduleListHtml() {
+               var i,
+                   checked,
+                   html = "";
+
+               for (i = 0; i < config.modules.length; i++) {
+                       if (config.modules[i].name !== "") {
+                               checked = config.moduleId.indexOf(config.modules[i].moduleId) > -1;
+                               html += "<li><label class='clickable" + (checked ? " checked" : "") + "'><input type='checkbox' " + "value='" + config.modules[i].moduleId + "'" + (checked ? " checked='checked'" : "") + " />" + escapeText(config.modules[i].name) + "</label></li>";
+                       }
+               }
+
+               return html;
+       }
+
+       function toolbarModuleFilter() {
+               var allCheckbox,
+                   commit,
+                   reset,
+                   moduleFilter = document$$1.createElement("form"),
+                   label = document$$1.createElement("label"),
+                   moduleSearch = document$$1.createElement("input"),
+                   dropDown = document$$1.createElement("div"),
+                   actions = document$$1.createElement("span"),
+                   dropDownList = document$$1.createElement("ul"),
+                   dirty = false;
+
+               moduleSearch.id = "qunit-modulefilter-search";
+               addEvent(moduleSearch, "input", searchInput);
+               addEvent(moduleSearch, "input", searchFocus);
+               addEvent(moduleSearch, "focus", searchFocus);
+               addEvent(moduleSearch, "click", searchFocus);
+
+               label.id = "qunit-modulefilter-search-container";
+               label.innerHTML = "Module: ";
+               label.appendChild(moduleSearch);
+
+               actions.id = "qunit-modulefilter-actions";
+               actions.innerHTML = "<button style='display:none'>Apply</button>" + "<button type='reset' style='display:none'>Reset</button>" + "<label class='clickable" + (config.moduleId.length ? "" : " checked") + "'><input type='checkbox'" + (config.moduleId.length ? "" : " checked='checked'") + ">All modules</label>";
+               allCheckbox = actions.lastChild.firstChild;
+               commit = actions.firstChild;
+               reset = commit.nextSibling;
+               addEvent(commit, "click", applyUrlParams);
+
+               dropDownList.id = "qunit-modulefilter-dropdown-list";
+               dropDownList.innerHTML = moduleListHtml();
+
+               dropDown.id = "qunit-modulefilter-dropdown";
+               dropDown.style.display = "none";
+               dropDown.appendChild(actions);
+               dropDown.appendChild(dropDownList);
+               addEvent(dropDown, "change", selectionChange);
+               selectionChange();
+
+               moduleFilter.id = "qunit-modulefilter";
+               moduleFilter.appendChild(label);
+               moduleFilter.appendChild(dropDown);
+               addEvent(moduleFilter, "submit", interceptNavigation);
+               addEvent(moduleFilter, "reset", function () {
+
+                       // Let the reset happen, then update styles
+                       window.setTimeout(selectionChange);
+               });
+
+               // Enables show/hide for the dropdown
+               function searchFocus() {
+                       if (dropDown.style.display !== "none") {
+                               return;
+                       }
+
+                       dropDown.style.display = "block";
+                       addEvent(document$$1, "click", hideHandler);
+                       addEvent(document$$1, "keydown", hideHandler);
+
+                       // Hide on Escape keydown or outside-container click
+                       function hideHandler(e) {
+                               var inContainer = moduleFilter.contains(e.target);
+
+                               if (e.keyCode === 27 || !inContainer) {
+                                       if (e.keyCode === 27 && inContainer) {
+                                               moduleSearch.focus();
+                                       }
+                                       dropDown.style.display = "none";
+                                       removeEvent(document$$1, "click", hideHandler);
+                                       removeEvent(document$$1, "keydown", hideHandler);
+                                       moduleSearch.value = "";
+                                       searchInput();
+                               }
+                       }
+               }
+
+               // Processes module search box input
+               function searchInput() {
+                       var i,
+                           item,
+                           searchText = moduleSearch.value.toLowerCase(),
+                           listItems = dropDownList.children;
+
+                       for (i = 0; i < listItems.length; i++) {
+                               item = listItems[i];
+                               if (!searchText || item.textContent.toLowerCase().indexOf(searchText) > -1) {
+                                       item.style.display = "";
+                               } else {
+                                       item.style.display = "none";
+                               }
+                       }
+               }
+
+               // Processes selection changes
+               function selectionChange(evt) {
+                       var i,
+                           item,
+                           checkbox = evt && evt.target || allCheckbox,
+                           modulesList = dropDownList.getElementsByTagName("input"),
+                           selectedNames = [];
+
+                       toggleClass(checkbox.parentNode, "checked", checkbox.checked);
+
+                       dirty = false;
+                       if (checkbox.checked && checkbox !== allCheckbox) {
+                               allCheckbox.checked = false;
+                               removeClass(allCheckbox.parentNode, "checked");
+                       }
+                       for (i = 0; i < modulesList.length; i++) {
+                               item = modulesList[i];
+                               if (!evt) {
+                                       toggleClass(item.parentNode, "checked", item.checked);
+                               } else if (checkbox === allCheckbox && checkbox.checked) {
+                                       item.checked = false;
+                                       removeClass(item.parentNode, "checked");
+                               }
+                               dirty = dirty || item.checked !== item.defaultChecked;
+                               if (item.checked) {
+                                       selectedNames.push(item.parentNode.textContent);
+                               }
+                       }
+
+                       commit.style.display = reset.style.display = dirty ? "" : "none";
+                       moduleSearch.placeholder = selectedNames.join(", ") || allCheckbox.parentNode.textContent;
+                       moduleSearch.title = "Type to filter list. Current selection:\n" + (selectedNames.join("\n") || allCheckbox.parentNode.textContent);
+               }
+
+               return moduleFilter;
+       }
+
+       function appendToolbar() {
+               var toolbar = id("qunit-testrunner-toolbar");
+
+               if (toolbar) {
+                       toolbar.appendChild(toolbarUrlConfigContainer());
+                       toolbar.appendChild(toolbarModuleFilter());
+                       toolbar.appendChild(toolbarLooseFilter());
+                       toolbar.appendChild(document$$1.createElement("div")).className = "clearfix";
+               }
+       }
+
+       function appendHeader() {
+               var header = id("qunit-header");
+
+               if (header) {
+                       header.innerHTML = "<a href='" + escapeText(unfilteredUrl) + "'>" + header.innerHTML + "</a> ";
+               }
+       }
+
+       function appendBanner() {
+               var banner = id("qunit-banner");
+
+               if (banner) {
+                       banner.className = "";
+               }
+       }
+
+       function appendTestResults() {
+               var tests = id("qunit-tests"),
+                   result = id("qunit-testresult"),
+                   controls;
+
+               if (result) {
+                       result.parentNode.removeChild(result);
+               }
+
+               if (tests) {
+                       tests.innerHTML = "";
+                       result = document$$1.createElement("p");
+                       result.id = "qunit-testresult";
+                       result.className = "result";
+                       tests.parentNode.insertBefore(result, tests);
+                       result.innerHTML = "<div id=\"qunit-testresult-display\">Running...<br />&#160;</div>" + "<div id=\"qunit-testresult-controls\"></div>" + "<div class=\"clearfix\"></div>";
+                       controls = id("qunit-testresult-controls");
+               }
+
+               if (controls) {
+                       controls.appendChild(abortTestsButton());
+               }
+       }
+
+       function appendFilteredTest() {
+               var testId = QUnit.config.testId;
+               if (!testId || testId.length <= 0) {
+                       return "";
+               }
+               return "<div id='qunit-filteredTest'>Rerunning selected tests: " + escapeText(testId.join(", ")) + " <a id='qunit-clearFilter' href='" + escapeText(unfilteredUrl) + "'>Run all tests</a></div>";
+       }
+
+       function appendUserAgent() {
+               var userAgent = id("qunit-userAgent");
+
+               if (userAgent) {
+                       userAgent.innerHTML = "";
+                       userAgent.appendChild(document$$1.createTextNode("QUnit " + QUnit.version + "; " + navigator.userAgent));
+               }
+       }
+
+       function appendInterface() {
+               var qunit = id("qunit");
+
+               if (qunit) {
+                       qunit.innerHTML = "<h1 id='qunit-header'>" + escapeText(document$$1.title) + "</h1>" + "<h2 id='qunit-banner'></h2>" + "<div id='qunit-testrunner-toolbar'></div>" + appendFilteredTest() + "<h2 id='qunit-userAgent'></h2>" + "<ol id='qunit-tests'></ol>";
+               }
+
+               appendHeader();
+               appendBanner();
+               appendTestResults();
+               appendUserAgent();
+               appendToolbar();
+       }
+
+       function appendTestsList(modules) {
+               var i, l, x, z, test, moduleObj;
+
+               for (i = 0, l = modules.length; i < l; i++) {
+                       moduleObj = modules[i];
+
+                       for (x = 0, z = moduleObj.tests.length; x < z; x++) {
+                               test = moduleObj.tests[x];
+
+                               appendTest(test.name, test.testId, moduleObj.name);
+                       }
+               }
+       }
+
+       function appendTest(name, testId, moduleName) {
+               var title,
+                   rerunTrigger,
+                   testBlock,
+                   assertList,
+                   tests = id("qunit-tests");
+
+               if (!tests) {
+                       return;
+               }
+
+               title = document$$1.createElement("strong");
+               title.innerHTML = getNameHtml(name, moduleName);
+
+               rerunTrigger = document$$1.createElement("a");
+               rerunTrigger.innerHTML = "Rerun";
+               rerunTrigger.href = setUrl({ testId: testId });
+
+               testBlock = document$$1.createElement("li");
+               testBlock.appendChild(title);
+               testBlock.appendChild(rerunTrigger);
+               testBlock.id = "qunit-test-output-" + testId;
+
+               assertList = document$$1.createElement("ol");
+               assertList.className = "qunit-assert-list";
+
+               testBlock.appendChild(assertList);
+
+               tests.appendChild(testBlock);
+       }
+
+       // HTML Reporter initialization and load
+       QUnit.begin(function (details) {
+               var i, moduleObj, tests;
+
+               // Sort modules by name for the picker
+               for (i = 0; i < details.modules.length; i++) {
+                       moduleObj = details.modules[i];
+                       if (moduleObj.name) {
+                               modulesList.push(moduleObj.name);
+                       }
+               }
+               modulesList.sort(function (a, b) {
+                       return a.localeCompare(b);
+               });
+
+               // Initialize QUnit elements
+               appendInterface();
+               appendTestsList(details.modules);
+               tests = id("qunit-tests");
+               if (tests && config.hidepassed) {
+                       addClass(tests, "hidepass");
+               }
+       });
+
+       QUnit.done(function (details) {
+               var banner = id("qunit-banner"),
+                   tests = id("qunit-tests"),
+                   abortButton = id("qunit-abort-tests-button"),
+                   totalTests = stats.passedTests + stats.skippedTests + stats.todoTests + stats.failedTests,
+                   html = [totalTests, " tests completed in ", details.runtime, " milliseconds, with ", stats.failedTests, " failed, ", stats.skippedTests, " skipped, and ", stats.todoTests, " todo.<br />", "<span class='passed'>", details.passed, "</span> assertions of <span class='total'>", details.total, "</span> passed, <span class='failed'>", details.failed, "</span> failed."].join(""),
+                   test,
+                   assertLi,
+                   assertList;
+
+               // Update remaing tests to aborted
+               if (abortButton && abortButton.disabled) {
+                       html = "Tests aborted after " + details.runtime + " milliseconds.";
+
+                       for (var i = 0; i < tests.children.length; i++) {
+                               test = tests.children[i];
+                               if (test.className === "" || test.className === "running") {
+                                       test.className = "aborted";
+                                       assertList = test.getElementsByTagName("ol")[0];
+                                       assertLi = document$$1.createElement("li");
+                                       assertLi.className = "fail";
+                                       assertLi.innerHTML = "Test aborted.";
+                                       assertList.appendChild(assertLi);
+                               }
+                       }
+               }
+
+               if (banner && (!abortButton || abortButton.disabled === false)) {
+                       banner.className = stats.failedTests ? "qunit-fail" : "qunit-pass";
+               }
+
+               if (abortButton) {
+                       abortButton.parentNode.removeChild(abortButton);
+               }
+
+               if (tests) {
+                       id("qunit-testresult-display").innerHTML = html;
+               }
+
+               if (config.altertitle && document$$1.title) {
+
+                       // Show ✖ for good, ✔ for bad suite result in title
+                       // use escape sequences in case file gets loaded with non-utf-8-charset
+                       document$$1.title = [stats.failedTests ? "\u2716" : "\u2714", document$$1.title.replace(/^[\u2714\u2716] /i, "")].join(" ");
+               }
+
+               // Scroll back to top to show results
+               if (config.scrolltop && window.scrollTo) {
+                       window.scrollTo(0, 0);
+               }
+       });
+
+       function getNameHtml(name, module) {
+               var nameHtml = "";
+
+               if (module) {
+                       nameHtml = "<span class='module-name'>" + escapeText(module) + "</span>: ";
+               }
+
+               nameHtml += "<span class='test-name'>" + escapeText(name) + "</span>";
+
+               return nameHtml;
+       }
+
+       QUnit.testStart(function (details) {
+               var running, testBlock, bad;
+
+               testBlock = id("qunit-test-output-" + details.testId);
+               if (testBlock) {
+                       testBlock.className = "running";
+               } else {
+
+                       // Report later registered tests
+                       appendTest(details.name, details.testId, details.module);
+               }
+
+               running = id("qunit-testresult-display");
+               if (running) {
+                       bad = QUnit.config.reorder && details.previousFailure;
+
+                       running.innerHTML = (bad ? "Rerunning previously failed test: <br />" : "Running: <br />") + getNameHtml(details.name, details.module);
+               }
+       });
+
+       function stripHtml(string) {
+
+               // Strip tags, html entity and whitespaces
+               return string.replace(/<\/?[^>]+(>|$)/g, "").replace(/\&quot;/g, "").replace(/\s+/g, "");
+       }
+
+       QUnit.log(function (details) {
+               var assertList,
+                   assertLi,
+                   message,
+                   expected,
+                   actual,
+                   diff,
+                   showDiff = false,
+                   testItem = id("qunit-test-output-" + details.testId);
+
+               if (!testItem) {
+                       return;
+               }
+
+               message = escapeText(details.message) || (details.result ? "okay" : "failed");
+               message = "<span class='test-message'>" + message + "</span>";
+               message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
+
+               // The pushFailure doesn't provide details.expected
+               // when it calls, it's implicit to also not show expected and diff stuff
+               // Also, we need to check details.expected existence, as it can exist and be undefined
+               if (!details.result && hasOwn.call(details, "expected")) {
+                       if (details.negative) {
+                               expected = "NOT " + QUnit.dump.parse(details.expected);
+                       } else {
+                               expected = QUnit.dump.parse(details.expected);
+                       }
+
+                       actual = QUnit.dump.parse(details.actual);
+                       message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + escapeText(expected) + "</pre></td></tr>";
+
+                       if (actual !== expected) {
+
+                               message += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText(actual) + "</pre></td></tr>";
+
+                               if (typeof details.actual === "number" && typeof details.expected === "number") {
+                                       if (!isNaN(details.actual) && !isNaN(details.expected)) {
+                                               showDiff = true;
+                                               diff = details.actual - details.expected;
+                                               diff = (diff > 0 ? "+" : "") + diff;
+                                       }
+                               } else if (typeof details.actual !== "boolean" && typeof details.expected !== "boolean") {
+                                       diff = QUnit.diff(expected, actual);
+
+                                       // don't show diff if there is zero overlap
+                                       showDiff = stripHtml(diff).length !== stripHtml(expected).length + stripHtml(actual).length;
+                               }
+
+                               if (showDiff) {
+                                       message += "<tr class='test-diff'><th>Diff: </th><td><pre>" + diff + "</pre></td></tr>";
+                               }
+                       } else if (expected.indexOf("[object Array]") !== -1 || expected.indexOf("[object Object]") !== -1) {
+                               message += "<tr class='test-message'><th>Message: </th><td>" + "Diff suppressed as the depth of object is more than current max depth (" + QUnit.config.maxDepth + ").<p>Hint: Use <code>QUnit.dump.maxDepth</code> to " + " run with a higher max depth or <a href='" + escapeText(setUrl({ maxDepth: -1 })) + "'>" + "Rerun</a> without max depth.</p></td></tr>";
+                       } else {
+                               message += "<tr class='test-message'><th>Message: </th><td>" + "Diff suppressed as the expected and actual results have an equivalent" + " serialization</td></tr>";
+                       }
+
+                       if (details.source) {
+                               message += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + "</pre></td></tr>";
+                       }
+
+                       message += "</table>";
+
+                       // This occurs when pushFailure is set and we have an extracted stack trace
+               } else if (!details.result && details.source) {
+                       message += "<table>" + "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + "</pre></td></tr>" + "</table>";
+               }
+
+               assertList = testItem.getElementsByTagName("ol")[0];
+
+               assertLi = document$$1.createElement("li");
+               assertLi.className = details.result ? "pass" : "fail";
+               assertLi.innerHTML = message;
+               assertList.appendChild(assertLi);
+       });
+
+       QUnit.testDone(function (details) {
+               var testTitle,
+                   time,
+                   testItem,
+                   assertList,
+                   good,
+                   bad,
+                   testCounts,
+                   skipped,
+                   sourceName,
+                   tests = id("qunit-tests");
+
+               if (!tests) {
+                       return;
+               }
+
+               testItem = id("qunit-test-output-" + details.testId);
+
+               assertList = testItem.getElementsByTagName("ol")[0];
+
+               good = details.passed;
+               bad = details.failed;
+
+               // This test passed if it has no unexpected failed assertions
+               var testPassed = details.failed > 0 ? details.todo : !details.todo;
+
+               if (testPassed) {
+
+                       // Collapse the passing tests
+                       addClass(assertList, "qunit-collapsed");
+               } else if (config.collapse) {
+                       if (!collapseNext) {
+
+                               // Skip collapsing the first failing test
+                               collapseNext = true;
+                       } else {
+
+                               // Collapse remaining tests
+                               addClass(assertList, "qunit-collapsed");
+                       }
+               }
+
+               // The testItem.firstChild is the test name
+               testTitle = testItem.firstChild;
+
+               testCounts = bad ? "<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " : "";
+
+               testTitle.innerHTML += " <b class='counts'>(" + testCounts + details.assertions.length + ")</b>";
+
+               if (details.skipped) {
+                       stats.skippedTests++;
+
+                       testItem.className = "skipped";
+                       skipped = document$$1.createElement("em");
+                       skipped.className = "qunit-skipped-label";
+                       skipped.innerHTML = "skipped";
+                       testItem.insertBefore(skipped, testTitle);
+               } else {
+                       addEvent(testTitle, "click", function () {
+                               toggleClass(assertList, "qunit-collapsed");
+                       });
+
+                       testItem.className = testPassed ? "pass" : "fail";
+
+                       if (details.todo) {
+                               var todoLabel = document$$1.createElement("em");
+                               todoLabel.className = "qunit-todo-label";
+                               todoLabel.innerHTML = "todo";
+                               testItem.className += " todo";
+                               testItem.insertBefore(todoLabel, testTitle);
+                       }
+
+                       time = document$$1.createElement("span");
+                       time.className = "runtime";
+                       time.innerHTML = details.runtime + " ms";
+                       testItem.insertBefore(time, assertList);
+
+                       if (!testPassed) {
+                               stats.failedTests++;
+                       } else if (details.todo) {
+                               stats.todoTests++;
+                       } else {
+                               stats.passedTests++;
+                       }
+               }
+
+               // Show the source of the test when showing assertions
+               if (details.source) {
+                       sourceName = document$$1.createElement("p");
+                       sourceName.innerHTML = "<strong>Source: </strong>" + details.source;
+                       addClass(sourceName, "qunit-source");
+                       if (testPassed) {
+                               addClass(sourceName, "qunit-collapsed");
+                       }
+                       addEvent(testTitle, "click", function () {
+                               toggleClass(sourceName, "qunit-collapsed");
+                       });
+                       testItem.appendChild(sourceName);
+               }
+       });
+
+       // Avoid readyState issue with phantomjs
+       // Ref: #818
+       var notPhantom = function (p) {
+               return !(p && p.version && p.version.major > 0);
+       }(window.phantom);
+
+       if (notPhantom && document$$1.readyState === "complete") {
+               QUnit.load();
+       } else {
+               addEvent(window, "load", QUnit.load);
+       }
+
+       // Wrap window.onerror. We will call the original window.onerror to see if
+       // the existing handler fully handles the error; if not, we will call the
+       // QUnit.onError function.
+       var originalWindowOnError = window.onerror;
+
+       // Cover uncaught exceptions
+       // Returning true will suppress the default browser handler,
+       // returning false will let it run.
+       window.onerror = function (message, fileName, lineNumber) {
+               var ret = false;
+               if (originalWindowOnError) {
+                       for (var _len = arguments.length, args = Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) {
+                               args[_key - 3] = arguments[_key];
+                       }
+
+                       ret = originalWindowOnError.call.apply(originalWindowOnError, [this, message, fileName, lineNumber].concat(args));
+               }
+
+               // Treat return value as window.onerror itself does,
+               // Only do our handling if not suppressed.
+               if (ret !== true) {
+                       var error = {
+                               message: message,
+                               fileName: fileName,
+                               lineNumber: lineNumber
+                       };
+
+                       ret = QUnit.onError(error);
+               }
+
+               return ret;
+       };
+  })();
+
+  /*
+   * This file is a modified version of google-diff-match-patch's JavaScript implementation
+   * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js),
+   * modifications are licensed as more fully set forth in LICENSE.txt.
+   *
+   * The original source of google-diff-match-patch is attributable and licensed as follows:
+   *
+   * Copyright 2006 Google Inc.
+   * https://code.google.com/p/google-diff-match-patch/
+   *
+   * Licensed under the Apache License, Version 2.0 (the "License");
+   * you may not use this file except in compliance with the License.
+   * You may obtain a copy of the License at
+   *
+   * https://www.apache.org/licenses/LICENSE-2.0
+   *
+   * Unless required by applicable law or agreed to in writing, software
+   * distributed under the License is distributed on an "AS IS" BASIS,
+   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   * See the License for the specific language governing permissions and
+   * limitations under the License.
+   *
+   * More Info:
+   *  https://code.google.com/p/google-diff-match-patch/
+   *
+   * Usage: QUnit.diff(expected, actual)
+   *
+   */
+  QUnit.diff = function () {
+       function DiffMatchPatch() {}
+
+       //  DIFF FUNCTIONS
+
+       /**
+    * The data structure representing a diff is an array of tuples:
+    * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
+    * which means: delete 'Hello', add 'Goodbye' and keep ' world.'
+    */
+       var DIFF_DELETE = -1,
+           DIFF_INSERT = 1,
+           DIFF_EQUAL = 0;
+
+       /**
+    * Find the differences between two texts.  Simplifies the problem by stripping
+    * any common prefix or suffix off the texts before diffing.
+    * @param {string} text1 Old string to be diffed.
+    * @param {string} text2 New string to be diffed.
+    * @param {boolean=} optChecklines Optional speedup flag. If present and false,
+    *     then don't run a line-level diff first to identify the changed areas.
+    *     Defaults to true, which does a faster, slightly less optimal diff.
+    * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+    */
+       DiffMatchPatch.prototype.DiffMain = function (text1, text2, optChecklines) {
+               var deadline, checklines, commonlength, commonprefix, commonsuffix, diffs;
+
+               // The diff must be complete in up to 1 second.
+               deadline = new Date().getTime() + 1000;
+
+               // Check for null inputs.
+               if (text1 === null || text2 === null) {
+                       throw new Error("Null input. (DiffMain)");
+               }
+
+               // Check for equality (speedup).
+               if (text1 === text2) {
+                       if (text1) {
+                               return [[DIFF_EQUAL, text1]];
+                       }
+                       return [];
+               }
+
+               if (typeof optChecklines === "undefined") {
+                       optChecklines = true;
+               }
+
+               checklines = optChecklines;
+
+               // Trim off common prefix (speedup).
+               commonlength = this.diffCommonPrefix(text1, text2);
+               commonprefix = text1.substring(0, commonlength);
+               text1 = text1.substring(commonlength);
+               text2 = text2.substring(commonlength);
+
+               // Trim off common suffix (speedup).
+               commonlength = this.diffCommonSuffix(text1, text2);
+               commonsuffix = text1.substring(text1.length - commonlength);
+               text1 = text1.substring(0, text1.length - commonlength);
+               text2 = text2.substring(0, text2.length - commonlength);
+
+               // Compute the diff on the middle block.
+               diffs = this.diffCompute(text1, text2, checklines, deadline);
+
+               // Restore the prefix and suffix.
+               if (commonprefix) {
+                       diffs.unshift([DIFF_EQUAL, commonprefix]);
+               }
+               if (commonsuffix) {
+                       diffs.push([DIFF_EQUAL, commonsuffix]);
+               }
+               this.diffCleanupMerge(diffs);
+               return diffs;
+       };
+
+       /**
+    * Reduce the number of edits by eliminating operationally trivial equalities.
+    * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+    */
+       DiffMatchPatch.prototype.diffCleanupEfficiency = function (diffs) {
+               var changes, equalities, equalitiesLength, lastequality, pointer, preIns, preDel, postIns, postDel;
+               changes = false;
+               equalities = []; // Stack of indices where equalities are found.
+               equalitiesLength = 0; // Keeping our own length var is faster in JS.
+               /** @type {?string} */
+               lastequality = null;
+
+               // Always equal to diffs[equalities[equalitiesLength - 1]][1]
+               pointer = 0; // Index of current position.
+
+               // Is there an insertion operation before the last equality.
+               preIns = false;
+
+               // Is there a deletion operation before the last equality.
+               preDel = false;
+
+               // Is there an insertion operation after the last equality.
+               postIns = false;
+
+               // Is there a deletion operation after the last equality.
+               postDel = false;
+               while (pointer < diffs.length) {
+
+                       // Equality found.
+                       if (diffs[pointer][0] === DIFF_EQUAL) {
+                               if (diffs[pointer][1].length < 4 && (postIns || postDel)) {
+
+                                       // Candidate found.
+                                       equalities[equalitiesLength++] = pointer;
+                                       preIns = postIns;
+                                       preDel = postDel;
+                                       lastequality = diffs[pointer][1];
+                               } else {
+
+                                       // Not a candidate, and can never become one.
+                                       equalitiesLength = 0;
+                                       lastequality = null;
+                               }
+                               postIns = postDel = false;
+
+                               // An insertion or deletion.
+                       } else {
+
+                               if (diffs[pointer][0] === DIFF_DELETE) {
+                                       postDel = true;
+                               } else {
+                                       postIns = true;
+                               }
+
+                               /*
+       * Five types to be split:
+       * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
+       * <ins>A</ins>X<ins>C</ins><del>D</del>
+       * <ins>A</ins><del>B</del>X<ins>C</ins>
+       * <ins>A</del>X<ins>C</ins><del>D</del>
+       * <ins>A</ins><del>B</del>X<del>C</del>
+       */
+                               if (lastequality && (preIns && preDel && postIns && postDel || lastequality.length < 2 && preIns + preDel + postIns + postDel === 3)) {
+
+                                       // Duplicate record.
+                                       diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]);
+
+                                       // Change second copy to insert.
+                                       diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
+                                       equalitiesLength--; // Throw away the equality we just deleted;
+                                       lastequality = null;
+                                       if (preIns && preDel) {
+
+                                               // No changes made which could affect previous entry, keep going.
+                                               postIns = postDel = true;
+                                               equalitiesLength = 0;
+                                       } else {
+                                               equalitiesLength--; // Throw away the previous equality.
+                                               pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
+                                               postIns = postDel = false;
+                                       }
+                                       changes = true;
+                               }
+                       }
+                       pointer++;
+               }
+
+               if (changes) {
+                       this.diffCleanupMerge(diffs);
+               }
+       };
+
+       /**
+    * Convert a diff array into a pretty HTML report.
+    * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+    * @param {integer} string to be beautified.
+    * @return {string} HTML representation.
+    */
+       DiffMatchPatch.prototype.diffPrettyHtml = function (diffs) {
+               var op,
+                   data,
+                   x,
+                   html = [];
+               for (x = 0; x < diffs.length; x++) {
+                       op = diffs[x][0]; // Operation (insert, delete, equal)
+                       data = diffs[x][1]; // Text of change.
+                       switch (op) {
+                               case DIFF_INSERT:
+                                       html[x] = "<ins>" + escapeText(data) + "</ins>";
+                                       break;
+                               case DIFF_DELETE:
+                                       html[x] = "<del>" + escapeText(data) + "</del>";
+                                       break;
+                               case DIFF_EQUAL:
+                                       html[x] = "<span>" + escapeText(data) + "</span>";
+                                       break;
+                       }
+               }
+               return html.join("");
+       };
+
+       /**
+    * Determine the common prefix of two strings.
+    * @param {string} text1 First string.
+    * @param {string} text2 Second string.
+    * @return {number} The number of characters common to the start of each
+    *     string.
+    */
+       DiffMatchPatch.prototype.diffCommonPrefix = function (text1, text2) {
+               var pointermid, pointermax, pointermin, pointerstart;
+
+               // Quick check for common null cases.
+               if (!text1 || !text2 || text1.charAt(0) !== text2.charAt(0)) {
+                       return 0;
+               }
+
+               // Binary search.
+               // Performance analysis: https://neil.fraser.name/news/2007/10/09/
+               pointermin = 0;
+               pointermax = Math.min(text1.length, text2.length);
+               pointermid = pointermax;
+               pointerstart = 0;
+               while (pointermin < pointermid) {
+                       if (text1.substring(pointerstart, pointermid) === text2.substring(pointerstart, pointermid)) {
+                               pointermin = pointermid;
+                               pointerstart = pointermin;
+                       } else {
+                               pointermax = pointermid;
+                       }
+                       pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
+               }
+               return pointermid;
+       };
+
+       /**
+    * Determine the common suffix of two strings.
+    * @param {string} text1 First string.
+    * @param {string} text2 Second string.
+    * @return {number} The number of characters common to the end of each string.
+    */
+       DiffMatchPatch.prototype.diffCommonSuffix = function (text1, text2) {
+               var pointermid, pointermax, pointermin, pointerend;
+
+               // Quick check for common null cases.
+               if (!text1 || !text2 || text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1)) {
+                       return 0;
+               }
+
+               // Binary search.
+               // Performance analysis: https://neil.fraser.name/news/2007/10/09/
+               pointermin = 0;
+               pointermax = Math.min(text1.length, text2.length);
+               pointermid = pointermax;
+               pointerend = 0;
+               while (pointermin < pointermid) {
+                       if (text1.substring(text1.length - pointermid, text1.length - pointerend) === text2.substring(text2.length - pointermid, text2.length - pointerend)) {
+                               pointermin = pointermid;
+                               pointerend = pointermin;
+                       } else {
+                               pointermax = pointermid;
+                       }
+                       pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
+               }
+               return pointermid;
+       };
+
+       /**
+    * Find the differences between two texts.  Assumes that the texts do not
+    * have any common prefix or suffix.
+    * @param {string} text1 Old string to be diffed.
+    * @param {string} text2 New string to be diffed.
+    * @param {boolean} checklines Speedup flag.  If false, then don't run a
+    *     line-level diff first to identify the changed areas.
+    *     If true, then run a faster, slightly less optimal diff.
+    * @param {number} deadline Time when the diff should be complete by.
+    * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffCompute = function (text1, text2, checklines, deadline) {
+               var diffs, longtext, shorttext, i, hm, text1A, text2A, text1B, text2B, midCommon, diffsA, diffsB;
+
+               if (!text1) {
+
+                       // Just add some text (speedup).
+                       return [[DIFF_INSERT, text2]];
+               }
+
+               if (!text2) {
+
+                       // Just delete some text (speedup).
+                       return [[DIFF_DELETE, text1]];
+               }
+
+               longtext = text1.length > text2.length ? text1 : text2;
+               shorttext = text1.length > text2.length ? text2 : text1;
+               i = longtext.indexOf(shorttext);
+               if (i !== -1) {
+
+                       // Shorter text is inside the longer text (speedup).
+                       diffs = [[DIFF_INSERT, longtext.substring(0, i)], [DIFF_EQUAL, shorttext], [DIFF_INSERT, longtext.substring(i + shorttext.length)]];
+
+                       // Swap insertions for deletions if diff is reversed.
+                       if (text1.length > text2.length) {
+                               diffs[0][0] = diffs[2][0] = DIFF_DELETE;
+                       }
+                       return diffs;
+               }
+
+               if (shorttext.length === 1) {
+
+                       // Single character string.
+                       // After the previous speedup, the character can't be an equality.
+                       return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
+               }
+
+               // Check to see if the problem can be split in two.
+               hm = this.diffHalfMatch(text1, text2);
+               if (hm) {
+
+                       // A half-match was found, sort out the return data.
+                       text1A = hm[0];
+                       text1B = hm[1];
+                       text2A = hm[2];
+                       text2B = hm[3];
+                       midCommon = hm[4];
+
+                       // Send both pairs off for separate processing.
+                       diffsA = this.DiffMain(text1A, text2A, checklines, deadline);
+                       diffsB = this.DiffMain(text1B, text2B, checklines, deadline);
+
+                       // Merge the results.
+                       return diffsA.concat([[DIFF_EQUAL, midCommon]], diffsB);
+               }
+
+               if (checklines && text1.length > 100 && text2.length > 100) {
+                       return this.diffLineMode(text1, text2, deadline);
+               }
+
+               return this.diffBisect(text1, text2, deadline);
+       };
+
+       /**
+    * Do the two texts share a substring which is at least half the length of the
+    * longer text?
+    * This speedup can produce non-minimal diffs.
+    * @param {string} text1 First string.
+    * @param {string} text2 Second string.
+    * @return {Array.<string>} Five element Array, containing the prefix of
+    *     text1, the suffix of text1, the prefix of text2, the suffix of
+    *     text2 and the common middle.  Or null if there was no match.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffHalfMatch = function (text1, text2) {
+               var longtext, shorttext, dmp, text1A, text2B, text2A, text1B, midCommon, hm1, hm2, hm;
+
+               longtext = text1.length > text2.length ? text1 : text2;
+               shorttext = text1.length > text2.length ? text2 : text1;
+               if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {
+                       return null; // Pointless.
+               }
+               dmp = this; // 'this' becomes 'window' in a closure.
+
+               /**
+     * Does a substring of shorttext exist within longtext such that the substring
+     * is at least half the length of longtext?
+     * Closure, but does not reference any external variables.
+     * @param {string} longtext Longer string.
+     * @param {string} shorttext Shorter string.
+     * @param {number} i Start index of quarter length substring within longtext.
+     * @return {Array.<string>} Five element Array, containing the prefix of
+     *     longtext, the suffix of longtext, the prefix of shorttext, the suffix
+     *     of shorttext and the common middle.  Or null if there was no match.
+     * @private
+     */
+               function diffHalfMatchI(longtext, shorttext, i) {
+                       var seed, j, bestCommon, prefixLength, suffixLength, bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB;
+
+                       // Start with a 1/4 length substring at position i as a seed.
+                       seed = longtext.substring(i, i + Math.floor(longtext.length / 4));
+                       j = -1;
+                       bestCommon = "";
+                       while ((j = shorttext.indexOf(seed, j + 1)) !== -1) {
+                               prefixLength = dmp.diffCommonPrefix(longtext.substring(i), shorttext.substring(j));
+                               suffixLength = dmp.diffCommonSuffix(longtext.substring(0, i), shorttext.substring(0, j));
+                               if (bestCommon.length < suffixLength + prefixLength) {
+                                       bestCommon = shorttext.substring(j - suffixLength, j) + shorttext.substring(j, j + prefixLength);
+                                       bestLongtextA = longtext.substring(0, i - suffixLength);
+                                       bestLongtextB = longtext.substring(i + prefixLength);
+                                       bestShorttextA = shorttext.substring(0, j - suffixLength);
+                                       bestShorttextB = shorttext.substring(j + prefixLength);
+                               }
+                       }
+                       if (bestCommon.length * 2 >= longtext.length) {
+                               return [bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB, bestCommon];
+                       } else {
+                               return null;
+                       }
+               }
+
+               // First check if the second quarter is the seed for a half-match.
+               hm1 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 4));
+
+               // Check again based on the third quarter.
+               hm2 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 2));
+               if (!hm1 && !hm2) {
+                       return null;
+               } else if (!hm2) {
+                       hm = hm1;
+               } else if (!hm1) {
+                       hm = hm2;
+               } else {
+
+                       // Both matched.  Select the longest.
+                       hm = hm1[4].length > hm2[4].length ? hm1 : hm2;
+               }
+
+               // A half-match was found, sort out the return data.
+               if (text1.length > text2.length) {
+                       text1A = hm[0];
+                       text1B = hm[1];
+                       text2A = hm[2];
+                       text2B = hm[3];
+               } else {
+                       text2A = hm[0];
+                       text2B = hm[1];
+                       text1A = hm[2];
+                       text1B = hm[3];
+               }
+               midCommon = hm[4];
+               return [text1A, text1B, text2A, text2B, midCommon];
+       };
+
+       /**
+    * Do a quick line-level diff on both strings, then rediff the parts for
+    * greater accuracy.
+    * This speedup can produce non-minimal diffs.
+    * @param {string} text1 Old string to be diffed.
+    * @param {string} text2 New string to be diffed.
+    * @param {number} deadline Time when the diff should be complete by.
+    * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffLineMode = function (text1, text2, deadline) {
+               var a, diffs, linearray, pointer, countInsert, countDelete, textInsert, textDelete, j;
+
+               // Scan the text on a line-by-line basis first.
+               a = this.diffLinesToChars(text1, text2);
+               text1 = a.chars1;
+               text2 = a.chars2;
+               linearray = a.lineArray;
+
+               diffs = this.DiffMain(text1, text2, false, deadline);
+
+               // Convert the diff back to original text.
+               this.diffCharsToLines(diffs, linearray);
+
+               // Eliminate freak matches (e.g. blank lines)
+               this.diffCleanupSemantic(diffs);
+
+               // Rediff any replacement blocks, this time character-by-character.
+               // Add a dummy entry at the end.
+               diffs.push([DIFF_EQUAL, ""]);
+               pointer = 0;
+               countDelete = 0;
+               countInsert = 0;
+               textDelete = "";
+               textInsert = "";
+               while (pointer < diffs.length) {
+                       switch (diffs[pointer][0]) {
+                               case DIFF_INSERT:
+                                       countInsert++;
+                                       textInsert += diffs[pointer][1];
+                                       break;
+                               case DIFF_DELETE:
+                                       countDelete++;
+                                       textDelete += diffs[pointer][1];
+                                       break;
+                               case DIFF_EQUAL:
+
+                                       // Upon reaching an equality, check for prior redundancies.
+                                       if (countDelete >= 1 && countInsert >= 1) {
+
+                                               // Delete the offending records and add the merged ones.
+                                               diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert);
+                                               pointer = pointer - countDelete - countInsert;
+                                               a = this.DiffMain(textDelete, textInsert, false, deadline);
+                                               for (j = a.length - 1; j >= 0; j--) {
+                                                       diffs.splice(pointer, 0, a[j]);
+                                               }
+                                               pointer = pointer + a.length;
+                                       }
+                                       countInsert = 0;
+                                       countDelete = 0;
+                                       textDelete = "";
+                                       textInsert = "";
+                                       break;
+                       }
+                       pointer++;
+               }
+               diffs.pop(); // Remove the dummy entry at the end.
+
+               return diffs;
+       };
+
+       /**
+    * Find the 'middle snake' of a diff, split the problem in two
+    * and return the recursively constructed diff.
+    * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
+    * @param {string} text1 Old string to be diffed.
+    * @param {string} text2 New string to be diffed.
+    * @param {number} deadline Time at which to bail if not yet complete.
+    * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffBisect = function (text1, text2, deadline) {
+               var text1Length, text2Length, maxD, vOffset, vLength, v1, v2, x, delta, front, k1start, k1end, k2start, k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2;
+
+               // Cache the text lengths to prevent multiple calls.
+               text1Length = text1.length;
+               text2Length = text2.length;
+               maxD = Math.ceil((text1Length + text2Length) / 2);
+               vOffset = maxD;
+               vLength = 2 * maxD;
+               v1 = new Array(vLength);
+               v2 = new Array(vLength);
+
+               // Setting all elements to -1 is faster in Chrome & Firefox than mixing
+               // integers and undefined.
+               for (x = 0; x < vLength; x++) {
+                       v1[x] = -1;
+                       v2[x] = -1;
+               }
+               v1[vOffset + 1] = 0;
+               v2[vOffset + 1] = 0;
+               delta = text1Length - text2Length;
+
+               // If the total number of characters is odd, then the front path will collide
+               // with the reverse path.
+               front = delta % 2 !== 0;
+
+               // Offsets for start and end of k loop.
+               // Prevents mapping of space beyond the grid.
+               k1start = 0;
+               k1end = 0;
+               k2start = 0;
+               k2end = 0;
+               for (d = 0; d < maxD; d++) {
+
+                       // Bail out if deadline is reached.
+                       if (new Date().getTime() > deadline) {
+                               break;
+                       }
+
+                       // Walk the front path one step.
+                       for (k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {
+                               k1Offset = vOffset + k1;
+                               if (k1 === -d || k1 !== d && v1[k1Offset - 1] < v1[k1Offset + 1]) {
+                                       x1 = v1[k1Offset + 1];
+                               } else {
+                                       x1 = v1[k1Offset - 1] + 1;
+                               }
+                               y1 = x1 - k1;
+                               while (x1 < text1Length && y1 < text2Length && text1.charAt(x1) === text2.charAt(y1)) {
+                                       x1++;
+                                       y1++;
+                               }
+                               v1[k1Offset] = x1;
+                               if (x1 > text1Length) {
+
+                                       // Ran off the right of the graph.
+                                       k1end += 2;
+                               } else if (y1 > text2Length) {
+
+                                       // Ran off the bottom of the graph.
+                                       k1start += 2;
+                               } else if (front) {
+                                       k2Offset = vOffset + delta - k1;
+                                       if (k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] !== -1) {
+
+                                               // Mirror x2 onto top-left coordinate system.
+                                               x2 = text1Length - v2[k2Offset];
+                                               if (x1 >= x2) {
+
+                                                       // Overlap detected.
+                                                       return this.diffBisectSplit(text1, text2, x1, y1, deadline);
+                                               }
+                                       }
+                               }
+                       }
+
+                       // Walk the reverse path one step.
+                       for (k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {
+                               k2Offset = vOffset + k2;
+                               if (k2 === -d || k2 !== d && v2[k2Offset - 1] < v2[k2Offset + 1]) {
+                                       x2 = v2[k2Offset + 1];
+                               } else {
+                                       x2 = v2[k2Offset - 1] + 1;
+                               }
+                               y2 = x2 - k2;
+                               while (x2 < text1Length && y2 < text2Length && text1.charAt(text1Length - x2 - 1) === text2.charAt(text2Length - y2 - 1)) {
+                                       x2++;
+                                       y2++;
+                               }
+                               v2[k2Offset] = x2;
+                               if (x2 > text1Length) {
+
+                                       // Ran off the left of the graph.
+                                       k2end += 2;
+                               } else if (y2 > text2Length) {
+
+                                       // Ran off the top of the graph.
+                                       k2start += 2;
+                               } else if (!front) {
+                                       k1Offset = vOffset + delta - k2;
+                                       if (k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] !== -1) {
+                                               x1 = v1[k1Offset];
+                                               y1 = vOffset + x1 - k1Offset;
+
+                                               // Mirror x2 onto top-left coordinate system.
+                                               x2 = text1Length - x2;
+                                               if (x1 >= x2) {
+
+                                                       // Overlap detected.
+                                                       return this.diffBisectSplit(text1, text2, x1, y1, deadline);
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               // Diff took too long and hit the deadline or
+               // number of diffs equals number of characters, no commonality at all.
+               return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
+       };
+
+       /**
+    * Given the location of the 'middle snake', split the diff in two parts
+    * and recurse.
+    * @param {string} text1 Old string to be diffed.
+    * @param {string} text2 New string to be diffed.
+    * @param {number} x Index of split point in text1.
+    * @param {number} y Index of split point in text2.
+    * @param {number} deadline Time at which to bail if not yet complete.
+    * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffBisectSplit = function (text1, text2, x, y, deadline) {
+               var text1a, text1b, text2a, text2b, diffs, diffsb;
+               text1a = text1.substring(0, x);
+               text2a = text2.substring(0, y);
+               text1b = text1.substring(x);
+               text2b = text2.substring(y);
+
+               // Compute both diffs serially.
+               diffs = this.DiffMain(text1a, text2a, false, deadline);
+               diffsb = this.DiffMain(text1b, text2b, false, deadline);
+
+               return diffs.concat(diffsb);
+       };
+
+       /**
+    * Reduce the number of edits by eliminating semantically trivial equalities.
+    * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+    */
+       DiffMatchPatch.prototype.diffCleanupSemantic = function (diffs) {
+               var changes, equalities, equalitiesLength, lastequality, pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1, lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2;
+               changes = false;
+               equalities = []; // Stack of indices where equalities are found.
+               equalitiesLength = 0; // Keeping our own length var is faster in JS.
+               /** @type {?string} */
+               lastequality = null;
+
+               // Always equal to diffs[equalities[equalitiesLength - 1]][1]
+               pointer = 0; // Index of current position.
+
+               // Number of characters that changed prior to the equality.
+               lengthInsertions1 = 0;
+               lengthDeletions1 = 0;
+
+               // Number of characters that changed after the equality.
+               lengthInsertions2 = 0;
+               lengthDeletions2 = 0;
+               while (pointer < diffs.length) {
+                       if (diffs[pointer][0] === DIFF_EQUAL) {
+                               // Equality found.
+                               equalities[equalitiesLength++] = pointer;
+                               lengthInsertions1 = lengthInsertions2;
+                               lengthDeletions1 = lengthDeletions2;
+                               lengthInsertions2 = 0;
+                               lengthDeletions2 = 0;
+                               lastequality = diffs[pointer][1];
+                       } else {
+                               // An insertion or deletion.
+                               if (diffs[pointer][0] === DIFF_INSERT) {
+                                       lengthInsertions2 += diffs[pointer][1].length;
+                               } else {
+                                       lengthDeletions2 += diffs[pointer][1].length;
+                               }
+
+                               // Eliminate an equality that is smaller or equal to the edits on both
+                               // sides of it.
+                               if (lastequality && lastequality.length <= Math.max(lengthInsertions1, lengthDeletions1) && lastequality.length <= Math.max(lengthInsertions2, lengthDeletions2)) {
+
+                                       // Duplicate record.
+                                       diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]);
+
+                                       // Change second copy to insert.
+                                       diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
+
+                                       // Throw away the equality we just deleted.
+                                       equalitiesLength--;
+
+                                       // Throw away the previous equality (it needs to be reevaluated).
+                                       equalitiesLength--;
+                                       pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
+
+                                       // Reset the counters.
+                                       lengthInsertions1 = 0;
+                                       lengthDeletions1 = 0;
+                                       lengthInsertions2 = 0;
+                                       lengthDeletions2 = 0;
+                                       lastequality = null;
+                                       changes = true;
+                               }
+                       }
+                       pointer++;
+               }
+
+               // Normalize the diff.
+               if (changes) {
+                       this.diffCleanupMerge(diffs);
+               }
+
+               // Find any overlaps between deletions and insertions.
+               // e.g: <del>abcxxx</del><ins>xxxdef</ins>
+               //   -> <del>abc</del>xxx<ins>def</ins>
+               // e.g: <del>xxxabc</del><ins>defxxx</ins>
+               //   -> <ins>def</ins>xxx<del>abc</del>
+               // Only extract an overlap if it is as big as the edit ahead or behind it.
+               pointer = 1;
+               while (pointer < diffs.length) {
+                       if (diffs[pointer - 1][0] === DIFF_DELETE && diffs[pointer][0] === DIFF_INSERT) {
+                               deletion = diffs[pointer - 1][1];
+                               insertion = diffs[pointer][1];
+                               overlapLength1 = this.diffCommonOverlap(deletion, insertion);
+                               overlapLength2 = this.diffCommonOverlap(insertion, deletion);
+                               if (overlapLength1 >= overlapLength2) {
+                                       if (overlapLength1 >= deletion.length / 2 || overlapLength1 >= insertion.length / 2) {
+
+                                               // Overlap found.  Insert an equality and trim the surrounding edits.
+                                               diffs.splice(pointer, 0, [DIFF_EQUAL, insertion.substring(0, overlapLength1)]);
+                                               diffs[pointer - 1][1] = deletion.substring(0, deletion.length - overlapLength1);
+                                               diffs[pointer + 1][1] = insertion.substring(overlapLength1);
+                                               pointer++;
+                                       }
+                               } else {
+                                       if (overlapLength2 >= deletion.length / 2 || overlapLength2 >= insertion.length / 2) {
+
+                                               // Reverse overlap found.
+                                               // Insert an equality and swap and trim the surrounding edits.
+                                               diffs.splice(pointer, 0, [DIFF_EQUAL, deletion.substring(0, overlapLength2)]);
+
+                                               diffs[pointer - 1][0] = DIFF_INSERT;
+                                               diffs[pointer - 1][1] = insertion.substring(0, insertion.length - overlapLength2);
+                                               diffs[pointer + 1][0] = DIFF_DELETE;
+                                               diffs[pointer + 1][1] = deletion.substring(overlapLength2);
+                                               pointer++;
+                                       }
+                               }
+                               pointer++;
+                       }
+                       pointer++;
+               }
+       };
+
+       /**
+    * Determine if the suffix of one string is the prefix of another.
+    * @param {string} text1 First string.
+    * @param {string} text2 Second string.
+    * @return {number} The number of characters common to the end of the first
+    *     string and the start of the second string.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffCommonOverlap = function (text1, text2) {
+               var text1Length, text2Length, textLength, best, length, pattern, found;
+
+               // Cache the text lengths to prevent multiple calls.
+               text1Length = text1.length;
+               text2Length = text2.length;
+
+               // Eliminate the null case.
+               if (text1Length === 0 || text2Length === 0) {
+                       return 0;
+               }
+
+               // Truncate the longer string.
+               if (text1Length > text2Length) {
+                       text1 = text1.substring(text1Length - text2Length);
+               } else if (text1Length < text2Length) {
+                       text2 = text2.substring(0, text1Length);
+               }
+               textLength = Math.min(text1Length, text2Length);
+
+               // Quick check for the worst case.
+               if (text1 === text2) {
+                       return textLength;
+               }
+
+               // Start by looking for a single character match
+               // and increase length until no match is found.
+               // Performance analysis: https://neil.fraser.name/news/2010/11/04/
+               best = 0;
+               length = 1;
+               while (true) {
+                       pattern = text1.substring(textLength - length);
+                       found = text2.indexOf(pattern);
+                       if (found === -1) {
+                               return best;
+                       }
+                       length += found;
+                       if (found === 0 || text1.substring(textLength - length) === text2.substring(0, length)) {
+                               best = length;
+                               length++;
+                       }
+               }
+       };
+
+       /**
+    * Split two texts into an array of strings.  Reduce the texts to a string of
+    * hashes where each Unicode character represents one line.
+    * @param {string} text1 First string.
+    * @param {string} text2 Second string.
+    * @return {{chars1: string, chars2: string, lineArray: !Array.<string>}}
+    *     An object containing the encoded text1, the encoded text2 and
+    *     the array of unique strings.
+    *     The zeroth element of the array of unique strings is intentionally blank.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffLinesToChars = function (text1, text2) {
+               var lineArray, lineHash, chars1, chars2;
+               lineArray = []; // E.g. lineArray[4] === 'Hello\n'
+               lineHash = {}; // E.g. lineHash['Hello\n'] === 4
+
+               // '\x00' is a valid character, but various debuggers don't like it.
+               // So we'll insert a junk entry to avoid generating a null character.
+               lineArray[0] = "";
+
+               /**
+     * Split a text into an array of strings.  Reduce the texts to a string of
+     * hashes where each Unicode character represents one line.
+     * Modifies linearray and linehash through being a closure.
+     * @param {string} text String to encode.
+     * @return {string} Encoded string.
+     * @private
+     */
+               function diffLinesToCharsMunge(text) {
+                       var chars, lineStart, lineEnd, lineArrayLength, line;
+                       chars = "";
+
+                       // Walk the text, pulling out a substring for each line.
+                       // text.split('\n') would would temporarily double our memory footprint.
+                       // Modifying text would create many large strings to garbage collect.
+                       lineStart = 0;
+                       lineEnd = -1;
+
+                       // Keeping our own length variable is faster than looking it up.
+                       lineArrayLength = lineArray.length;
+                       while (lineEnd < text.length - 1) {
+                               lineEnd = text.indexOf("\n", lineStart);
+                               if (lineEnd === -1) {
+                                       lineEnd = text.length - 1;
+                               }
+                               line = text.substring(lineStart, lineEnd + 1);
+                               lineStart = lineEnd + 1;
+
+                               if (lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : lineHash[line] !== undefined) {
+                                       chars += String.fromCharCode(lineHash[line]);
+                               } else {
+                                       chars += String.fromCharCode(lineArrayLength);
+                                       lineHash[line] = lineArrayLength;
+                                       lineArray[lineArrayLength++] = line;
+                               }
+                       }
+                       return chars;
+               }
+
+               chars1 = diffLinesToCharsMunge(text1);
+               chars2 = diffLinesToCharsMunge(text2);
+               return {
+                       chars1: chars1,
+                       chars2: chars2,
+                       lineArray: lineArray
+               };
+       };
+
+       /**
+    * Rehydrate the text in a diff from a string of line hashes to real lines of
+    * text.
+    * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+    * @param {!Array.<string>} lineArray Array of unique strings.
+    * @private
+    */
+       DiffMatchPatch.prototype.diffCharsToLines = function (diffs, lineArray) {
+               var x, chars, text, y;
+               for (x = 0; x < diffs.length; x++) {
+                       chars = diffs[x][1];
+                       text = [];
+                       for (y = 0; y < chars.length; y++) {
+                               text[y] = lineArray[chars.charCodeAt(y)];
+                       }
+                       diffs[x][1] = text.join("");
+               }
+       };
+
+       /**
+    * Reorder and merge like edit sections.  Merge equalities.
+    * Any edit section can move as long as it doesn't cross an equality.
+    * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+    */
+       DiffMatchPatch.prototype.diffCleanupMerge = function (diffs) {
+               var pointer, countDelete, countInsert, textInsert, textDelete, commonlength, changes, diffPointer, position;
+               diffs.push([DIFF_EQUAL, ""]); // Add a dummy entry at the end.
+               pointer = 0;
+               countDelete = 0;
+               countInsert = 0;
+               textDelete = "";
+               textInsert = "";
+
+               while (pointer < diffs.length) {
+                       switch (diffs[pointer][0]) {
+                               case DIFF_INSERT:
+                                       countInsert++;
+                                       textInsert += diffs[pointer][1];
+                                       pointer++;
+                                       break;
+                               case DIFF_DELETE:
+                                       countDelete++;
+                                       textDelete += diffs[pointer][1];
+                                       pointer++;
+                                       break;
+                               case DIFF_EQUAL:
+
+                                       // Upon reaching an equality, check for prior redundancies.
+                                       if (countDelete + countInsert > 1) {
+                                               if (countDelete !== 0 && countInsert !== 0) {
+
+                                                       // Factor out any common prefixes.
+                                                       commonlength = this.diffCommonPrefix(textInsert, textDelete);
+                                                       if (commonlength !== 0) {
+                                                               if (pointer - countDelete - countInsert > 0 && diffs[pointer - countDelete - countInsert - 1][0] === DIFF_EQUAL) {
+                                                                       diffs[pointer - countDelete - countInsert - 1][1] += textInsert.substring(0, commonlength);
+                                                               } else {
+                                                                       diffs.splice(0, 0, [DIFF_EQUAL, textInsert.substring(0, commonlength)]);
+                                                                       pointer++;
+                                                               }
+                                                               textInsert = textInsert.substring(commonlength);
+                                                               textDelete = textDelete.substring(commonlength);
+                                                       }
+
+                                                       // Factor out any common suffixies.
+                                                       commonlength = this.diffCommonSuffix(textInsert, textDelete);
+                                                       if (commonlength !== 0) {
+                                                               diffs[pointer][1] = textInsert.substring(textInsert.length - commonlength) + diffs[pointer][1];
+                                                               textInsert = textInsert.substring(0, textInsert.length - commonlength);
+                                                               textDelete = textDelete.substring(0, textDelete.length - commonlength);
+                                                       }
+                                               }
+
+                                               // Delete the offending records and add the merged ones.
+                                               if (countDelete === 0) {
+                                                       diffs.splice(pointer - countInsert, countDelete + countInsert, [DIFF_INSERT, textInsert]);
+                                               } else if (countInsert === 0) {
+                                                       diffs.splice(pointer - countDelete, countDelete + countInsert, [DIFF_DELETE, textDelete]);
+                                               } else {
+                                                       diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert, [DIFF_DELETE, textDelete], [DIFF_INSERT, textInsert]);
+                                               }
+                                               pointer = pointer - countDelete - countInsert + (countDelete ? 1 : 0) + (countInsert ? 1 : 0) + 1;
+                                       } else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) {
+
+                                               // Merge this equality with the previous one.
+                                               diffs[pointer - 1][1] += diffs[pointer][1];
+                                               diffs.splice(pointer, 1);
+                                       } else {
+                                               pointer++;
+                                       }
+                                       countInsert = 0;
+                                       countDelete = 0;
+                                       textDelete = "";
+                                       textInsert = "";
+                                       break;
+                       }
+               }
+               if (diffs[diffs.length - 1][1] === "") {
+                       diffs.pop(); // Remove the dummy entry at the end.
+               }
+
+               // Second pass: look for single edits surrounded on both sides by equalities
+               // which can be shifted sideways to eliminate an equality.
+               // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
+               changes = false;
+               pointer = 1;
+
+               // Intentionally ignore the first and last element (don't need checking).
+               while (pointer < diffs.length - 1) {
+                       if (diffs[pointer - 1][0] === DIFF_EQUAL && diffs[pointer + 1][0] === DIFF_EQUAL) {
+
+                               diffPointer = diffs[pointer][1];
+                               position = diffPointer.substring(diffPointer.length - diffs[pointer - 1][1].length);
+
+                               // This is a single edit surrounded by equalities.
+                               if (position === diffs[pointer - 1][1]) {
+
+                                       // Shift the edit over the previous equality.
+                                       diffs[pointer][1] = diffs[pointer - 1][1] + diffs[pointer][1].substring(0, diffs[pointer][1].length - diffs[pointer - 1][1].length);
+                                       diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];
+                                       diffs.splice(pointer - 1, 1);
+                                       changes = true;
+                               } else if (diffPointer.substring(0, diffs[pointer + 1][1].length) === diffs[pointer + 1][1]) {
+
+                                       // Shift the edit over the next equality.
+                                       diffs[pointer - 1][1] += diffs[pointer + 1][1];
+                                       diffs[pointer][1] = diffs[pointer][1].substring(diffs[pointer + 1][1].length) + diffs[pointer + 1][1];
+                                       diffs.splice(pointer + 1, 1);
+                                       changes = true;
+                               }
+                       }
+                       pointer++;
+               }
+
+               // If shifts were made, the diff needs reordering and another shift sweep.
+               if (changes) {
+                       this.diffCleanupMerge(diffs);
+               }
+       };
+
+       return function (o, n) {
+               var diff, output, text;
+               diff = new DiffMatchPatch();
+               output = diff.DiffMain(o, n);
+               diff.diffCleanupEfficiency(output);
+               text = diff.diffPrettyHtml(output);
+
+               return text;
+       };
+  }();
+
+}((function() { return this; }())));
index f4836aa..b6eda0f 100644 (file)
@@ -52,6 +52,7 @@
 
                this.conflicts = config.conflicts || {};
                this.defaultParams = {};
+               this.defaultFilters = {};
 
                this.aggregate( { update: 'filterItemUpdate' } );
                this.connect( this, { filterItemUpdate: 'onFilterItemUpdate' } );
@@ -89,6 +90,7 @@
                        var subsetNames = [],
                                filterItem = new mw.rcfilters.dm.FilterItem( filter.name, model, {
                                        group: model.getName(),
+                                       useDefaultAsBaseValue: !!filter.useDefaultAsBaseValue,
                                        label: filter.label || filter.name,
                                        description: filter.description || '',
                                        labelPrefixKey: model.labelPrefixKey,
                        items.push( filterItem );
 
                        // Store default parameter state; in this case, default is defined per filter
-                       if ( model.getType() === 'send_unselected_if_any' ) {
+                       if (
+                               model.getType() === 'send_unselected_if_any' ||
+                               model.getType() === 'boolean'
+                       ) {
                                // Store the default parameter state
                                // For this group type, parameter values are direct
                                // We need to convert from a boolean to a string ('1' and '0')
                        // or select the first option
                        this.selectItemByParamName( defaultParam );
                }
+
+               // Store default filter state based on default params
+               this.defaultFilters = this.getFilterRepresentation( this.getDefaultParams() );
+
+               // Check for filters that should be initially selected by their default value
+               this.getItems().forEach( function ( item ) {
+                       if (
+                               item.isUsingDefaultAsBaseValue() &&
+                               (
+                                       // This setting can only be applied to these groups
+                                       // the other groups are way too complex for that
+                                       model.getType() === 'single_option' ||
+                                       model.getType() === 'boolean'
+                               )
+                       ) {
+                               // Apply selection
+                               item.toggleSelected( !!model.defaultFilters[ item.getName() ] );
+                       }
+               } );
        };
 
        /**
                return this.defaultParams;
        };
 
+       /**
+        * Get the default filter state of this group
+        *
+        * @return {Object} Default filter state
+        */
+       mw.rcfilters.dm.FilterGroup.prototype.getDefaultFilters = function () {
+               return this.defaultFilters;
+       };
+
        /**
         * This is for a single_option and string_options group types
         * it returns the value of the default
                var values,
                        areAnySelected = false,
                        buildFromCurrentState = !filterRepresentation,
+                       defaultFilters = this.getDefaultFilters(),
                        result = {},
                        model = this,
                        filterParamNames = {},
                        } else if ( !filterRepresentation[ item.getName() ] ) {
                                // We are given a filter representation, but we have to make
                                // sure that we fill in the missing filters if there are any
-                               // we will assume they are all falsey
-                               filterRepresentation[ item.getName() ] = false;
+                               // we will assume they are all falsey, unless they have
+                               // isUsingDefaultAsBaseValue, in which case they get their
+                               // default state
+                               if (
+                                       item.isUsingDefaultAsBaseValue() &&
+                                       (
+                                               // This setting can only be applied to these groups
+                                               // the other groups are way too complex for that
+                                               model.getType() === 'single_option' ||
+                                               model.getType() === 'boolean'
+                                       )
+                               ) {
+                                       filterRepresentation[ item.getName() ] = !!defaultFilters[ item.getName() ];
+                               } else {
+                                       filterRepresentation[ item.getName() ] = false;
+                               }
                        }
 
                        if ( filterRepresentation[ item.getName() ] ) {
                } );
 
                // Build result
-               if ( this.getType() === 'send_unselected_if_any' ) {
+               if (
+                       this.getType() === 'send_unselected_if_any' ||
+                       this.getType() === 'boolean'
+               ) {
                        // First, check if any of the items are selected at all.
                        // If none is selected, we're treating it as if they are
                        // all false
                        // Go over the items and define the correct values
                        $.each( filterRepresentation, function ( name, value ) {
                                // We must store all parameter values as strings '0' or '1'
-                               result[ filterParamNames[ name ] ] = areAnySelected ?
-                                       String( Number( !value ) ) :
-                                       '0';
+                               if ( model.getType() === 'send_unselected_if_any' ) {
+                                       result[ filterParamNames[ name ] ] = areAnySelected ?
+                                               String( Number( !value ) ) :
+                                               '0';
+                               } else if ( model.getType() === 'boolean' ) {
+                                       // Representation is straight-forward and direct from
+                                       // the parameter value to the filter state
+                                       result[ filterParamNames[ name ] ] = String( Number( !!value ) );
+                               }
                        } );
                } else if ( this.getType() === 'string_options' ) {
                        values = [];
         * Get the filter representation this group would provide
         * based on given parameter states.
         *
-        * @param {Object|string} [paramRepresentation] An object defining a parameter
+        * @param {Object} [paramRepresentation] An object defining a parameter
         *  state to translate the filter state from. If not given, an object
         *  representing all filters as falsey is returned; same as if the parameter
         *  given were an empty object, or had some of the filters missing.
         * @return {Object} Filter representation
         */
        mw.rcfilters.dm.FilterGroup.prototype.getFilterRepresentation = function ( paramRepresentation ) {
-               var areAnySelected, paramValues, defaultValue, item,
+               var areAnySelected, paramValues, defaultValue, item, currentValue,
                        oneWasSelected = false,
+                       defaultParams = this.getDefaultParams(),
+                       defaultFilters = this.getDefaultFilters(),
+                       expandedParams = $.extend( true, {}, paramRepresentation ),
                        model = this,
                        paramToFilterMap = {},
                        result = {};
 
-               if ( this.getType() === 'send_unselected_if_any' ) {
-                       paramRepresentation = paramRepresentation || {};
-                       // Expand param representation to include all filters in the group
+               paramRepresentation = paramRepresentation || {};
+               if (
+                       this.getType() === 'send_unselected_if_any' ||
+                       this.getType() === 'boolean'
+               ) {
+                       // Go over param representation; map and check for selections
                        this.getItems().forEach( function ( filterItem ) {
                                var paramName = filterItem.getParamName();
 
-                               paramRepresentation[ paramName ] = paramRepresentation[ paramName ] || '0';
+                               expandedParams[ paramName ] = paramRepresentation[ paramName ] || '0';
                                paramToFilterMap[ paramName ] = filterItem;
 
                                if ( Number( paramRepresentation[ filterItem.getParamName() ] ) ) {
                                }
                        } );
 
-                       $.each( paramRepresentation, function ( paramName, paramValue ) {
-                               var filterItem = paramToFilterMap[ paramName ];
-
-                               // Flip the definition between the parameter
-                               // state and the filter state
-                               // This is what the 'toggleSelected' value of the filter is
-                               result[ filterItem.getName() ] = areAnySelected ?
-                                       !Number( paramValue ) :
-                                       // Otherwise, there are no selected items in the
-                                       // group, which means the state is false
-                                       false;
+                       $.each( expandedParams, function ( paramName, paramValue ) {
+                               var value = paramValue,
+                                       filterItem = paramToFilterMap[ paramName ];
+
+                               if ( model.getType() === 'send_unselected_if_any' ) {
+                                       // Flip the definition between the parameter
+                                       // state and the filter state
+                                       // This is what the 'toggleSelected' value of the filter is
+                                       result[ filterItem.getName() ] = areAnySelected ?
+                                               !Number( paramValue ) :
+                                               // Otherwise, there are no selected items in the
+                                               // group, which means the state is false
+                                               false;
+                               } else if ( model.getType() === 'boolean' ) {
+                                       // Straight-forward definition of state
+                                       if (
+                                               filterItem.isUsingDefaultAsBaseValue() &&
+                                               paramRepresentation[ filterItem.getParamName() ] === undefined
+                                       ) {
+                                               value = defaultParams[ filterItem.getParamName() ];
+                                       }
+                                       result[ filterItem.getName() ] = !!Number( value );
+                               }
                        } );
                } else if ( this.getType() === 'string_options' ) {
-                       paramRepresentation = paramRepresentation || '';
+                       currentValue = paramRepresentation[ this.getName() ] || '';
 
                        // Normalize the given parameter values
                        paramValues = mw.rcfilters.utils.normalizeParamOptions(
                                // Given
-                               paramRepresentation.split(
+                               currentValue.split(
                                        this.getSeparator()
                                ),
                                // Allowed values
                } else if ( this.getType() === 'single_option' ) {
                        // There is parameter that fits a single filter and if not, get the default
                        this.getItems().forEach( function ( filterItem ) {
-                               result[ filterItem.getName() ] = filterItem.getParamName() === paramRepresentation;
-                               oneWasSelected = oneWasSelected || filterItem.getParamName() === paramRepresentation;
+                               var selected = false;
+
+                               if (
+                                       filterItem.isUsingDefaultAsBaseValue() &&
+                                       paramRepresentation[ model.getName() ] === undefined
+                               ) {
+                                       selected = !!Number( paramRepresentation[ model.getName() ] );
+                               } else {
+                                       selected = filterItem.getParamName() === paramRepresentation[ model.getName() ];
+                               }
+                               result[ filterItem.getName() ] = selected;
+                               oneWasSelected = oneWasSelected || selected;
                        } );
                }
 
                // Go over result and make sure all filters are represented.
                // If any filters are missing, they will get a falsey value
                this.getItems().forEach( function ( filterItem ) {
-                       result[ filterItem.getName() ] = !!result[ filterItem.getName() ];
+                       if (
+                               (
+                                       // This setting can only be applied to these groups
+                                       // the other groups are way too complex for that
+                                       model.getType() === 'single_option' ||
+                                       model.getType() === 'boolean'
+                               ) &&
+                               result[ filterItem.getName() ] === undefined &&
+                               filterItem.isUsingDefaultAsBaseValue()
+                       ) {
+                               result[ filterItem.getName() ] = !!defaultFilters[ filterItem.getName() ];
+                       }
                        oneWasSelected = oneWasSelected || !!result[ filterItem.getName() ];
                } );
 
index a30ebbf..3281735 100644 (file)
                } );
 
                // Collect views
-               allViews = {
+               allViews = $.extend( true, {
                        'default': {
                                title: mw.msg( 'rcfilters-filterlist-title' ),
                                groups: filterGroups
                        }
-               };
-
-               if ( views && mw.config.get( 'wgStructuredChangeFiltersEnableExperimentalViews' ) ) {
-                       // If we have extended views, add them in
-                       $.extend( true, allViews, views );
-               }
+               }, views );
 
                // Go over all views
                $.each( allViews, function ( viewName, viewData ) {
 
                // Create a map between known parameters and their models
                $.each( this.groups, function ( group, groupModel ) {
-                       if ( groupModel.getType() === 'send_unselected_if_any' ) {
+                       if (
+                               groupModel.getType() === 'send_unselected_if_any' ||
+                               groupModel.getType() === 'boolean'
+                       ) {
                                // Individual filters
                                groupModel.getItems().forEach( function ( filterItem ) {
                                        model.parameterMap[ filterItem.getParamName() ] = filterItem;
                //    group2: "param4|param5"
                // }
                $.each( params, function ( paramName, paramValue ) {
-                       var itemOrGroup = model.parameterMap[ paramName ];
-
-                       if ( itemOrGroup instanceof mw.rcfilters.dm.FilterItem ) {
-                               groupMap[ itemOrGroup.getGroupName() ] = groupMap[ itemOrGroup.getGroupName() ] || {};
-                               groupMap[ itemOrGroup.getGroupName() ][ itemOrGroup.getParamName() ] = paramValue;
-                       } else if ( itemOrGroup instanceof mw.rcfilters.dm.FilterGroup ) {
-                               // This parameter represents a group (values are the filters)
-                               // this is equivalent to checking if the group is 'string_options'
-                               groupMap[ itemOrGroup.getName() ] = groupMap[ itemOrGroup.getName() ] || {};
-                               groupMap[ itemOrGroup.getName() ] = paramValue;
+                       var groupName,
+                               itemOrGroup = model.parameterMap[ paramName ];
+
+                       if ( itemOrGroup ) {
+                               groupName = itemOrGroup instanceof mw.rcfilters.dm.FilterItem ?
+                                       itemOrGroup.getGroupName() : itemOrGroup.getName();
+
+                               groupMap[ groupName ] = groupMap[ groupName ] || {};
+                               groupMap[ groupName ][ paramName ] = paramValue;
                        }
                } );
 
index aa82e21..54a4dbe 100644 (file)
@@ -32,6 +32,7 @@
                this.namePrefix = config.namePrefix || 'item_';
                this.name = this.namePrefix + param;
 
+               this.useDefaultAsBaseValue = !!config.useDefaultAsBaseValue;
                this.label = config.label || this.name;
                this.labelPrefixKey = config.labelPrefixKey;
                this.description = config.description || '';
                return this.identifiers;
        };
 
+       /**
+        * Check whether the item uses its default state as a base value
+        *
+        * @return {boolean} Use default as base value
+        */
+       mw.rcfilters.dm.ItemModel.prototype.isUsingDefaultAsBaseValue = function () {
+               return this.useDefaultAsBaseValue;
+       };
+
        /**
         * Toggle the highlight feature on and off for this filter.
         * It only works if highlight is supported for this filter.
index 5ebec27..3b8ebbd 100644 (file)
@@ -35,6 +35,7 @@
                        items = [],
                        uri = new mw.Uri(),
                        $changesList = $( '.mw-changeslist' ).first().contents(),
+                       experimentalViews = mw.config.get( 'wgStructuredChangeFiltersEnableExperimentalViews' ),
                        createFilterDataFromNumber = function ( num, convertedNumForLabel ) {
                                return {
                                        name: String( num ),
@@ -43,7 +44,7 @@
                        };
 
                // Prepare views
-               if ( namespaceStructure ) {
+               if ( namespaceStructure && experimentalViews ) {
                        items = [];
                        $.each( namespaceStructure, function ( namespaceID, label ) {
                                // Build and clean up the individual namespace items definition
@@ -74,7 +75,7 @@
                                } ]
                        };
                }
-               if ( tagList ) {
+               if ( tagList && experimentalViews ) {
                        views.tags = {
                                title: mw.msg( 'rcfilters-view-tags' ),
                                trigger: '#',
index 6cd2d0b..e758f26 100644 (file)
@@ -36,8 +36,6 @@
 
                        $( '.rcfilters-container' ).append( filtersWidget.$element );
                        $( 'body' ).append( $overlay );
-
-                       // Set as ready
                        $( '.rcfilters-head' ).addClass( 'mw-rcfilters-ui-ready' );
 
                        $( 'a.mw-helplink' ).attr(
index 6277fd9..9f3b809 100644 (file)
@@ -1,5 +1,5 @@
 // Corrections for the standard special page
-.client-js{
+.client-js {
        .rcoptions {
                border: 0;
                border-bottom: 1px solid #a2a9b1;
 
        .rcfilters-head {
                min-height: 310px;
+
                &:not( .mw-rcfilters-ui-ready ) {
-                       /* @embed */
-                       background-image: url( ../images/pending.gif );
-                       margin: 0;
+                       opacity: 0.5;
+                       pointer-events: none;
 
-                       * {
-                               visibility: hidden;
+                       .rcoptions {
+                               display: none;
                        }
                }
        }
                margin: 0;
        }
 
-       .mw-changeslist-empty {
-               // Hide the 'empty' message when we load rcfilters
-               // since we replace it anyways with a specific
-               // message of our own
+       .mw-changeslist {
+               &-empty {
+                       // Hide the 'empty' message when we load rcfilters
+                       // since we replace it anyways with a specific
+                       // message of our own
+                       display: none;
+               }
+
+               &:not( .mw-rcfilters-ui-ready ) {
+                       opacity: 0.5;
+               }
+       }
+
+       .rcfilters-spinner {
+               margin: -2em auto 0;
+               width: 70px;
+               opacity: 0.8;
                display: none;
+               white-space: nowrap;
+
+               &:not( .mw-rcfilters-ui-ready ) {
+                       display: block;
+               }
+
+               & .rcfilters-spinner-bounce,
+               &:before,
+               &:after {
+                       content: '';
+                       display: inline-block;
+                       width: 12px;
+                       height: 12px;
+                       background-color: #c8ccd1;
+                       border-radius: 100%;
+                       animation: rcfiltersBouncedelay 1.5s infinite ease-in-out;
+                       animation-fill-mode: both;
+                       animation-delay: -0.16s;
+               }
+
+               &:before {
+                       animation-delay: -0.33s;
+               }
+
+               &:after {
+                       animation-delay: 0s;
+               }
+
        }
 }
 
 .mw-rcfilters-staticfilters-selected {
        font-weight: bold;
 }
+
+@keyframes rcfiltersBouncedelay {
+       0%,
+       100%,
+       80% {
+               transform: scale( 0.7 );
+       }
+       40% {
+               transform: scale( 1 );
+               background-color: #a2a9b1;
+       }
+}
index e8f504a..04f4174 100644 (file)
@@ -7,6 +7,7 @@
                &:after {
                        content: '';
                        mix-blend-mode: screen;
+                       pointer-events: none;
                        position: absolute;
                        width: 1.875em;
                        height: 1.875em;
index 6512f04..c2533df 100644 (file)
@@ -3,7 +3,6 @@
         * List of changes
         *
         * @extends OO.ui.Widget
-        * @mixins OO.ui.mixin.PendingElement
         *
         * @constructor
         * @param {mw.rcfilters.dm.FiltersViewModel} filtersViewModel View model
@@ -23,8 +22,6 @@
 
                // Parent
                mw.rcfilters.ui.ChangesListWrapperWidget.parent.call( this, config );
-               // Mixin constructors
-               OO.ui.mixin.PendingElement.call( this, config );
 
                this.filtersViewModel = filtersViewModel;
                this.changesListViewModel = changesListViewModel;
@@ -51,7 +48,6 @@
        /* Initialization */
 
        OO.inheritClass( mw.rcfilters.ui.ChangesListWrapperWidget, OO.ui.Widget );
-       OO.mixinClass( mw.rcfilters.ui.ChangesListWrapperWidget, OO.ui.mixin.PendingElement );
 
        /**
         * Respond to the highlight feature being toggled on and off
@@ -80,7 +76,8 @@
         * Respond to changes list model invalidate
         */
        mw.rcfilters.ui.ChangesListWrapperWidget.prototype.onModelInvalidate = function () {
-               this.pushPending();
+               $( '.rcfilters-spinner' ).removeClass( 'mw-rcfilters-ui-ready' );
+               this.$element.removeClass( 'mw-rcfilters-ui-ready' );
        };
 
        /**
                        this.$element.append( $message );
                } else {
                        this.$changesListContent = $changesListContent;
-                       this.$element.empty().append( this.$changesListContent );
+                       if ( !isInitialDOM ) {
+                               this.$element.empty().append( this.$changesListContent );
+                       }
                        // Set up highlight containers
                        this.setupHighlightContainers( this.$element );
 
                                mw.hook( 'wikipage.content' ).fire( this.$element );
                        }
                }
-               this.popPending();
+
+               $( '.rcfilters-spinner' ).addClass( 'mw-rcfilters-ui-ready' );
+               this.$element.addClass( 'mw-rcfilters-ui-ready' );
        };
 
        /**
index 0abaff2..ee8e0bc 100644 (file)
@@ -19,8 +19,6 @@
                mw.rcfilters.ui.FormWrapperWidget.parent.call( this, $.extend( {}, config, {
                        $element: $formRoot
                } ) );
-               // Mixin constructors
-               OO.ui.mixin.PendingElement.call( this, config );
 
                this.changeListModel = changeListModel;
                this.filtersModel = filtersModel;
@@ -48,7 +46,6 @@
        /* Initialization */
 
        OO.inheritClass( mw.rcfilters.ui.FormWrapperWidget, OO.ui.Widget );
-       OO.mixinClass( mw.rcfilters.ui.FormWrapperWidget, OO.ui.mixin.PendingElement );
 
        /**
         * Respond to link click
@@ -89,8 +86,8 @@
         * Respond to model invalidate
         */
        mw.rcfilters.ui.FormWrapperWidget.prototype.onChangesModelInvalidate = function () {
-               this.pushPending();
                this.$submitButton.prop( 'disabled', true );
+               this.$element.removeClass( 'mw-rcfilters-ui-ready' );
        };
 
        /**
         */
        mw.rcfilters.ui.FormWrapperWidget.prototype.onChangesModelUpdate = function ( $changesList, $fieldset, isInitialDOM ) {
                this.$submitButton.prop( 'disabled', false );
+               this.$element.removeClass( 'mw-rcfilters-ui-ready' );
 
                // Replace the entire fieldset
                this.$element.empty().append( $fieldset.contents() );
                }
 
                this.cleanUpFieldset();
-
-               this.popPending();
        };
 
        /**
diff --git a/resources/src/mediawiki/mediawiki.hlist-allskins.less b/resources/src/mediawiki/mediawiki.hlist-allskins.less
new file mode 100644 (file)
index 0000000..d7071e4
--- /dev/null
@@ -0,0 +1,21 @@
+.hlist {
+       dl,
+       ol,
+       ul {
+               margin: 0;
+               padding: 0;
+
+               dl,
+               ol,
+               ul {
+                       display: inline;
+               }
+       }
+
+       dd,
+       dt,
+       li {
+               margin: 0;
+               display: inline;
+       }
+}
index c0788a4..2663d87 100644 (file)
@@ -2,31 +2,6 @@
  * Stylesheet for mediawiki.hlist module
  * @author [[User:Edokter]]
  */
-.hlist dl,
-.hlist ol,
-.hlist ul {
-       margin: 0;
-       padding: 0;
-}
-/* Display list items inline */
-.hlist dd,
-.hlist dt,
-.hlist li {
-       margin: 0;
-       display: inline;
-}
-/* Display nested lists inline */
-.hlist dl dl,
-.hlist dl ol,
-.hlist dl ul,
-.hlist ol dl,
-.hlist ol ol,
-.hlist ol ul,
-.hlist ul dl,
-.hlist ul ol,
-.hlist ul ul {
-       display: inline;
-}
 /* Generate interpuncts */
 .hlist dt:after {
        content: ':';
index 3de3ba7..9b9ea6d 100644 (file)
@@ -56,6 +56,7 @@ $wgAutoloadClasses += [
        'MediaWikiLangTestCase' => "$testDir/phpunit/MediaWikiLangTestCase.php",
        'ResourceLoaderTestCase' => "$testDir/phpunit/ResourceLoaderTestCase.php",
        'ResourceLoaderTestModule' => "$testDir/phpunit/ResourceLoaderTestCase.php",
+       'ResourceLoaderFileTestModule' => "$testDir/phpunit/ResourceLoaderTestCase.php",
        'ResourceLoaderFileModuleTestModule' => "$testDir/phpunit/ResourceLoaderTestCase.php",
        'EmptyResourceLoader' => "$testDir/phpunit/ResourceLoaderTestCase.php",
        'TestUser' => "$testDir/phpunit/includes/TestUser.php",
index 9255733..feed77f 100644 (file)
@@ -168,7 +168,7 @@ class ParserTestRunner {
                global $wgParserTestFiles;
 
                // Add core test files
-               $files = array_map( function( $item ) {
+               $files = array_map( function ( $item ) {
                        return __DIR__ . "/$item";
                }, self::$coreTestFiles );
 
@@ -1036,6 +1036,9 @@ class ParserTestRunner {
                $linkHolderBatchSize =
                        self::getOptionValue( 'wgLinkHolderBatchSize', $opts, 1000 );
 
+               // Default to fallback skin, but allow it to be overridden
+               $skin = self::getOptionValue( 'skin', $opts, 'fallback' );
+
                $setup = [
                        'wgEnableUploads' => self::getOptionValue( 'wgEnableUploads', $opts, true ),
                        'wgLanguageCode' => $langCode,
@@ -1105,7 +1108,13 @@ class ParserTestRunner {
                $context = RequestContext::getMain();
                $context->setUser( $user );
                $context->setLanguage( $lang );
-               $teardown[] = function () use ( $context ) {
+               // And the skin!
+               $oldSkin = $context->getSkin();
+               $skinFactory = MediaWikiServices::getInstance()->getSkinFactory();
+               $context->setSkin( $skinFactory->makeSkin( $skin ) );
+               $context->setOutput( new OutputPage( $context ) );
+               $setup['wgOut'] = $context->getOutput();
+               $teardown[] = function () use ( $context, $oldSkin ) {
                        // Clear language conversion tables
                        $wrapper = TestingAccessWrapper::newFromObject(
                                $context->getLanguage()->getConverter()
@@ -1114,6 +1123,8 @@ class ParserTestRunner {
                        // Reset context to the restored globals
                        $context->setUser( $GLOBALS['wgUser'] );
                        $context->setLanguage( $GLOBALS['wgContLang'] );
+                       $context->setSkin( $oldSkin );
+                       $context->setOutput( $GLOBALS['wgOut'] );
                };
 
                $teardown[] = $this->executeSetupSnippets( $setup );
index e8ccd9d..f8ba742 100644 (file)
@@ -28364,3 +28364,25 @@ wgRawHtml=1
 <style data-mw-foobar="baz">.foo::after { content: "<bar>"; }</style>
 </div>
 !! end
+
+!! test
+Decoding of HTML entities in headings and links for IDs and link fragments (T103714)
+!! wikitext
+== A&B&amp;C&amp;amp;D&amp;amp;amp;E ==
+[[#A&B&amp;C&amp;amp;D&amp;amp;amp;E]]
+!! html/php
+<h2><span class="mw-headline" id="A.26B.26C.26amp.3BD.26amp.3Bamp.3BE">A&amp;B&amp;C&amp;amp;D&amp;amp;amp;E</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: A&amp;B&amp;C&amp;amp;D&amp;amp;amp;E">edit</a><span class="mw-editsection-bracket">]</span></span></h2>
+<p><a href="#A.26B.26C.26D.26amp.3BE">#A&amp;B&amp;C&amp;amp;D&amp;amp;amp;E</a>
+</p>
+!! end
+
+!! test
+Decoding of HTML entities in indicator names for IDs (T104196)
+!! options
+showindicators
+!! wikitext
+<indicator name="1&2&amp;3&amp;amp;4&amp;amp;amp;5">Indicator</indicator>
+!! html/php
+1&2&3&amp;4&amp;amp;5=Indicator
+
+!! end
index 31cfa70..215d292 100644 (file)
@@ -1087,10 +1087,15 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
                        $page->doEditContent(
                                new WikitextContent( 'UTContent' ),
                                'UTPageSummary',
-                               EDIT_NEW,
+                               EDIT_NEW | EDIT_SUPPRESS_RC,
                                false,
                                $user
                        );
+                       // an edit always attempt to purge backlink links such as history
+                       // pages. That is unneccessary.
+                       JobQueueGroup::singleton()->get( 'htmlCacheUpdate' )->delete();
+                       // WikiPages::doEditUpdates randomly adds RC purges
+                       JobQueueGroup::singleton()->get( 'recentChangesUpdate' )->delete();
 
                        // doEditContent() probably started the session via
                        // User::loadFromSession(). Close it now.
index a8a8f4d..f75cc22 100644 (file)
@@ -94,6 +94,7 @@ class ResourceLoaderTestModule extends ResourceLoaderModule {
        protected $isKnownEmpty = false;
        protected $type = ResourceLoaderModule::LOAD_GENERAL;
        protected $targets = [ 'phpunit' ];
+       protected $shouldEmbed = null;
 
        public function __construct( $options = [] ) {
                foreach ( $options as $key => $value ) {
@@ -143,11 +144,31 @@ class ResourceLoaderTestModule extends ResourceLoaderModule {
                return $this->isKnownEmpty;
        }
 
+       public function shouldEmbedModule( ResourceLoaderContext $context ) {
+               return $this->shouldEmbed !== null ? $this->shouldEmbed : parent::shouldEmbedModule( $context );
+       }
+
        public function enableModuleContentVersion() {
                return true;
        }
 }
 
+class ResourceLoaderFileTestModule extends ResourceLoaderFileModule {
+       protected $lessVars = [];
+
+       public function __construct( $options = [], $test = [] ) {
+               parent::__construct( $options );
+
+               foreach ( $test as $key => $value ) {
+                       $this->$key = $value;
+               }
+       }
+
+       public function getLessVars( ResourceLoaderContext $context ) {
+               return $this->lessVars;
+       }
+}
+
 class ResourceLoaderFileModuleTestModule extends ResourceLoaderFileModule {
 }
 
index 76a4f51..802f9b1 100644 (file)
@@ -39,7 +39,7 @@ class DeprecatedGlobalTest extends MediaWikiTestCase {
                global $wgDummyLazy;
 
                $called = false;
-               $factory = function() use ( &$called ) {
+               $factory = function () use ( &$called ) {
                        $called = true;
                        return new HashBagOStuff();
                };
index 89416f2..ae858f5 100644 (file)
@@ -23,7 +23,7 @@ class GitInfoTest extends MediaWikiTestCase {
        public function testValidJsonData() {
                global $IP;
 
-               $this->assertValidGitInfo( new GitInfo( "$IP/testValidJsonData") );
+               $this->assertValidGitInfo( new GitInfo( "$IP/testValidJsonData" ) );
                $this->assertValidGitInfo( new GitInfo( __DIR__ . "/../data/gitinfo/extension" ) );
        }
 
index ada516d..d78c1e7 100644 (file)
@@ -131,10 +131,9 @@ class PreferencesTest extends MediaWikiTestCase {
                        ->method( 'getConfig' )
                        ->willReturn( $configMock );
 
-               $this->setTemporaryHook( 'PreferencesFormPreSave', function(
+               $this->setTemporaryHook( 'PreferencesFormPreSave', function (
                        $formData, $form, $user, &$result, $oldUserOptions )
                        use ( $newOptions, $oldOptions, $userMock ) {
-
                        $this->assertSame( $userMock, $user );
                        foreach ( $newOptions as $option => $value ) {
                                $this->assertSame( $value, $formData[ $option ] );
index abcf1d4..6d093b0 100644 (file)
@@ -343,6 +343,41 @@ class SanitizerTest extends MediaWikiTestCase {
                ];
        }
 
+       /**
+        * Test Sanitizer::escapeId
+        *
+        * @dataProvider provideEscapeId
+        * @covers Sanitizer::escapeId
+        */
+       public function testEscapeId( $input, $output ) {
+               $this->assertEquals(
+                       $output,
+                       Sanitizer::escapeId( $input, [ 'noninitial', 'legacy' ] )
+               );
+       }
+
+       public static function provideEscapeId() {
+               return [
+                       [ '+', '.2B' ],
+                       [ '&', '.26' ],
+                       [ '=', '.3D' ],
+                       [ ':', ':' ],
+                       [ ';', '.3B' ],
+                       [ '@', '.40' ],
+                       [ '$', '.24' ],
+                       [ '-_.', '-_.' ],
+                       [ '!', '.21' ],
+                       [ '*', '.2A' ],
+                       [ '/', '.2F' ],
+                       [ '[]', '.5B.5D' ],
+                       [ '<>', '.3C.3E' ],
+                       [ '\'', '.27' ],
+                       [ '§', '.C2.A7' ],
+                       [ 'Test:A & B/Here', 'Test:A_.26_B.2FHere' ],
+                       [ 'A&B&amp;C&amp;amp;D&amp;amp;amp;E', 'A.26B.26C.26amp.3BD.26amp.3Bamp.3BE' ],
+               ];
+       }
+
        /**
         * Test escapeIdReferenceList for consistency with escapeId
         *
diff --git a/tests/phpunit/includes/SiteStatsTest.php b/tests/phpunit/includes/SiteStatsTest.php
new file mode 100644 (file)
index 0000000..ea476a7
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+
+class SiteStatsTest extends MediaWikiTestCase {
+
+       /**
+        * @covers SiteStats::jobs
+        */
+       function testJobsCountGetCached() {
+               $this->setService( 'MainWANObjectCache',
+                       new WANObjectCache( [ 'cache' => new HashBagOStuff() ] ) );
+               $cache = \MediaWiki\MediaWikiServices::getInstance()->getMainWANObjectCache();
+               $jobq = JobQueueGroup::singleton();
+
+               $jobq->push( new NullJob( Title::newMainPage(), [] ) );
+               $this->assertEquals( 1, SiteStats::jobs(),
+                        'A single job enqueued bumps jobscount stat to 1' );
+
+               $jobq->push( new NullJob( Title::newMainPage(), [] ) );
+               $this->assertEquals( 1, SiteStats::jobs(),
+                       'SiteStats::jobs() count does not reflect addition ' .
+                       'of a second job (cached)'
+               );
+
+               $jobq->get( 'null' )->delete();  // clear jobqueue
+               $this->assertEquals( 0, $jobq->get( 'null' )->getSize(),
+                       'Job queue for NullJob has been cleaned' );
+
+               $cache->delete( $cache->makeKey( 'SiteStats', 'jobscount' ) );
+               $this->assertEquals( 1, SiteStats::jobs(),
+                       'jobs count is kept in process cache' );
+
+               $cache->clearProcessCache();
+               $this->assertEquals( 0, SiteStats::jobs() );
+       }
+
+}
index a2c0d39..d47481c 100644 (file)
@@ -526,6 +526,10 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
         * @param array $expect
         */
        public function testGetMessageFromException( $exception, $options, $expect ) {
+               if ( $exception instanceof UsageException ) {
+                       $this->hideDeprecated( 'UsageException::getMessageArray' );
+               }
+
                $result = new ApiResult( 8388608 );
                $formatter = new ApiErrorFormatter( $result, Language::factory( 'en' ), 'html', false );
 
@@ -571,6 +575,12 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
        }
 
        public static function provideGetMessageFromException() {
+               MediaWiki\suppressWarnings();
+               $usageException = new UsageException(
+                       '<b>Something broke!</b>', 'ue-code', 0, [ 'xxx' => 'yyy', 'baz' => 23 ]
+               );
+               MediaWiki\restoreWarnings();
+
                return [
                        'Normal exception' => [
                                new RuntimeException( '<b>Something broke!</b>' ),
@@ -591,7 +601,7 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
                                ]
                        ],
                        'UsageException' => [
-                               new UsageException( '<b>Something broke!</b>', 'ue-code', 0, [ 'xxx' => 'yyy', 'baz' => 23 ] ),
+                               $usageException,
                                [],
                                [
                                        'text' => '&#60;b&#62;Something broke!&#60;/b&#62;',
@@ -600,7 +610,7 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
                                ]
                        ],
                        'UsageException, wrapped' => [
-                               new UsageException( '<b>Something broke!</b>', 'ue-code', 0, [ 'xxx' => 'yyy', 'baz' => 23 ] ),
+                               $usageException,
                                [ 'wrap' => 'parentheses', 'code' => 'some-code', 'data' => [ 'foo' => 'bar', 'baz' => 42 ] ],
                                [
                                        'text' => '(&#60;b&#62;Something broke!&#60;/b&#62;)',
index ea33a9e..ad334e9 100644 (file)
@@ -500,6 +500,10 @@ class ApiMainTest extends ApiTestCase {
                        MWExceptionHandler::getRedactedTraceAsString( $dbex )
                )->inLanguage( 'en' )->useDatabase( false )->text();
 
+               MediaWiki\suppressWarnings();
+               $usageEx = new UsageException( 'Usage exception!', 'ue', 0, [ 'foo' => 'bar' ] );
+               MediaWiki\restoreWarnings();
+
                $apiEx1 = new ApiUsageException( null,
                        StatusValue::newFatal( new ApiRawMessage( 'An error', 'sv-error1' ) ) );
                TestingAccessWrapper::newFromObject( $apiEx1 )->modulePath = 'foo+bar';
@@ -545,7 +549,7 @@ class ApiMainTest extends ApiTestCase {
                                ]
                        ],
                        [
-                               new UsageException( 'Usage exception!', 'ue', 0, [ 'foo' => 'bar' ] ),
+                               $usageEx,
                                [ 'existing-error', 'ue' ],
                                [
                                        'warnings' => [
diff --git a/tests/phpunit/includes/changetags/ChangeTagsTest.php b/tests/phpunit/includes/changetags/ChangeTagsTest.php
new file mode 100644 (file)
index 0000000..723d685
--- /dev/null
@@ -0,0 +1,247 @@
+<?php
+
+/**
+ * @covers ChangeTags
+ */
+class ChangeTagsTest extends MediaWikiTestCase {
+
+       // TODO only modifyDisplayQuery is tested, nothing else is
+
+       /** @dataProvider provideModifyDisplayQuery */
+       public function testModifyDisplayQuery( $origQuery, $filter_tag, $useTags, $modifiedQuery ) {
+               $this->setMwGlobals( 'wgUseTagFilter', $useTags );
+               // HACK resolve deferred group concats (see comment in provideModifyDisplayQuery)
+               if ( isset( $modifiedQuery['fields']['ts_tags'] ) ) {
+                       $modifiedQuery['fields']['ts_tags'] = call_user_func_array(
+                               [ wfGetDB( DB_REPLICA ), 'buildGroupConcatField' ],
+                               $modifiedQuery['fields']['ts_tags']
+                       );
+               }
+               if ( isset( $modifiedQuery['exception'] ) ) {
+                       $this->setExpectedException( $modifiedQuery['exception'] );
+               }
+               ChangeTags::modifyDisplayQuery(
+                       $origQuery['tables'],
+                       $origQuery['fields'],
+                       $origQuery['conds'],
+                       $origQuery['join_conds'],
+                       $origQuery['options'],
+                       $filter_tag
+               );
+               if ( !isset( $modifiedQuery['exception'] ) ) {
+                       $this->assertArrayEquals(
+                               $modifiedQuery,
+                               $origQuery,
+                               /* ordered = */ false,
+                               /* named = */ true
+                       );
+               }
+       }
+
+       public function provideModifyDisplayQuery() {
+               // HACK if we call $dbr->buildGroupConcatField() now, it will return the wrong table names
+               // We have to have the test runner call it instead
+               $groupConcats = [
+                       'recentchanges' => [ ',', 'change_tag', 'ct_tag', 'ct_rc_id=rc_id' ],
+                       'logging' => [ ',', 'change_tag', 'ct_tag', 'ct_log_id=log_id' ],
+                       'revision' => [ ',', 'change_tag', 'ct_tag', 'ct_rev_id=rev_id' ],
+                       'archive' => [ ',', 'change_tag', 'ct_tag', 'ct_rev_id=ar_rev_id' ],
+               ];
+
+               return [
+                       'simple recentchanges query' => [
+                               [
+                                       'tables' => [ 'recentchanges' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp' ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
+                               ],
+                               '', // no tag filter
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'recentchanges' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp', 'ts_tags' => $groupConcats['recentchanges'] ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
+                               ]
+                       ],
+                       'simple query with strings' => [
+                               [
+                                       'tables' => 'recentchanges',
+                                       'fields' => 'rc_id',
+                                       'conds' => "rc_timestamp > '20170714183203'",
+                                       'join_conds' => [],
+                                       'options' => 'ORDER BY rc_timestamp DESC',
+                               ],
+                               '', // no tag filter
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'recentchanges' ],
+                                       'fields' => [ 'rc_id', 'ts_tags' => $groupConcats['recentchanges'] ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY rc_timestamp DESC' ],
+                               ]
+                       ],
+                       'recentchanges query with single tag filter' => [
+                               [
+                                       'tables' => [ 'recentchanges' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp' ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
+                               ],
+                               'foo',
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'recentchanges', 'change_tag' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp', 'ts_tags' => $groupConcats['recentchanges'] ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'", 'ct_tag' => 'foo' ],
+                                       'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rc_id=rc_id' ] ],
+                                       'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
+                               ]
+                       ],
+                       'logging query with single tag filter and strings' => [
+                               [
+                                       'tables' => 'logging',
+                                       'fields' => 'log_id',
+                                       'conds' => "log_timestamp > '20170714183203'",
+                                       'join_conds' => [],
+                                       'options' => 'ORDER BY log_timestamp DESC',
+                               ],
+                               'foo',
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'logging', 'change_tag' ],
+                                       'fields' => [ 'log_id', 'ts_tags' => $groupConcats['logging'] ],
+                                       'conds' => [ "log_timestamp > '20170714183203'", 'ct_tag' => 'foo' ],
+                                       'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_log_id=log_id' ] ],
+                                       'options' => [ 'ORDER BY log_timestamp DESC' ],
+                               ]
+                       ],
+                       'revision query with single tag filter' => [
+                               [
+                                       'tables' => [ 'revision' ],
+                                       'fields' => [ 'rev_id', 'rev_timestamp' ],
+                                       'conds' => [ "rev_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'rev_timestamp DESC' ],
+                               ],
+                               'foo',
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'revision', 'change_tag' ],
+                                       'fields' => [ 'rev_id', 'rev_timestamp', 'ts_tags' => $groupConcats['revision'] ],
+                                       'conds' => [ "rev_timestamp > '20170714183203'", 'ct_tag' => 'foo' ],
+                                       'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rev_id=rev_id' ] ],
+                                       'options' => [ 'ORDER BY' => 'rev_timestamp DESC' ],
+                               ]
+                       ],
+                       'archive query with single tag filter' => [
+                               [
+                                       'tables' => [ 'archive' ],
+                                       'fields' => [ 'ar_id', 'ar_timestamp' ],
+                                       'conds' => [ "ar_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'ar_timestamp DESC' ],
+                               ],
+                               'foo',
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'archive', 'change_tag' ],
+                                       'fields' => [ 'ar_id', 'ar_timestamp', 'ts_tags' => $groupConcats['archive'] ],
+                                       'conds' => [ "ar_timestamp > '20170714183203'", 'ct_tag' => 'foo' ],
+                                       'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rev_id=ar_rev_id' ] ],
+                                       'options' => [ 'ORDER BY' => 'ar_timestamp DESC' ],
+                               ]
+                       ],
+                       'unsupported table name throws exception (even without tag filter)' => [
+                               [
+                                       'tables' => [ 'foobar' ],
+                                       'fields' => [ 'fb_id', 'fb_timestamp' ],
+                                       'conds' => [ "fb_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'fb_timestamp DESC' ],
+                               ],
+                               '',
+                               true, // tag filtering enabled
+                               [ 'exception' => MWException::class ]
+                       ],
+                       'tag filter ignored when tag filtering is disabled' => [
+                               [
+                                       'tables' => [ 'archive' ],
+                                       'fields' => [ 'ar_id', 'ar_timestamp' ],
+                                       'conds' => [ "ar_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'ar_timestamp DESC' ],
+                               ],
+                               'foo',
+                               false, // tag filtering disabled
+                               [
+                                       'tables' => [ 'archive' ],
+                                       'fields' => [ 'ar_id', 'ar_timestamp', 'ts_tags' => $groupConcats['archive'] ],
+                                       'conds' => [ "ar_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'ar_timestamp DESC' ],
+                               ]
+                       ],
+                       'recentchanges query with multiple tag filter' => [
+                               [
+                                       'tables' => [ 'recentchanges' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp' ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'ORDER BY' => 'rc_timestamp DESC' ],
+                               ],
+                               [ 'foo', 'bar' ],
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'recentchanges', 'change_tag' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp', 'ts_tags' => $groupConcats['recentchanges'] ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'", 'ct_tag' => [ 'foo', 'bar' ] ],
+                                       'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rc_id=rc_id' ] ],
+                                       'options' => [ 'ORDER BY' => 'rc_timestamp DESC', 'DISTINCT' ],
+                               ]
+                       ],
+                       'recentchanges query with multiple tag filter that already has DISTINCT' => [
+                               [
+                                       'tables' => [ 'recentchanges' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp' ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'" ],
+                                       'join_conds' => [],
+                                       'options' => [ 'DISTINCT', 'ORDER BY' => 'rc_timestamp DESC' ],
+                               ],
+                               [ 'foo', 'bar' ],
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'recentchanges', 'change_tag' ],
+                                       'fields' => [ 'rc_id', 'rc_timestamp', 'ts_tags' => $groupConcats['recentchanges'] ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'", 'ct_tag' => [ 'foo', 'bar' ] ],
+                                       'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rc_id=rc_id' ] ],
+                                       'options' => [ 'DISTINCT', 'ORDER BY' => 'rc_timestamp DESC' ],
+                               ]
+                       ],
+                       'recentchanges query with multiple tag filter with strings' => [
+                               [
+                                       'tables' => 'recentchanges',
+                                       'fields' => 'rc_id',
+                                       'conds' => "rc_timestamp > '20170714183203'",
+                                       'join_conds' => [],
+                                       'options' => 'ORDER BY rc_timestamp DESC',
+                               ],
+                               [ 'foo', 'bar' ],
+                               true, // tag filtering enabled
+                               [
+                                       'tables' => [ 'recentchanges', 'change_tag' ],
+                                       'fields' => [ 'rc_id', 'ts_tags' => $groupConcats['recentchanges'] ],
+                                       'conds' => [ "rc_timestamp > '20170714183203'", 'ct_tag' => [ 'foo', 'bar' ] ],
+                                       'join_conds' => [ 'change_tag' => [ 'INNER JOIN', 'ct_rc_id=rc_id' ] ],
+                                       'options' => [ 'ORDER BY rc_timestamp DESC', 'DISTINCT' ],
+                               ]
+                       ],
+               ];
+       }
+
+}
index ba38128..608d8d9 100644 (file)
@@ -47,7 +47,7 @@ class ConfigFactoryTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers ConfigFactory::register
+        * @covers ConfigFactory::salvage
         */
        public function testSalvage() {
                $oldFactory = new ConfigFactory();
@@ -83,7 +83,7 @@ class ConfigFactoryTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers ConfigFactory::register
+        * @covers ConfigFactory::getConfigNames
         */
        public function testGetConfigNames() {
                $factory = new ConfigFactory();
@@ -96,7 +96,7 @@ class ConfigFactoryTest extends MediaWikiTestCase {
        /**
         * @covers ConfigFactory::makeConfig
         */
-       public function testMakeConfig() {
+       public function testMakeConfigWithCallback() {
                $factory = new ConfigFactory();
                $factory->register( 'unittest', 'GlobalVarConfig::newInstance' );
 
@@ -105,6 +105,16 @@ class ConfigFactoryTest extends MediaWikiTestCase {
                $this->assertSame( $conf, $factory->makeConfig( 'unittest' ) );
        }
 
+       /**
+        * @covers ConfigFactory::makeConfig
+        */
+       public function testMakeConfigWithObject() {
+               $factory = new ConfigFactory();
+               $conf = new HashConfig();
+               $factory->register( 'test', $conf );
+               $this->assertSame( $conf, $factory->makeConfig( 'test' ) );
+       }
+
        /**
         * @covers ConfigFactory::makeConfig
         */
index 763bfa8..19cffa2 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 
+use Wikimedia\TestingAccessWrapper;
+
 class EtcConfigTest extends PHPUnit_Framework_TestCase {
 
        private function createConfigMock( array $options = [] ) {
@@ -359,4 +361,155 @@ class EtcConfigTest extends PHPUnit_Framework_TestCase {
 
                $this->assertSame( 'from-cache-expired', $mock->get( 'known' ) );
        }
+
+       public static function provideFetchFromServer() {
+               return [
+                       '200 OK - Success' => [
+                               'http' => [
+                                       'code' => 200,
+                                       'reason' => 'OK',
+                                       'headers' => [],
+                                       'body' => json_encode( [ 'node' => [ 'nodes' => [
+                                               [
+                                                       'key' => '/example/foo',
+                                                       'value' => json_encode( [ 'val' => true ] )
+                                               ],
+                                       ] ] ] ),
+                                       'error' => '',
+                               ],
+                               'expect' => [
+                                       [ 'foo' => true ], // data
+                                       null,
+                                       false // retry
+                               ],
+                       ],
+                       '200 OK - Skip dir' => [
+                               'http' => [
+                                       'code' => 200,
+                                       'reason' => 'OK',
+                                       'headers' => [],
+                                       'body' => json_encode( [ 'node' => [ 'nodes' => [
+                                               [
+                                                       'key' => '/example/foo',
+                                                       'value' => json_encode( [ 'val' => true ] )
+                                               ],
+                                               [
+                                                       'key' => '/example/sub',
+                                                       'dir' => true
+                                               ],
+                                               [
+                                                       'key' => '/example/bar',
+                                                       'value' => json_encode( [ 'val' => false ] )
+                                               ],
+                                       ] ] ] ),
+                                       'error' => '',
+                               ],
+                               'expect' => [
+                                       [ 'foo' => true, 'bar' => false ], // data
+                                       null,
+                                       false // retry
+                               ],
+                       ],
+                       '200 OK - Bad value' => [
+                               'http' => [
+                                       'code' => 200,
+                                       'reason' => 'OK',
+                                       'headers' => [],
+                                       'body' => json_encode( [ 'node' => [ 'nodes' => [
+                                               [
+                                                       'key' => '/example/foo',
+                                                       'value' => ';"broken{value'
+                                               ]
+                                       ] ] ] ),
+                                       'error' => '',
+                               ],
+                               'expect' => [
+                                       null, // data
+                                       "Failed to parse value for 'foo'.",
+                                       false // retry
+                               ],
+                       ],
+                       '200 OK - Empty node list' => [
+                               'http' => [
+                                       'code' => 200,
+                                       'reason' => 'OK',
+                                       'headers' => [],
+                                       'body' => '{"node":{"nodes":[]}}',
+                                       'error' => '',
+                               ],
+                               'expect' => [
+                                       [], // data
+                                       null,
+                                       false // retry
+                               ],
+                       ],
+                       '200 OK - Invalid JSON' => [
+                               'http' => [
+                                       'code' => 200,
+                                       'reason' => 'OK',
+                                       'headers' => [ 'content-length' => 0 ],
+                                       'body' => '',
+                                       'error' => '(curl error: no status set)',
+                               ],
+                               'expect' => [
+                                       null, // data
+                                       "Unexpected JSON response; missing 'nodes' list.",
+                                       false // retry
+                               ],
+                       ],
+                       '404 Not Found' => [
+                               'http' => [
+                                       'code' => 404,
+                                       'reason' => 'Not Found',
+                                       'headers' => [ 'content-length' => 0 ],
+                                       'body' => '',
+                                       'error' => '',
+                               ],
+                               'expect' => [
+                                       null, // data
+                                       'HTTP 404 (Not Found)',
+                                       false // retry
+                               ],
+                       ],
+                       '400 Bad Request - custom error' => [
+                               'http' => [
+                                       'code' => 400,
+                                       'reason' => 'Bad Request',
+                                       'headers' => [ 'content-length' => 0 ],
+                                       'body' => '',
+                                       'error' => 'No good reason',
+                               ],
+                               'expect' => [
+                                       null, // data
+                                       'No good reason',
+                                       true // retry
+                               ],
+                       ],
+               ];
+       }
+
+       /**
+        * @covers EtcdConfig::fetchAllFromEtcdServer
+        * @covers EtcdConfig::unserialize
+        * @dataProvider provideFetchFromServer
+        */
+       public function testFetchFromServer( array $httpResponse, array $expected ) {
+               $http = $this->getMockBuilder( MultiHttpClient::class )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+               $http->expects( $this->once() )->method( 'run' )
+                       ->willReturn( array_values( $httpResponse ) );
+
+               $conf = $this->getMockBuilder( EtcdConfig::class )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+               // Access for protected member and method
+               $conf = TestingAccessWrapper::newFromObject( $conf );
+               $conf->http = $http;
+
+               $this->assertSame(
+                       $expected,
+                       $conf->fetchAllFromEtcdServer( 'etcd-tcp.example.net' )
+               );
+       }
 }
index b9ce997..d0996e3 100644 (file)
@@ -103,16 +103,16 @@ more stuff
 
        public static function dataGetSection() {
                return [
-                       [ WikitextContentTest::$sections,
+                       [ self::$sections,
                                "0",
                                "Intro"
                        ],
-                       [ WikitextContentTest::$sections,
+                       [ self::$sections,
                                "2",
                                "== test ==
 just a test"
                        ],
-                       [ WikitextContentTest::$sections,
+                       [ self::$sections,
                                "8",
                                false
                        ],
@@ -138,38 +138,38 @@ just a test"
 
        public static function dataReplaceSection() {
                return [
-                       [ WikitextContentTest::$sections,
+                       [ self::$sections,
                                "0",
                                "No more",
                                null,
-                               trim( preg_replace( '/^Intro/sm', 'No more', WikitextContentTest::$sections ) )
+                               trim( preg_replace( '/^Intro/sm', 'No more', self::$sections ) )
                        ],
-                       [ WikitextContentTest::$sections,
+                       [ self::$sections,
                                "",
                                "No more",
                                null,
                                "No more"
                        ],
-                       [ WikitextContentTest::$sections,
+                       [ self::$sections,
                                "2",
                                "== TEST ==\nmore fun",
                                null,
                                trim( preg_replace(
                                        '/^== test ==.*== foo ==/sm', "== TEST ==\nmore fun\n\n== foo ==",
-                                       WikitextContentTest::$sections
+                                       self::$sections
                                ) )
                        ],
-                       [ WikitextContentTest::$sections,
+                       [ self::$sections,
                                "8",
                                "No more",
                                null,
-                               WikitextContentTest::$sections
+                               self::$sections
                        ],
-                       [ WikitextContentTest::$sections,
+                       [ self::$sections,
                                "new",
                                "No more",
                                "New",
-                               trim( WikitextContentTest::$sections ) . "\n\n\n== New ==\n\nNo more"
+                               trim( self::$sections ) . "\n\n\n== New ==\n\nNo more"
                        ],
                ];
        }
index d0121b1..1a15c26 100644 (file)
@@ -5,6 +5,10 @@
  * @author Timo Tijhof
  */
 
+/**
+ * @group ResourceLoader
+ * @group CSSMin
+ */
 class CSSMinTest extends MediaWikiTestCase {
 
        protected function setUp() {
@@ -233,6 +237,11 @@ class CSSMinTest extends MediaWikiTestCase {
                                [ 'foo { prop: url(/w/skin/images/bar.png); }', false, 'http://example.org/quux', false ],
                                'foo { prop: url(http://doc.example.org/w/skin/images/bar.png); }',
                        ],
+                       [
+                               "Don't barf at behavior: url(#default#behaviorName) - T162973",
+                               [ 'foo { behavior: url(#default#bar); }', false, '/w/', false ],
+                               'foo { behavior: url("#default#bar"); }',
+                       ],
                ];
        }
 
index 775709f..4a9f6cc 100644 (file)
@@ -81,22 +81,26 @@ class MultiWriteBagOStuffTest extends MediaWikiTestCase {
         */
        public function testSetDelayed() {
                $key = wfRandomString();
-               $value = wfRandomString();
+               $value = (object)[ 'v' => wfRandomString() ];
+               $expectValue = clone $value;
 
                // XXX: DeferredUpdates bound to transactions in CLI mode
                $dbw = wfGetDB( DB_MASTER );
                $dbw->begin();
                $this->cache->set( $key, $value );
 
+               // Test that later changes to $value don't affect the saved value (e.g. T168040)
+               $value->v = 'bogus';
+
                // Set in tier 1
-               $this->assertEquals( $value, $this->cache1->get( $key ), 'Written to tier 1' );
+               $this->assertEquals( $expectValue, $this->cache1->get( $key ), 'Written to tier 1' );
                // Not yet set in tier 2
                $this->assertEquals( false, $this->cache2->get( $key ), 'Not written to tier 2' );
 
                $dbw->commit();
 
                // Set in tier 2
-               $this->assertEquals( $value, $this->cache2->get( $key ), 'Written to tier 2' );
+               $this->assertEquals( $expectValue, $this->cache2->get( $key ), 'Written to tier 2' );
        }
 
        /**
index 3dc7e28..a8dbdd3 100644 (file)
@@ -8,16 +8,20 @@ use Wikimedia\Rdbms\DatabaseDomain;
 class DatabaseDomainTest extends PHPUnit_Framework_TestCase {
        public static function provideConstruct() {
                return [
-                       // All strings
-                       [ 'foo', 'bar', 'baz', 'foo-bar-baz' ],
-                       // Nothing
-                       [ null, null, '', '' ],
-                       // Invalid $database
-                       [ 0, 'bar', '', '', true ],
-                       // - in one of the fields
-                       [ 'foo-bar', 'baz', 'baa', 'foo?hbar-baz-baa' ],
-                       // ? in one of the fields
-                       [ 'foo?bar', 'baz', 'baa', 'foo??bar-baz-baa' ],
+                       'All strings' =>
+                               [ 'foo', 'bar', 'baz', 'foo-bar-baz' ],
+                       'Nothing' =>
+                               [ null, null, '', '' ],
+                       'Invalid $database' =>
+                               [ 0, 'bar', '', '', true ],
+                       'Invalid $schema' =>
+                               [ 'foo', 0, '', '', true ],
+                       'Invalid $prefix' =>
+                               [ 'foo', 'bar', 0, '', true ],
+                       'Dash' =>
+                               [ 'foo-bar', 'baz', 'baa', 'foo?hbar-baz-baa' ],
+                       'Question mark' =>
+                               [ 'foo?bar', 'baz', 'baa', 'foo??bar-baz-baa' ],
                ];
        }
 
@@ -27,6 +31,8 @@ class DatabaseDomainTest extends PHPUnit_Framework_TestCase {
        public function testConstruct( $db, $schema, $prefix, $id, $exception = false ) {
                if ( $exception ) {
                        $this->setExpectedException( InvalidArgumentException::class );
+                       new DatabaseDomain( $db, $schema, $prefix );
+                       return;
                }
 
                $domain = new DatabaseDomain( $db, $schema, $prefix );
@@ -35,23 +41,27 @@ class DatabaseDomainTest extends PHPUnit_Framework_TestCase {
                $this->assertEquals( $schema, $domain->getSchema() );
                $this->assertEquals( $prefix, $domain->getTablePrefix() );
                $this->assertEquals( $id, $domain->getId() );
+               $this->assertEquals( $id, strval( $domain ), 'toString' );
        }
 
        public static function provideNewFromId() {
                return [
-                       // basic
-                       [ 'foo', 'foo', null, '' ],
-                       // <database>-<prefix>
-                       [ 'foo-bar', 'foo', null, 'bar' ],
-                       [ 'foo-bar-baz', 'foo', 'bar', 'baz' ],
-                       // ?h -> -
-                       [ 'foo?hbar-baz-baa', 'foo-bar', 'baz', 'baa' ],
-                       // ?? -> ?
-                       [ 'foo??bar-baz-baa', 'foo?bar', 'baz', 'baa' ],
-                       // ? is left alone
-                       [ 'foo?bar-baz-baa', 'foo?bar', 'baz', 'baa' ],
-                       // too many parts
-                       [ 'foo-bar-baz-baa', '', '', '', true ],
+                       'Basic' =>
+                               [ 'foo', 'foo', null, '' ],
+                       'db+prefix' =>
+                               [ 'foo-bar', 'foo', null, 'bar' ],
+                       'db+schema+prefix' =>
+                               [ 'foo-bar-baz', 'foo', 'bar', 'baz' ],
+                       '?h -> -' =>
+                               [ 'foo?hbar-baz-baa', 'foo-bar', 'baz', 'baa' ],
+                       '?? -> ?' =>
+                               [ 'foo??bar-baz-baa', 'foo?bar', 'baz', 'baa' ],
+                       '? is left alone' =>
+                               [ 'foo?bar-baz-baa', 'foo?bar', 'baz', 'baa' ],
+                       'too many parts' =>
+                               [ 'foo-bar-baz-baa', '', '', '', true ],
+                       'from instance' =>
+                               [ DatabaseDomain::newUnspecified(), null, null, '' ],
                ];
        }
 
@@ -61,6 +71,8 @@ class DatabaseDomainTest extends PHPUnit_Framework_TestCase {
        public function testNewFromId( $id, $db, $schema, $prefix, $exception = false ) {
                if ( $exception ) {
                        $this->setExpectedException( InvalidArgumentException::class );
+                       DatabaseDomain::newFromId( $id );
+                       return;
                }
                $domain = DatabaseDomain::newFromId( $id );
                $this->assertInstanceOf( DatabaseDomain::class, $domain );
@@ -68,4 +80,50 @@ class DatabaseDomainTest extends PHPUnit_Framework_TestCase {
                $this->assertEquals( $schema, $domain->getSchema() );
                $this->assertEquals( $prefix, $domain->getTablePrefix() );
        }
+
+       public static function provideEquals() {
+               return [
+                       'Basic' =>
+                               [ 'foo', 'foo', null, '' ],
+                       'db+prefix' =>
+                               [ 'foo-bar', 'foo', null, 'bar' ],
+                       'db+schema+prefix' =>
+                               [ 'foo-bar-baz', 'foo', 'bar', 'baz' ],
+                       '?h -> -' =>
+                               [ 'foo?hbar-baz-baa', 'foo-bar', 'baz', 'baa' ],
+                       '?? -> ?' =>
+                               [ 'foo??bar-baz-baa', 'foo?bar', 'baz', 'baa' ],
+                       'Nothing' =>
+                               [ '', null, null, '' ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideEquals
+        * @covers Wikimedia\Rdbms\DatabaseDomain::equals
+        */
+       public function testEquals( $id, $db, $schema, $prefix ) {
+               $fromId = DatabaseDomain::newFromId( $id );
+               $this->assertInstanceOf( DatabaseDomain::class, $fromId );
+
+               $constructed = new DatabaseDomain( $db, $schema, $prefix );
+
+               $this->assertTrue( $constructed->equals( $id ), 'constructed equals string' );
+               $this->assertTrue( $fromId->equals( $id ), 'fromId equals string' );
+
+               $this->assertTrue( $constructed->equals( $fromId ), 'compare constructed to newId' );
+               $this->assertTrue( $fromId->equals( $constructed ), 'compare newId to constructed' );
+       }
+
+       /**
+        * @covers Wikimedia\Rdbms\DatabaseDomain::newUnspecified
+        */
+       public function testNewUnspecified() {
+               $domain = DatabaseDomain::newUnspecified();
+               $this->assertInstanceOf( DatabaseDomain::class, $domain );
+               $this->assertTrue( $domain->equals( '' ) );
+               $this->assertSame( null, $domain->getDatabase() );
+               $this->assertSame( null, $domain->getSchema() );
+               $this->assertSame( '', $domain->getTablePrefix() );
+       }
 }
index 57666bd..f519772 100644 (file)
@@ -22,13 +22,18 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
                );
        }
 
-       protected function assertLastSqlDb( $sqlText, $db ) {
+       protected function assertLastSqlDb( $sqlText, DatabaseTestHelper $db ) {
                $this->assertEquals( $sqlText, $db->getLastSqls() );
        }
 
        /**
         * @dataProvider provideSelect
         * @covers Wikimedia\Rdbms\Database::select
+        * @covers Wikimedia\Rdbms\Database::selectSQLText
+        * @covers Wikimedia\Rdbms\Database::tableNamesWithIndexClauseOrJOIN
+        * @covers Wikimedia\Rdbms\Database::makeSelectOptions
+        * @covers Wikimedia\Rdbms\Database::makeOrderBy
+        * @covers Wikimedia\Rdbms\Database::makeGroupByWithHaving
         */
        public function testSelect( $sql, $sqlText ) {
                $this->database->select(
@@ -54,6 +59,23 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
                                        "FROM table " .
                                        "WHERE alias = 'text'"
                        ],
+                       [
+                               [
+                                       // 'tables' with space prepended indicates pre-escaped table name
+                                       'tables' => ' table LEFT JOIN table2',
+                                       'fields' => [ 'field' ],
+                                       'conds' => [ 'field' => 'text' ],
+                               ],
+                               "SELECT field FROM  table LEFT JOIN table2 WHERE field = 'text'"
+                       ],
+                       [
+                               [
+                                       // Empty 'tables' is allowed
+                                       'tables' => '',
+                                       'fields' => [ 'SPECIAL_QUERY()' ],
+                               ],
+                               "SELECT SPECIAL_QUERY()"
+                       ],
                        [
                                [
                                        'tables' => 'table',
@@ -129,12 +151,38 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
                                        "FROM table " .
                                        "WHERE alias IN ('1','2','3','4')"
                        ],
+                       [
+                               [
+                                       'tables' => 'table',
+                                       'fields' => [ 'field' ],
+                                       'options' => [ 'DISTINCT', 'LOCK IN SHARE MODE' ],
+                               ],
+                               "SELECT DISTINCT field FROM table      LOCK IN SHARE MODE"
+                       ],
+                       [
+                               [
+                                       'tables' => 'table',
+                                       'fields' => [ 'field' ],
+                                       'options' => [ 'EXPLAIN' => true ],
+                               ],
+                               'EXPLAIN SELECT field FROM table'
+                       ],
+                       [
+                               [
+                                       'tables' => 'table',
+                                       'fields' => [ 'field' ],
+                                       'options' => [ 'FOR UPDATE' ],
+                               ],
+                               "SELECT field FROM table      FOR UPDATE"
+                       ],
                ];
        }
 
        /**
         * @dataProvider provideUpdate
         * @covers Wikimedia\Rdbms\Database::update
+        * @covers Wikimedia\Rdbms\Database::makeUpdateOptions
+        * @covers Wikimedia\Rdbms\Database::makeUpdateOptionsArray
         */
        public function testUpdate( $sql, $sqlText ) {
                $this->database->update(
@@ -303,6 +351,7 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
        /**
         * @dataProvider provideInsert
         * @covers Wikimedia\Rdbms\Database::insert
+        * @covers Wikimedia\Rdbms\Database::makeInsertOptions
         */
        public function testInsert( $sql, $sqlText ) {
                $this->database->insert(
@@ -356,6 +405,7 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
        /**
         * @dataProvider provideInsertSelect
         * @covers Wikimedia\Rdbms\Database::insertSelect
+        * @covers Wikimedia\Rdbms\Database::nativeInsertSelect
         */
        public function testInsertSelect( $sql, $sqlTextNative, $sqlSelect, $sqlInsert ) {
                $this->database->insertSelect(
@@ -673,6 +723,7 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
        /**
         * @dataProvider provideBuildLike
         * @covers Wikimedia\Rdbms\Database::buildLike
+        * @covers Wikimedia\Rdbms\Database::escapeLikeInternal
         */
        public function testBuildLike( $array, $sqlText ) {
                $this->assertEquals( trim( $this->database->buildLike(
@@ -921,6 +972,7 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
 
        /**
         * @covers Wikimedia\Rdbms\Database::commit
+        * @covers Wikimedia\Rdbms\Database::doCommit
         */
        public function testTransactionCommit() {
                $this->database->begin( __METHOD__ );
@@ -930,6 +982,7 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
 
        /**
         * @covers Wikimedia\Rdbms\Database::rollback
+        * @covers Wikimedia\Rdbms\Database::doRollback
         */
        public function testTransactionRollback() {
                $this->database->begin( __METHOD__ );
@@ -1035,6 +1088,9 @@ class DatabaseSQLTest extends PHPUnit_Framework_TestCase {
                ];
        }
 
+       /**
+        * @covers Wikimedia\Rdbms\Database::registerTempTableOperation
+        */
        public function testSessionTempTables() {
                $temp1 = $this->database->tableName( 'tmp_table_1' );
                $temp2 = $this->database->tableName( 'tmp_table_2' );
index 9bea7ff..70b6c36 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 
 use Wikimedia\Rdbms\IDatabase;
+use Wikimedia\Rdbms\LBFactorySingle;
 use Wikimedia\Rdbms\TransactionProfiler;
 use Wikimedia\TestingAccessWrapper;
 
@@ -135,6 +136,71 @@ class DatabaseTest extends PHPUnit_Framework_TestCase {
                $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
        }
 
+       /**
+        * @covers Wikimedia\Rdbms\Database::onTransactionPreCommitOrIdle
+        * @covers Wikimedia\Rdbms\Database::runOnTransactionPreCommitCallbacks
+        */
+       public function testTransactionPreCommitOrIdle() {
+               $db = $this->getMockDB( [ 'isOpen' ] );
+               $db->method( 'isOpen' )->willReturn( true );
+               $db->clearFlag( DBO_TRX );
+
+               $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX is not set' );
+
+               $called = false;
+               $db->onTransactionPreCommitOrIdle(
+                       function () use ( &$called ) {
+                               $called = true;
+                       },
+                       __METHOD__
+               );
+               $this->assertTrue( $called, 'Called when idle' );
+
+               $db->begin( __METHOD__ );
+               $called = false;
+               $db->onTransactionPreCommitOrIdle(
+                       function () use ( &$called ) {
+                               $called = true;
+                       },
+                       __METHOD__
+               );
+               $this->assertFalse( $called, 'Not called when transaction is active' );
+               $db->commit( __METHOD__ );
+               $this->assertTrue( $called, 'Called when transaction is committed' );
+       }
+
+       /**
+        * @covers Wikimedia\Rdbms\Database::onTransactionPreCommitOrIdle
+        * @covers Wikimedia\Rdbms\Database::runOnTransactionPreCommitCallbacks
+        */
+       public function testTransactionPreCommitOrIdle_TRX() {
+               $db = $this->getMockDB( [ 'isOpen' ] );
+               $db->method( 'isOpen' )->willReturn( true );
+               $db->setFlag( DBO_TRX );
+
+               $lbFactory = LBFactorySingle::newFromConnection( $db );
+               // Ask for the connectin so that LB sets internal state
+               // about this connection being the master connection
+               $lb = $lbFactory->getMainLB();
+               $conn = $lb->openConnection( $lb->getWriterIndex() );
+               $this->assertSame( $db, $conn, 'Same DB instance' );
+               $this->assertTrue( $db->getFlag( DBO_TRX ), 'DBO_TRX is set' );
+
+               $called = false;
+               $db->onTransactionPreCommitOrIdle(
+                       function () use ( &$called ) {
+                               $called = true;
+                       }
+               );
+               $this->assertFalse( $called, 'Not called when idle if DBO_TRX is set' );
+
+               $lbFactory->beginMasterChanges( __METHOD__ );
+               $this->assertFalse( $called, 'Not called when lb-transaction is active' );
+
+               $lbFactory->commitMasterChanges( __METHOD__ );
+               $this->assertTrue( $called, 'Called when lb-transaction is committed' );
+       }
+
        /**
         * @covers Wikimedia\Rdbms\Database::onTransactionResolution
         * @covers Wikimedia\Rdbms\Database::runOnTransactionIdleCallbacks
index 556a348..3079d8f 100644 (file)
@@ -625,15 +625,15 @@ more stuff
                return [
                        [ 'Help:WikiPageTest_testReplaceSection',
                                CONTENT_MODEL_WIKITEXT,
-                               WikiPageTest::$sections,
+                               self::$sections,
                                "0",
                                "No more",
                                null,
-                               trim( preg_replace( '/^Intro/sm', 'No more', WikiPageTest::$sections ) )
+                               trim( preg_replace( '/^Intro/sm', 'No more', self::$sections ) )
                        ],
                        [ 'Help:WikiPageTest_testReplaceSection',
                                CONTENT_MODEL_WIKITEXT,
-                               WikiPageTest::$sections,
+                               self::$sections,
                                "",
                                "No more",
                                null,
@@ -641,29 +641,29 @@ more stuff
                        ],
                        [ 'Help:WikiPageTest_testReplaceSection',
                                CONTENT_MODEL_WIKITEXT,
-                               WikiPageTest::$sections,
+                               self::$sections,
                                "2",
                                "== TEST ==\nmore fun",
                                null,
                                trim( preg_replace( '/^== test ==.*== foo ==/sm',
                                        "== TEST ==\nmore fun\n\n== foo ==",
-                                       WikiPageTest::$sections ) )
+                                       self::$sections ) )
                        ],
                        [ 'Help:WikiPageTest_testReplaceSection',
                                CONTENT_MODEL_WIKITEXT,
-                               WikiPageTest::$sections,
+                               self::$sections,
                                "8",
                                "No more",
                                null,
-                               trim( WikiPageTest::$sections )
+                               trim( self::$sections )
                        ],
                        [ 'Help:WikiPageTest_testReplaceSection',
                                CONTENT_MODEL_WIKITEXT,
-                               WikiPageTest::$sections,
+                               self::$sections,
                                "new",
                                "No more",
                                "New",
-                               trim( WikiPageTest::$sections ) . "\n\n== New ==\n\nNo more"
+                               trim( self::$sections ) . "\n\n== New ==\n\nNo more"
                        ],
                ];
        }
index 3e0d883..3530d3c 100644 (file)
@@ -43,6 +43,7 @@ class ResourceLoaderClientHtmlTest extends PHPUnit_Framework_TestCase {
                        'test.top' => [ 'position' => 'top' ],
                        'test.private.top' => [ 'group' => 'private', 'position' => 'top' ],
                        'test.private.bottom' => [ 'group' => 'private', 'position' => 'bottom' ],
+                       'test.shouldembed' => [ 'shouldEmbed' => true ],
 
                        'test.styles.pure' => [ 'type' => ResourceLoaderModule::LOAD_STYLES ],
                        'test.styles.mixed' => [],
@@ -64,12 +65,24 @@ class ResourceLoaderClientHtmlTest extends PHPUnit_Framework_TestCase {
                                'group' => 'private',
                                'styles' => '.private{}',
                        ],
+                       'test.styles.shouldembed' => [
+                               'type' => ResourceLoaderModule::LOAD_STYLES,
+                               'shouldEmbed' => true,
+                               'styles' => '.shouldembed{}',
+                       ],
 
                        'test.scripts' => [],
                        'test.scripts.top' => [ 'position' => 'top' ],
                        'test.scripts.user' => [ 'group' => 'user' ],
                        'test.scripts.user.empty' => [ 'group' => 'user', 'isKnownEmpty' => true ],
                        'test.scripts.raw' => [ 'isRaw' => true ],
+                       'test.scripts.shouldembed' => [ 'shouldEmbed' => true ],
+
+                       'test.ordering.a' => [ 'shouldEmbed' => false ],
+                       'test.ordering.b' => [ 'shouldEmbed' => false ],
+                       'test.ordering.c' => [ 'shouldEmbed' => true, 'styles' => '.orderingC{}' ],
+                       'test.ordering.d' => [ 'shouldEmbed' => true, 'styles' => '.orderingD{}' ],
+                       'test.ordering.e' => [ 'shouldEmbed' => false ],
                ];
                return array_map( function ( $options ) {
                        return self::makeModule( $options );
@@ -102,6 +115,7 @@ class ResourceLoaderClientHtmlTest extends PHPUnit_Framework_TestCase {
                        'test.private.bottom',
                        'test.private.top',
                        'test.top',
+                       'test.shouldembed',
                        'test.unregistered',
                ] );
                $client->setModuleStyles( [
@@ -109,12 +123,14 @@ class ResourceLoaderClientHtmlTest extends PHPUnit_Framework_TestCase {
                        'test.styles.user.empty',
                        'test.styles.private',
                        'test.styles.pure',
+                       'test.styles.shouldembed',
                        'test.unregistered.styles',
                ] );
                $client->setModuleScripts( [
                        'test.scripts',
                        'test.scripts.user.empty',
                        'test.scripts.top',
+                       'test.scripts.shouldembed',
                        'test.unregistered.scripts',
                ] );
 
@@ -122,12 +138,15 @@ class ResourceLoaderClientHtmlTest extends PHPUnit_Framework_TestCase {
                        'states' => [
                                'test.private.top' => 'loading',
                                'test.private.bottom' => 'loading',
+                               'test.shouldembed' => 'loading',
                                'test.styles.pure' => 'ready',
                                'test.styles.user.empty' => 'ready',
                                'test.styles.private' => 'ready',
+                               'test.styles.shouldembed' => 'ready',
                                'test.scripts' => 'loading',
                                'test.scripts.top' => 'loading',
                                'test.scripts.user.empty' => 'ready',
+                               'test.scripts.shouldembed' => 'loading',
                        ],
                        'general' => [
                                'test',
@@ -139,12 +158,14 @@ class ResourceLoaderClientHtmlTest extends PHPUnit_Framework_TestCase {
                        'scripts' => [
                                'test.scripts',
                                'test.scripts.top',
+                               'test.scripts.shouldembed',
                        ],
                        'embed' => [
-                               'styles' => [ 'test.styles.private' ],
+                               'styles' => [ 'test.styles.private', 'test.styles.shouldembed' ],
                                'general' => [
                                        'test.private.bottom',
                                        'test.private.top',
+                                       'test.shouldembed',
                                ],
                        ],
                ];
@@ -276,6 +297,47 @@ class ResourceLoaderClientHtmlTest extends PHPUnit_Framework_TestCase {
                                'only' => ResourceLoaderModule::TYPE_STYLES,
                                'output' => '<noscript><link rel="stylesheet" href="/w/load.php?debug=false&amp;lang=nl&amp;modules=test.styles.noscript&amp;only=styles&amp;skin=fallback"/></noscript>',
                        ],
+                       [
+                               'context' => [],
+                               'modules' => [ 'test.shouldembed' ],
+                               'only' => ResourceLoaderModule::TYPE_COMBINED,
+                               'output' => '<script>(window.RLQ=window.RLQ||[]).push(function(){mw.loader.implement("test.shouldembed@09p30q0",function($,jQuery,require,module){},{"css":[]});});</script>',
+                       ],
+                       [
+                               'context' => [],
+                               'modules' => [ 'test.styles.shouldembed' ],
+                               'only' => ResourceLoaderModule::TYPE_STYLES,
+                               'output' => '<style>.shouldembed{}</style>',
+                       ],
+                       [
+                               'context' => [],
+                               'modules' => [ 'test.scripts.shouldembed' ],
+                               'only' => ResourceLoaderModule::TYPE_SCRIPTS,
+                               'output' => '<script>(window.RLQ=window.RLQ||[]).push(function(){mw.loader.state({"test.scripts.shouldembed":"ready"});});</script>',
+                       ],
+                       [
+                               'context' => [],
+                               'modules' => [ 'test', 'test.shouldembed' ],
+                               'only' => ResourceLoaderModule::TYPE_COMBINED,
+                               'output' => '<script>(window.RLQ=window.RLQ||[]).push(function(){mw.loader.load("/w/load.php?debug=false\u0026lang=nl\u0026modules=test\u0026skin=fallback");mw.loader.implement("test.shouldembed@09p30q0",function($,jQuery,require,module){},{"css":[]});});</script>',
+                       ],
+                       [
+                               'context' => [],
+                               'modules' => [ 'test.styles.pure', 'test.styles.shouldembed' ],
+                               'only' => ResourceLoaderModule::TYPE_STYLES,
+                               'output' =>
+                                       '<link rel="stylesheet" href="/w/load.php?debug=false&amp;lang=nl&amp;modules=test.styles.pure&amp;only=styles&amp;skin=fallback"/>' . "\n"
+                                       . '<style>.shouldembed{}</style>'
+                       ],
+                       [
+                               'context' => [],
+                               'modules' => [ 'test.ordering.a', 'test.ordering.e', 'test.ordering.b', 'test.ordering.d', 'test.ordering.c' ],
+                               'only' => ResourceLoaderModule::TYPE_STYLES,
+                               'output' =>
+                                       '<link rel="stylesheet" href="/w/load.php?debug=false&amp;lang=nl&amp;modules=test.ordering.a%2Cb&amp;only=styles&amp;skin=fallback"/>' . "\n"
+                                       . '<style>.orderingC{}.orderingD{}</style>' . "\n"
+                                       . '<link rel="stylesheet" href="/w/load.php?debug=false&amp;lang=nl&amp;modules=test.ordering.e&amp;only=styles&amp;skin=fallback"/>'
+                       ],
                        // @codingStandardsIgnoreEnd
                ];
        }
index ded56e9..e82bab7 100644 (file)
@@ -331,4 +331,23 @@ class ResourceLoaderFileModuleTest extends ResourceLoaderTestCase {
                        'Leading BOM removed when concatenating files'
                );
        }
+
+       /**
+        * @covers ResourceLoaderFileModule::getDefinitionSummary
+        */
+       public function testGetVersionHash() {
+               $context = $this->getResourceLoaderContext();
+
+               // Less variables
+               $module = new ResourceLoaderFileTestModule();
+               $version = $module->getVersionHash( $context );
+               $module = new ResourceLoaderFileTestModule( [], [
+                       'lessVars' => [ 'key' => 'value' ],
+               ] );
+               $this->assertNotEquals(
+                       $version,
+                       $module->getVersionHash( $context ),
+                       'Using less variables is significant'
+               );
+       }
 }
index 6597906..39c3421 100644 (file)
@@ -109,6 +109,6 @@ class TestSites {
        public static function insertIntoDb() {
                $sitesTable = \MediaWiki\MediaWikiServices::getInstance()->getSiteStore();
                $sitesTable->clear();
-               $sitesTable->saveSites( TestSites::getSites() );
+               $sitesTable->saveSites( self::getSites() );
        }
 }
index 85becff..a9a612d 100644 (file)
@@ -27,6 +27,8 @@ class SpecialRecentchangesTest extends AbstractChangesListSpecialPageTestCase {
 
                        [ 'days=3', [ 'days' => '3' ] ],
 
+                       [ 'days=0.25', [ 'days' => '0.25'] ],
+
                        [ 'namespace=5', [ 'namespace' => '5' ] ],
 
                        [ 'namespace=5|3', [ 'namespace' => '5|3' ] ],
index 2f19c81..251a4a2 100644 (file)
@@ -68,7 +68,6 @@
                        <directory suffix=".php">../../includes</directory>
                        <directory suffix=".php">../../languages</directory>
                        <directory suffix=".php">../../maintenance</directory>
-                       <directory suffix=".php">../../skins</directory>
                </whitelist>
        </filter>
 </phpunit>
index 929fa1f..e944ef0 100644 (file)
@@ -2,7 +2,7 @@
 ( function ( $, mw, QUnit ) {
        'use strict';
 
-       var addons;
+       var addons, nested;
 
        /**
         * Make a safe copy of localEnv:
@@ -77,8 +77,8 @@
        ( function () {
                var orgModule = QUnit.module;
                QUnit.module = function ( name, localEnv, executeNow ) {
-                       var orgBeforeEach, orgAfterEach;
-                       if ( QUnit.config.moduleStack.length ) {
+                       var orgBeforeEach, orgAfterEach, orgExecute;
+                       if ( nested ) {
                                // In a nested module, don't re-run our handlers.
                                return orgModule.apply( this, arguments );
                        }
                                executeNow = localEnv;
                                localEnv = undefined;
                        }
+                       if ( executeNow ) {
+                               // Wrap executeNow() so that we can detect nested modules
+                               orgExecute = executeNow;
+                               executeNow = function () {
+                                       var ret;
+                                       nested = true;
+                                       ret = orgExecute.apply( this, arguments );
+                                       nested = false;
+                                       return ret;
+                               };
+                       }
 
                        localEnv = localEnv || {};
                        orgBeforeEach = localEnv.beforeEach;
                var orgModule = QUnit.module;
                QUnit.module = function ( name, localEnv, executeNow ) {
                        var orgBeforeEach, orgAfterEach;
-                       if ( QUnit.config.moduleStack.length ) {
+                       if ( nested ) {
                                // In a nested module, don't re-run our handlers.
                                return orgModule.apply( this, arguments );
                        }
index 5b973f6..c88941e 100644 (file)
         * @param {function($table)} callback something to do with the table before we compare
         */
        function tableTest( msg, header, data, expected, callback ) {
-               QUnit.test( msg, 1, function ( assert ) {
+               QUnit.test( msg, function ( assert ) {
                        var extracted,
                                $table = tableCreate( header, data );
 
         * @param {function($table)} callback Something to do with the table before we compare
         */
        function tableTestHTML( msg, html, expected, callback ) {
-               QUnit.test( msg, 1, function ( assert ) {
+               QUnit.test( msg, function ( assert ) {
                        var extracted,
                                $table = $( html );
 
index edaef79..da7bafd 100644 (file)
                                { name: 'option2', label: 'group5option2-label', description: 'group5option2-desc' },
                                { name: 'option3', label: 'group5option3-label', description: 'group5option3-desc' }
                        ]
+               }, {
+                       name: 'group6',
+                       type: 'boolean',
+                       filters: [
+                               { name: 'group6option1', label: 'group6option1-label', description: 'group5option1-desc' },
+                               { name: 'group6option2', label: 'group6option2-label', description: 'group5option2-desc', default: true, useDefaultAsBaseValue: true },
+                               { name: 'group6option3', label: 'group6option3-label', description: 'group5option3-desc', default: true }
+                       ]
                } ],
                viewsDefinition = {
                        namespaces: {
                        group3: 'filter8',
                        group4: 'option2',
                        group5: 'option1',
+                       group6option1: '0',
+                       group6option2: '1',
+                       group6option3: '1',
                        namespace: ''
                },
                baseParamRepresentation = {
                        group3: '',
                        group4: 'option2',
                        group5: 'option1',
+                       group6option1: '0',
+                       group6option2: '1',
+                       group6option3: '0',
                        namespace: ''
                },
                baseFilterRepresentation = {
                        group5__option1: true, // No default set, first item is default value
                        group5__option2: false,
                        group5__option3: false,
+                       group6__group6option1: false,
+                       group6__group6option2: true,
+                       group6__group6option3: false,
                        namespace__0: false,
                        namespace__1: false,
                        namespace__2: false,
                        group5__option1: { selected: true, conflicted: false, included: false },
                        group5__option2: { selected: false, conflicted: false, included: false },
                        group5__option3: { selected: false, conflicted: false, included: false },
+                       group6__group6option1: { selected: false, conflicted: false, included: false },
+                       group6__group6option2: { selected: true, conflicted: false, included: false },
+                       group6__group6option3: { selected: false, conflicted: false, included: false },
                        namespace__0: { selected: false, conflicted: false, included: false },
                        namespace__1: { selected: false, conflicted: false, included: false },
                        namespace__2: { selected: false, conflicted: false, included: false },